From cd34814b2bebcf96f86d0996d63f8cd0eb60ffea Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Tue, 1 Sep 2015 09:10:32 -0400 Subject: [PATCH 07/31] FIXME: Use rich locations in c-family/c-format.c TODO: * update the blurb * update the ChangeLog This is a proof-of-concept of (a) using the string-literal location info to show locations of errors in format strings, and (b) underlining the corresponding argument where applicable (no more having to guess what the compiler means by "argument 2") In particular, this can handle format strings built using concatentation, potentially split over multiple lines etc. Out-of-date screenshot: https://dmalcolm.fedorapeople.org/gcc/2015-09-04/ranges-in-format-string-diagnostics.html I also attempted to add captions to the underlines, but they looked too "busy", so I removed them for now. In theory this could replace some of the work done by Manu on PR c/52952 in e.g. r223470, since it adds handling of string concatentation, but I didn't go as far as rewriting all of them yet. gcc/c-family/ChangeLog: * c-format.c: Include gcc-rich-location.h. (check_format_arg): Pass in "format_tree" to check_format_info_main. (check_format_info_main): Add param "format_string_cst". Generate a source_range pass it to the call to check_format_types, (check_format_types): Replace location_t param with a source_range. Generate param_range and pars it to format_type_warning where applicable. (format_type_warning): Convert first param from location_t to source_range, as the range of the format string, and add a "param_range" parameter. Use them in the warnings. gcc/ChangeLog: * gcc-rich-location.c (gcc_rich_location::set_caption): New method. * gcc-rich-location.h (gcc_rich_location::set_caption): New method. gcc/testsuite/ChangeLog: * gcc.dg/format/diagnostic-ranges.c: New file. --- gcc/c-family/c-format.c | 397 +++++++++++++----------- gcc/testsuite/g++.dg/ext/builtin4.C | 8 +- gcc/testsuite/gcc.dg/cpp/pr66415-1.c | 8 +- gcc/testsuite/gcc.dg/format/asm_fprintf-1.c | 6 +- gcc/testsuite/gcc.dg/format/c90-printf-1.c | 14 +- gcc/testsuite/gcc.dg/format/diagnostic-ranges.c | 232 ++++++++++++++ 6 files changed, 480 insertions(+), 185 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/format/diagnostic-ranges.c diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c index c19c411..202d4f4 100644 --- a/gcc/c-family/c-format.c +++ b/gcc/c-family/c-format.c @@ -29,6 +29,7 @@ along with GCC; see the file COPYING3. If not see #include "intl.h" #include "langhooks.h" #include "c-format.h" +#include "diagnostic.h" /* Handle attributes associated with format checking. */ @@ -65,78 +66,84 @@ static int first_target_format_type; static const char *format_name (int format_num); static int format_flags (int format_num); -/* Given a string S of length LINE_WIDTH, find the visual column - corresponding to OFFSET bytes. */ - -static unsigned int -location_column_from_byte_offset (const char *s, int line_width, - unsigned int offset) +/* FIXME */ +ATTRIBUTE_GCC_DIAG (5,0) +static bool +format_warning_va (location_t fmt_string_loc, source_range fmt_substring_range, + source_range *param_range, + int opt, const char *gmsgid, va_list *ap) { - const char * c = s; - if (*c != '"') - return 0; + bool substring_within_range; + source_range fmt_loc_range = get_range_from_loc (line_table, fmt_string_loc); + if (fmt_substring_range.m_start >= fmt_loc_range.m_start + && fmt_substring_range.m_finish <= fmt_loc_range.m_finish) + substring_within_range = true; + else + substring_within_range = false; - c++, offset--; - while (offset > 0) - { - if (c - s >= line_width) - return 0; + location_t substring_loc = make_location (fmt_substring_range.m_finish, + fmt_substring_range.m_start, + fmt_substring_range.m_finish); - switch (*c) - { - case '\\': - c++; - if (c - s >= line_width) - return 0; - switch (*c) - { - case '\\': case '\'': case '"': case '?': - case '(': case '{': case '[': case '%': - case 'a': case 'b': case 'f': case 'n': - case 'r': case 't': case 'v': - case 'e': case 'E': - c++, offset--; - break; + if (substring_within_range) + fmt_string_loc = substring_loc; - default: - return 0; - } - break; - - case '"': - /* We found the end of the string too early. */ - return 0; - - default: - c++, offset--; - break; - } + rich_location richloc (line_table, fmt_string_loc); + + if (param_range) + { + location_t param_loc = make_location (param_range->m_start, + param_range->m_start, + param_range->m_finish); + richloc.add_range (param_loc, false); } - return c - s; -} -/* Return a location that encodes the same location as LOC but shifted - by OFFSET bytes. */ + diagnostic_info diagnostic; + diagnostic_set_info (&diagnostic, gmsgid, ap, &richloc, DK_WARNING); + diagnostic.option_index = opt; + bool warned = report_diagnostic (&diagnostic); -static location_t -location_from_offset (location_t loc, int offset) + if (!substring_within_range) + if (warned) + inform (substring_loc, "format string is defined here"); + + return warned; +} + +/* FIXME */ +ATTRIBUTE_GCC_DIAG (5,0) +static bool +format_warning (location_t fmt_string_loc, source_range fmt_substring_range, + source_range *param_range, + int opt, const char *gmsgid, ...) { - gcc_checking_assert (offset >= 0); - if (linemap_location_from_macro_expansion_p (line_table, loc) - || offset < 0) - return loc; + va_list ap; + va_start (ap, gmsgid); + bool warned = format_warning_va (fmt_string_loc, fmt_substring_range, + param_range, opt, gmsgid, &ap); + va_end (ap); - expanded_location s = expand_location_to_spelling_point (loc); - int line_width; - const char *line = location_get_source_line (s.file, s.line, &line_width); - if (line == NULL) - return loc; - line += s.column - 1 ; - line_width -= s.column - 1; - unsigned int column = - location_column_from_byte_offset (line, line_width, (unsigned) offset); + return warned; +} - return linemap_position_for_loc_and_offset (line_table, loc, column); +/* FIXME */ +ATTRIBUTE_GCC_DIAG (5,6) +static bool +format_warning (location_t fmt_string_loc, tree format_string_cst, + int char_idx, int opt, const char *gmsgid, ...) +{ + cpp_string_location *strloc = TREE_STRING_LOCATION (format_string_cst); + gcc_assert (strloc); + source_range fmt_substring_range + = strloc->get_range_between_indices (char_idx - 1, char_idx - 1); + + va_list ap; + va_start (ap, gmsgid); + bool warned = format_warning_va (fmt_string_loc, fmt_substring_range, NULL, + opt, gmsgid, &ap); + va_end (ap); + + return warned; } /* Check that we have a pointer to a string suitable for use as a format. @@ -1019,6 +1026,7 @@ static void check_format_info (function_format_info *, tree); static void check_format_arg (void *, tree, unsigned HOST_WIDE_INT); static void check_format_info_main (format_check_results *, function_format_info *, + location_t, tree, const char *, int, tree, unsigned HOST_WIDE_INT, object_allocator &); @@ -1032,8 +1040,14 @@ static void finish_dollar_format_checking (format_check_results *, int); static const format_flag_spec *get_flag_spec (const format_flag_spec *, int, const char *); -static void check_format_types (location_t, format_wanted_type *); -static void format_type_warning (location_t, format_wanted_type *, tree, tree); +static void check_format_types (location_t fmt_loc, + source_range fmt_substring_range, + format_wanted_type *); +static void format_type_warning (location_t fmt_loc, + source_range fmt_substring_range, + source_range *param_range, + format_wanted_type *, tree, + tree); /* Decode a format type from a string, returning the type, or format_type_error if not valid, in which case the caller should print an @@ -1509,6 +1523,8 @@ check_format_arg (void *ctx, tree format_tree, tree array_size = 0; tree array_init; + location_t fmt_param_loc = EXPR_LOC_OR_LOC (format_tree, input_location); + if (VAR_P (format_tree)) { /* Pull out a constant value if the front end didn't. */ @@ -1684,12 +1700,13 @@ check_format_arg (void *ctx, tree format_tree, need not adjust it for every return. */ res->number_other++; object_allocator fwt_pool ("format_wanted_type pool"); - check_format_info_main (res, info, format_chars, format_length, - params, arg_num, fwt_pool); + check_format_info_main (res, info, fmt_param_loc, format_tree, format_chars, + format_length, params, arg_num, fwt_pool); } -/* Do the main part of checking a call to a format function. FORMAT_CHARS +/* Do the main part of checking a call to a format function. + FORMAT_STRING_CST is the STRING_CST format string. FORMAT_CHARS is the NUL-terminated format string (which at this point may contain internal NUL characters); FORMAT_LENGTH is its length (excluding the terminating NUL character). ARG_NUM is one less than the number of @@ -1698,7 +1715,10 @@ check_format_arg (void *ctx, tree format_tree, static void check_format_info_main (format_check_results *res, - function_format_info *info, const char *format_chars, + function_format_info *info, + location_t fmt_param_loc, + tree format_string_cst, + const char *format_chars, int format_length, tree params, unsigned HOST_WIDE_INT arg_num, object_allocator &fwt_pool) @@ -1747,10 +1767,10 @@ check_format_info_main (format_check_results *res, continue; if (*format_chars == 0) { - warning_at (location_from_offset (format_string_loc, - format_chars - orig_format_chars), - OPT_Wformat_, - "spurious trailing %<%%%> in format"); + format_warning (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "spurious trailing %<%%%> in format"); continue; } if (*format_chars == '%') @@ -1758,6 +1778,7 @@ check_format_info_main (format_check_results *res, ++format_chars; continue; } + const char *start_of_this_format = format_chars; flag_chars[0] = 0; if ((fki->flags & (int) FMT_FLAG_USE_DOLLAR) && has_operand_number != 0) @@ -1794,11 +1815,10 @@ check_format_info_main (format_check_results *res, *format_chars, NULL); if (strchr (flag_chars, *format_chars) != 0) { - warning_at (location_from_offset (format_string_loc, - format_chars + 1 - - orig_format_chars), - OPT_Wformat_, - "repeated %s in format", _(s->name)); + format_warning (format_string_loc, format_string_cst, + format_chars + 1 - orig_format_chars, + OPT_Wformat_, + "repeated %s in format", _(s->name)); } else { @@ -1921,10 +1941,10 @@ check_format_info_main (format_check_results *res, flag_chars[i++] = fki->left_precision_char; flag_chars[i] = 0; if (!ISDIGIT (*format_chars)) - warning_at (location_from_offset (format_string_loc, - format_chars - orig_format_chars), - OPT_Wformat_, - "empty left precision in %s format", fki->name); + format_warning (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "empty left precision in %s format", fki->name); while (ISDIGIT (*format_chars)) ++format_chars; } @@ -2002,10 +2022,10 @@ check_format_info_main (format_check_results *res, { if (!(fki->flags & (int) FMT_FLAG_EMPTY_PREC_OK) && !ISDIGIT (*format_chars)) - warning_at (location_from_offset (format_string_loc, - format_chars - orig_format_chars), - OPT_Wformat_, - "empty precision in %s format", fki->name); + format_warning (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "empty precision in %s format", fki->name); while (ISDIGIT (*format_chars)) ++format_chars; } @@ -2090,11 +2110,10 @@ check_format_info_main (format_check_results *res, { const format_flag_spec *s = get_flag_spec (flag_specs, *format_chars, NULL); - warning_at (location_from_offset (format_string_loc, - format_chars - - orig_format_chars), - OPT_Wformat_, - "repeated %s in format", _(s->name)); + format_warning (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "repeated %s in format", _(s->name)); } else { @@ -2111,10 +2130,10 @@ check_format_info_main (format_check_results *res, || (!(fki->flags & (int) FMT_FLAG_FANCY_PERCENT_OK) && format_char == '%')) { - warning_at (location_from_offset (format_string_loc, - format_chars - orig_format_chars), - OPT_Wformat_, - "conversion lacks type at end of format"); + format_warning (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "conversion lacks type at end of format"); continue; } format_chars++; @@ -2125,27 +2144,27 @@ check_format_info_main (format_check_results *res, if (fci->format_chars == 0) { if (ISGRAPH (format_char)) - warning_at (location_from_offset (format_string_loc, - format_chars - orig_format_chars), - OPT_Wformat_, - "unknown conversion type character %qc in format", - format_char); + format_warning (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "unknown conversion type character %qc in format", + format_char); else - warning_at (location_from_offset (format_string_loc, - format_chars - orig_format_chars), - OPT_Wformat_, - "unknown conversion type character 0x%x in format", - format_char); + format_warning (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "unknown conversion type character 0x%x in format", + format_char); continue; } if (pedantic) { if (ADJ_STD (fci->std) > C_STD_VER) - warning_at (location_from_offset (format_string_loc, - format_chars - orig_format_chars), - OPT_Wformat_, - "%s does not support the %<%%%c%> %s format", - C_STD_NAME (fci->std), format_char, fki->name); + format_warning (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "%s does not support the %<%%%c%> %s format", + C_STD_NAME (fci->std), format_char, fki->name); } /* Validate the individual flags used, removing any that are invalid. */ @@ -2160,11 +2179,11 @@ check_format_info_main (format_check_results *res, continue; if (strchr (fci->flag_chars, flag_chars[i]) == 0) { - warning_at (location_from_offset (format_string_loc, - format_chars - - orig_format_chars), - OPT_Wformat_, "%s used with %<%%%c%> %s format", - _(s->name), format_char, fki->name); + format_warning (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "%s used with %<%%%c%> %s format", + _(s->name), format_char, fki->name); d++; continue; } @@ -2277,10 +2296,10 @@ check_format_info_main (format_check_results *res, ++format_chars; if (*format_chars != ']') /* The end of the format string was reached. */ - warning_at (location_from_offset (format_string_loc, - format_chars - orig_format_chars), - OPT_Wformat_, - "no closing %<]%> for %<%%[%> format"); + format_warning (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "no closing %<]%> for %<%%[%> format"); } wanted_type = 0; @@ -2293,12 +2312,13 @@ check_format_info_main (format_check_results *res, wanted_type_std = fci->types[length_chars_val].std; if (wanted_type == 0) { - warning_at (location_from_offset (format_string_loc, - format_chars - orig_format_chars), - OPT_Wformat_, - "use of %qs length modifier with %qc type character" - " has either no effect or undefined behavior", - length_chars, format_char); + format_warning (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "use of %qs length modifier with %qc type" + " character" + " has either no effect or undefined behavior", + length_chars, format_char); /* Heuristic: skip one argument when an invalid length/type combination is encountered. */ arg_num++; @@ -2314,12 +2334,12 @@ check_format_info_main (format_check_results *res, && ADJ_STD (wanted_type_std) > ADJ_STD (fci->std)) { if (ADJ_STD (wanted_type_std) > C_STD_VER) - warning_at (location_from_offset (format_string_loc, - format_chars - orig_format_chars), - OPT_Wformat_, - "%s does not support the %<%%%s%c%> %s format", - C_STD_NAME (wanted_type_std), length_chars, - format_char, fki->name); + format_warning (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "%s does not support the %<%%%s%c%> %s format", + C_STD_NAME (wanted_type_std), length_chars, + format_char, fki->name); } } @@ -2421,14 +2441,25 @@ check_format_info_main (format_check_results *res, } if (first_wanted_type != 0) - check_format_types (format_string_loc, first_wanted_type); + { + ptrdiff_t offset_to_format_start = (start_of_this_format - 1) - orig_format_chars; + ptrdiff_t offset_to_format_end = (format_chars - 1) - orig_format_chars; + cpp_string_location *strloc + = TREE_STRING_LOCATION (format_string_cst); + gcc_assert (strloc); + source_range fmt_substring_range + = strloc->get_range_between_indices (offset_to_format_start, + offset_to_format_end); + check_format_types (fmt_param_loc, fmt_substring_range, + first_wanted_type); + } } if (format_chars - orig_format_chars != format_length) - warning_at (location_from_offset (format_string_loc, - format_chars + 1 - orig_format_chars), - OPT_Wformat_contains_nul, - "embedded %<\\0%> in format"); + format_warning (format_string_loc, format_string_cst, + format_chars + 1 - orig_format_chars, + OPT_Wformat_contains_nul, + "embedded %<\\0%> in format"); if (info->first_arg_num != 0 && params != 0 && has_operand_number <= 0) { @@ -2439,12 +2470,13 @@ check_format_info_main (format_check_results *res, finish_dollar_format_checking (res, fki->flags & (int) FMT_FLAG_DOLLAR_GAP_POINTER_OK); } - +// FIXME: FMT_LOC /* Check the argument types from a single format conversion (possibly - including width and precision arguments). LOC is the location of - the format string. */ + including width and precision arguments). FMT_SUBSTRING_RANGE is the + location of the format string. */ static void -check_format_types (location_t loc, format_wanted_type *types) +check_format_types (location_t fmt_loc, source_range fmt_substring_range, + format_wanted_type *types) { for (; types != 0; types = types->next) { @@ -2471,7 +2503,8 @@ check_format_types (location_t loc, format_wanted_type *types) cur_param = types->param; if (!cur_param) { - format_type_warning (loc, types, wanted_type, NULL); + format_type_warning (fmt_loc, fmt_substring_range, NULL, types, + wanted_type, NULL); continue; } @@ -2481,6 +2514,16 @@ check_format_types (location_t loc, format_wanted_type *types) orig_cur_type = cur_type; char_type_flag = 0; + source_range param_range; + source_range *param_range_ptr; + if (CAN_HAVE_LOCATION_P (cur_param)) + { + param_range = EXPR_LOCATION_RANGE (cur_param); + param_range_ptr = ¶m_range; + } + else + param_range_ptr = NULL; + STRIP_NOPS (cur_param); /* Check the types of any additional pointer arguments @@ -2545,7 +2588,8 @@ check_format_types (location_t loc, format_wanted_type *types) } else { - format_type_warning (loc, types, wanted_type, orig_cur_type); + format_type_warning (fmt_loc, fmt_substring_range, param_range_ptr, + types, wanted_type, orig_cur_type); break; } } @@ -2613,20 +2657,26 @@ check_format_types (location_t loc, format_wanted_type *types) && TYPE_PRECISION (cur_type) == TYPE_PRECISION (wanted_type)) continue; /* Now we have a type mismatch. */ - format_type_warning (loc, types, wanted_type, orig_cur_type); + format_type_warning (fmt_loc, fmt_substring_range, param_range_ptr, types, + wanted_type, orig_cur_type); } } -/* Give a warning at LOC about a format argument of different type from that - expected. WANTED_TYPE is the type the argument should have, possibly - stripped of pointer dereferences. The description (such as "field +/* Give a warning at FMT_LOC about a format argument of different type + from that expected. FMT_SUBSTRING_RANGE gives the location of the + pertinent part of the format string (which may be different to FMT_LOC). + If non-NULL, PARAM_RANGE is the source range of the + relevant argument. WANTED_TYPE is the type the argument should have, + possibly stripped of pointer dereferences. The description (such as "field precision"), the placement in the format string, a possibly more friendly name of WANTED_TYPE, and the number of pointer dereferences are taken from TYPE. ARG_TYPE is the type of the actual argument, or NULL if it is missing. */ static void -format_type_warning (location_t loc, format_wanted_type *type, +format_type_warning (location_t fmt_loc, source_range fmt_substring_range, + source_range *param_range, + format_wanted_type *type, tree wanted_type, tree arg_type) { int kind = type->kind; @@ -2635,7 +2685,6 @@ format_type_warning (location_t loc, format_wanted_type *type, int format_length = type->format_length; int pointer_count = type->pointer_count; int arg_num = type->arg_num; - unsigned int offset_loc = type->offset_loc; char *p; /* If ARG_TYPE is a typedef with a misleading name (for example, @@ -2669,41 +2718,43 @@ format_type_warning (location_t loc, format_wanted_type *type, p[pointer_count + 1] = 0; } - loc = location_from_offset (loc, offset_loc); - if (wanted_type_name) { if (arg_type) - warning_at (loc, OPT_Wformat_, - "%s %<%s%.*s%> expects argument of type %<%s%s%>, " - "but argument %d has type %qT", - gettext (kind_descriptions[kind]), - (kind == CF_KIND_FORMAT ? "%" : ""), - format_length, format_start, - wanted_type_name, p, arg_num, arg_type); + format_warning (fmt_loc, fmt_substring_range, param_range, + OPT_Wformat_, + "%s %<%s%.*s%> expects argument of type %<%s%s%>, " + "but argument %d has type %qT", + gettext (kind_descriptions[kind]), + (kind == CF_KIND_FORMAT ? "%" : ""), + format_length, format_start, + wanted_type_name, p, arg_num, arg_type); else - warning_at (loc, OPT_Wformat_, - "%s %<%s%.*s%> expects a matching %<%s%s%> argument", - gettext (kind_descriptions[kind]), - (kind == CF_KIND_FORMAT ? "%" : ""), - format_length, format_start, wanted_type_name, p); + format_warning (fmt_loc, fmt_substring_range, param_range, + OPT_Wformat_, + "%s %<%s%.*s%> expects a matching %<%s%s%> argument", + gettext (kind_descriptions[kind]), + (kind == CF_KIND_FORMAT ? "%" : ""), + format_length, format_start, wanted_type_name, p); } else { if (arg_type) - warning_at (loc, OPT_Wformat_, - "%s %<%s%.*s%> expects argument of type %<%T%s%>, " - "but argument %d has type %qT", - gettext (kind_descriptions[kind]), - (kind == CF_KIND_FORMAT ? "%" : ""), - format_length, format_start, - wanted_type, p, arg_num, arg_type); + format_warning (fmt_loc, fmt_substring_range, param_range, + OPT_Wformat_, + "%s %<%s%.*s%> expects argument of type %<%T%s%>, " + "but argument %d has type %qT", + gettext (kind_descriptions[kind]), + (kind == CF_KIND_FORMAT ? "%" : ""), + format_length, format_start, + wanted_type, p, arg_num, arg_type); else - warning_at (loc, OPT_Wformat_, - "%s %<%s%.*s%> expects a matching %<%T%s%> argument", - gettext (kind_descriptions[kind]), - (kind == CF_KIND_FORMAT ? "%" : ""), - format_length, format_start, wanted_type, p); + format_warning (fmt_loc, fmt_substring_range, param_range, + OPT_Wformat_, + "%s %<%s%.*s%> expects a matching %<%T%s%> argument", + gettext (kind_descriptions[kind]), + (kind == CF_KIND_FORMAT ? "%" : ""), + format_length, format_start, wanted_type, p); } } diff --git a/gcc/testsuite/g++.dg/ext/builtin4.C b/gcc/testsuite/g++.dg/ext/builtin4.C index 8804b53..4d9bf10 100644 --- a/gcc/testsuite/g++.dg/ext/builtin4.C +++ b/gcc/testsuite/g++.dg/ext/builtin4.C @@ -1,10 +1,16 @@ // Verify that builtin is used when declared in global namespace // { dg-do compile } -// { dg-options "-Wall" } +// { dg-options "-Wall -fdiagnostics-show-caret" } extern "C" int printf(const char*,...); void foo() { printf("%d"); // { dg-warning "expects a matching" } + + /* Verify source range of diagnostic (PR c++/56856). */ + /* { dg-begin-multiline-output "" } + printf("%d"); + ~^ + { dg-end-multiline-output "" } */ } diff --git a/gcc/testsuite/gcc.dg/cpp/pr66415-1.c b/gcc/testsuite/gcc.dg/cpp/pr66415-1.c index 349ec48..1f67cb4 100644 --- a/gcc/testsuite/gcc.dg/cpp/pr66415-1.c +++ b/gcc/testsuite/gcc.dg/cpp/pr66415-1.c @@ -1,9 +1,15 @@ /* PR c/66415 */ /* { dg-do compile } */ -/* { dg-options "-Wformat" } */ +/* { dg-options "-Wformat -fdiagnostics-show-caret" } */ void fn1 (void) { __builtin_printf ("xxxxxxxxxxxxxxxxx%dxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); /* { dg-warning "71:format" } */ + +/* { dg-begin-multiline-output "" } + __builtin_printf ("xxxxxxxxxxxxxxxxx%dxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + ~^ + { dg-end-multiline-output "" } */ + } diff --git a/gcc/testsuite/gcc.dg/format/asm_fprintf-1.c b/gcc/testsuite/gcc.dg/format/asm_fprintf-1.c index 2eabbf9..50ca572 100644 --- a/gcc/testsuite/gcc.dg/format/asm_fprintf-1.c +++ b/gcc/testsuite/gcc.dg/format/asm_fprintf-1.c @@ -66,9 +66,9 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p, asm_fprintf ("%d", i, i); /* { dg-warning "16:arguments" "wrong number of args" } */ /* Miscellaneous bogus constructions. */ asm_fprintf (""); /* { dg-warning "16:zero-length" "warning for empty format" } */ - asm_fprintf ("\0"); /* { dg-warning "17:embedded" "warning for embedded NUL" } */ - asm_fprintf ("%d\0", i); /* { dg-warning "19:embedded" "warning for embedded NUL" } */ - asm_fprintf ("%d\0%d", i, i); /* { dg-warning "19:embedded|too many" "warning for embedded NUL" } */ + asm_fprintf ("\0"); /* { dg-warning "18:embedded" "warning for embedded NUL" } */ + asm_fprintf ("%d\0", i); /* { dg-warning "20:embedded" "warning for embedded NUL" } */ + asm_fprintf ("%d\0%d", i, i); /* { dg-warning "20:embedded|too many" "warning for embedded NUL" } */ asm_fprintf (NULL); /* { dg-warning "null" "null format string warning" } */ asm_fprintf ("%"); /* { dg-warning "17:trailing" "trailing % warning" } */ asm_fprintf ("%++d", i); /* { dg-warning "19:repeated" "repeated flag warning" } */ diff --git a/gcc/testsuite/gcc.dg/format/c90-printf-1.c b/gcc/testsuite/gcc.dg/format/c90-printf-1.c index 5329dad..338b971 100644 --- a/gcc/testsuite/gcc.dg/format/c90-printf-1.c +++ b/gcc/testsuite/gcc.dg/format/c90-printf-1.c @@ -58,11 +58,11 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p, printf ("%-%"); /* { dg-warning "13:type" "missing type" } */ /* { dg-warning "14:trailing" "bogus %%" { target *-*-* } 58 } */ printf ("%-%\n"); /* { dg-warning "13:format" "bogus %%" } */ - /* { dg-warning "15:format" "bogus %%" { target *-*-* } 60 } */ + /* { dg-warning "16:format" "bogus %%" { target *-*-* } 60 } */ printf ("%5%\n"); /* { dg-warning "13:format" "bogus %%" } */ - /* { dg-warning "15:format" "bogus %%" { target *-*-* } 62 } */ + /* { dg-warning "16:format" "bogus %%" { target *-*-* } 62 } */ printf ("%h%\n"); /* { dg-warning "13:format" "bogus %%" } */ - /* { dg-warning "15:format" "bogus %%" { target *-*-* } 64 } */ + /* { dg-warning "16:format" "bogus %%" { target *-*-* } 64 } */ /* Valid and invalid %h, %l, %L constructions. */ printf ("%hd", i); printf ("%hi", i); @@ -184,8 +184,8 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p, printf ("%-08G", d); /* { dg-warning "11:flags|ignored" "0 flag ignored with - flag" } */ /* Various tests of bad argument types. */ printf ("%d", l); /* { dg-warning "13:format" "bad argument types" } */ - printf ("%*.*d", l, i2, i); /* { dg-warning "13:field" "bad * argument types" } */ - printf ("%*.*d", i1, l, i); /* { dg-warning "15:field" "bad * argument types" } */ + printf ("%*.*d", l, i2, i); /* { dg-warning "16:field" "bad * argument types" } */ + printf ("%*.*d", i1, l, i); /* { dg-warning "16:field" "bad * argument types" } */ printf ("%ld", i); /* { dg-warning "14:format" "bad argument types" } */ printf ("%s", n); /* { dg-warning "13:format" "bad argument types" } */ printf ("%p", i); /* { dg-warning "13:format" "bad argument types" } */ @@ -231,8 +231,8 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p, printf ("%d", i, i); /* { dg-warning "11:arguments" "wrong number of args" } */ /* Miscellaneous bogus constructions. */ printf (""); /* { dg-warning "11:zero-length" "warning for empty format" } */ - printf ("\0"); /* { dg-warning "12:embedded" "warning for embedded NUL" } */ - printf ("%d\0", i); /* { dg-warning "14:embedded" "warning for embedded NUL" } */ + printf ("\0"); /* { dg-warning "13:embedded" "warning for embedded NUL" } */ + printf ("%d\0", i); /* { dg-warning "15:embedded" "warning for embedded NUL" } */ printf ("%d\0%d", i, i); /* { dg-warning "embedded|too many" "warning for embedded NUL" } */ printf (NULL); /* { dg-warning "3:null" "null format string warning" } */ printf ("%"); /* { dg-warning "12:trailing" "trailing % warning" } */ diff --git a/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c b/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c new file mode 100644 index 0000000..631b68d --- /dev/null +++ b/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c @@ -0,0 +1,232 @@ +/* { dg-options "-Wformat -fdiagnostics-show-caret" } */ + +/* See PR 52952. */ + +#include "format.h" + +void test_mismatching_types (const char *msg) +{ + printf("hello %i", msg); /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */ + +/* TODO: ideally would also underline "msg". */ +/* { dg-begin-multiline-output "" } + printf("hello %i", msg); + ~^ + { dg-end-multiline-output "" } */ +} + +void test_multiple_arguments (void) +{ + printf ("arg0: %i arg1: %s arg 2: %i", /* { dg-warning "29: format '%s'" } */ + 100, 101, 102); +/* TODO: ideally would also underline "101". */ +/* { dg-begin-multiline-output "" } + printf ("arg0: %i arg1: %s arg 2: %i", + ~^ + { dg-end-multiline-output "" } */ +} + +void test_multiple_arguments_2 (int i, int j) +{ + printf ("arg0: %i arg1: %s arg 2: %i", /* { dg-warning "29: format '%s'" } */ + 100, i + j, 102); +/* { dg-begin-multiline-output "" } + printf ("arg0: %i arg1: %s arg 2: %i", + ~^ + 100, i + j, 102); + ~~~~~ + { dg-end-multiline-output "" } */ +} + +void multiline_format_string (void) { + printf ("before the fmt specifier" /* { dg-warning "11: format '%d' expects a matching 'int' argument" } */ +/* { dg-begin-multiline-output "" } + printf ("before the fmt specifier" + ^~~~~~~~~~~~~~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ + + "%" + "d" /* { dg-message "12: format string is defined here" } */ + "after the fmt specifier"); + +/* { dg-begin-multiline-output "" } + "%" + ~~ + "d" + ~^ + { dg-end-multiline-output "" } */ +} + +void test_hex (const char *msg) +{ + /* "%" is \x25 + "i" is \x69 */ + printf("hello \x25\x69", msg); /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */ + +/* TODO: ideally would also underline "msg". */ +/* { dg-begin-multiline-output "" } + printf("hello \x25\x69", msg); + ~~~~~~~^ + { dg-end-multiline-output "" } */ +} + +void test_oct (const char *msg) +{ + /* "%" is octal 045 + "i" is octal 151. */ + printf("hello \045\151", msg); /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */ + +/* TODO: ideally would also underline "msg". */ +/* { dg-begin-multiline-output "" } + printf("hello \045\151", msg); + ~~~~~~~^ + { dg-end-multiline-output "" } */ +} + +void test_multiple (const char *msg) +{ + /* "%" is \x25 in hex + "i" is \151 in octal. */ + printf("prefix" "\x25" "\151" "suffix", /* { dg-warning "format '%i'" } */ + msg); +/* { dg-begin-multiline-output "" } + printf("prefix" "\x25" "\151" "suffix", + ^~~~~~~~ + { dg-end-multiline-output "" } */ + +/* TODO: ideally would also underline "msg". */ +/* { dg-begin-multiline-output "" } + printf("prefix" "\x25" "\151" "suffix", + ~~~~~~~~~~~^ + { dg-end-multiline-output "" } */ +} + +void test_u8 (const char *msg) +{ + printf(u8"hello %i", msg);/* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */ +/* TODO: ideally would also underline "msg". */ +/* { dg-begin-multiline-output "" } + printf(u8"hello %i", msg); + ~^ + { dg-end-multiline-output "" } */ +} + +void test_array (long long_value) +{ + const char array[] = "foo %d bar"; /* { dg-message "format string is defined here" } */ +/* { dg-begin-multiline-output "" } + const char array[] = "foo %d bar"; + ~^ + { dg-end-multiline-output "" } */ + + printf (array, long_value); /* { dg-warning "format '%d' expects argument of type 'int', but argument 2 has type 'long int' " } */ +/* { dg-begin-multiline-output "" } + printf (array, long_value); + ^~~~~ + { dg-end-multiline-output "" } */ +} + +void test_param (long long_i, long long_j) +{ + printf ("foo %s bar", long_i + long_j); /* { dg-warning "17: format '%s' expects argument of type 'char \\*', but argument 2 has type 'long int'" } */ +/* { dg-begin-multiline-output "" } + printf ("foo %s bar", long_i + long_j); + ~^ ~~~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ + + const char array[] = "foo %s bar"; /* { dg-message "30: format string is defined here" } */ +/* { dg-begin-multiline-output "" } + const char array[] = "foo %s bar"; + ~^ + { dg-end-multiline-output "" } */ + + printf (array, long_i + long_j); /* { dg-warning "11: format '%s' expects argument of type 'char \\*', but argument 2 has type 'long int'" } */ +/* { dg-begin-multiline-output "" } + printf (array, long_i + long_j); + ^~~~~ ~~~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ +} + +void test_field_width_specifier (long l, int i1, int i2) +{ + printf (" %*.*d ", l, i1, i2); /* { dg-warning "17: field width specifier '\\*' expects argument of type 'int', but argument 2 has type 'long int'" } */ +/* { dg-begin-multiline-output "" } + printf (" %*.*d ", l, i1, i2); + ~~~~^ + { dg-end-multiline-output "" } */ +} + +void test_spurious_percent (void) +{ + printf("hello world %"); /* { dg-warning "23: spurious trailing" } */ + +/* { dg-begin-multiline-output "" } + printf("hello world %"); + ^ + { dg-end-multiline-output "" } */ + + const char arr[] = "hello world %"; /* { dg-message "35: format string is defined here" } */ +/* { dg-begin-multiline-output "" } + const char arr[] = "hello world %"; + ^ + { dg-end-multiline-output "" } */ + printf (arr); /* { dg-warning "11: spurious trailing" } */ +/* { dg-begin-multiline-output "" } + printf (arr); + ^~~ + { dg-end-multiline-output "" } */ + +} + +void test_empty_precision (char *s, size_t m, double d) +{ + strfmon (s, m, "%#.5n", d); /* { dg-warning "20: empty left precision in gnu_strfmon format" } */ +/* { dg-begin-multiline-output "" } + strfmon (s, m, "%#.5n", d); + ^ + { dg-end-multiline-output "" } */ + + strfmon (s, m, "%#5.n", d); /* { dg-warning "22: empty precision in gnu_strfmon format" } */ +/* { dg-begin-multiline-output "" } + strfmon (s, m, "%#5.n", d); + ^ + { dg-end-multiline-output "" } */ +} + +void test_repeated (int i) +{ + printf ("%++d", i); /* { dg-warning "14: repeated '\\+' flag in format" } */ +/* { dg-begin-multiline-output "" } + printf ("%++d", i); + ^ + { dg-end-multiline-output "" } */ + + const char array[] = "%++d"; /* { dg-message "27: format string is defined here" } */ +/* { dg-begin-multiline-output "" } + const char array[] = "%++d"; + ^ + { dg-end-multiline-output "" } */ + printf (array, i); /* { dg-warning "11: repeated '\\+' flag in format" } */ +/* { dg-begin-multiline-output "" } + printf (array, i); + ^~~~~ + { dg-end-multiline-output "" } */ +} + +void test_conversion_lacks_type (void) +{ + printf (" %h"); /* { dg-warning "14:conversion lacks type at end of format" } */ +/* { dg-begin-multiline-output "" } + printf (" %h"); + ^ + { dg-end-multiline-output "" } */ +} + +void test_embedded_nul (void) +{ + printf (" \0 "); /* { dg-warning "14:embedded" "warning for embedded NUL" } */ +/* { dg-begin-multiline-output "" } + printf (" \0 "); + ~^ + { dg-end-multiline-output "" } */ +} -- 1.8.5.3