From 2baa6b51992d07fe9d60353be2ec29db27c1b104 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Sat, 23 Jul 2016 21:30:27 -0400 Subject: [PATCH 07/34] FIXME: use new loc in c-format.c --- gcc/c-family/c-format.c | 468 +++++++++++++++--------- 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 | 222 +++++++++++ 6 files changed, 544 insertions(+), 182 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..72ede66 100644 --- a/gcc/c-family/c-format.c +++ b/gcc/c-family/c-format.c @@ -29,6 +29,8 @@ along with GCC; see the file COPYING3. If not see #include "intl.h" #include "langhooks.h" #include "c-format.h" +#include "diagnostic.h" +#include "c-pragma.h" /* Handle attributes associated with format checking. */ @@ -65,78 +67,169 @@ 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. */ +/* Emit a warning governed by option OPT, using GMSGID as the format + string and AP as its arguments. -static unsigned int -location_column_from_byte_offset (const char *s, int line_width, - unsigned int offset) -{ - const char * c = s; - if (*c != '"') - return 0; + Attempt to obtain precise location information within a string + literal from FMT_LOC. + + Case 1: if substring location is available, and is within the range of + the format string itself, the primary location of the + diagnostic is the substring range obtained from FMT_LOC, with the + caret at the *end* of the substring range. + + For example: + + test.c:90:10: warning: problem with '%i' here [-Wformat=] + printf ("hello %i", msg); + ~^ + + Case 2: if the substring location is available, but is not within + the range of the format string, the primary location is that of the + format string, and an note is emitted showing the substring location. + + For example: + test.c:90:10: warning: problem with '%i' here [-Wformat=] + printf("hello " INT_FMT " world", msg); + ^~~~~~~~~~~~~~~~~~~~~~~~~ + test.c:19: note: format string is defined here + #define INT_FMT "%i" + ~^ + + Case 3: if precise substring information is unavailable, the primary + location is that of the whole string passed to FMT_LOC's constructor. + For example: + + test.c:90:10: warning: problem with '%i' here [-Wformat=] + printf(fmt, msg); + ^~~ + + For each of cases 1-3, if param_range is non-NULL, then it is used + as a secondary range within the warning. For example, here it + is used with case 1: - c++, offset--; - while (offset > 0) + test.c:90:16: warning: '%s' here but arg 2 has 'long' type [-Wformat=] + printf ("foo %s bar", long_i + long_j); + ~^ ~~~~~~~~~~~~~~~ + + and here with case 2: + + test.c:90:16: warning: '%s' here but arg 2 has 'long' type [-Wformat=] + printf ("foo " STR_FMT " bar", long_i + long_j); + ^~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~ + test.c:89:16: note: format string is defined here + #define STR_FMT "%s" + ~^ + + and with case 3: + + test.c:90:10: warning: '%i' here, but arg 2 is "const char *' [-Wformat=] + printf(fmt, msg); + ^~~ ~~~ + + Return true if a warning was emitted, false otherwise. */ + +ATTRIBUTE_GCC_DIAG (4,0) +static bool +format_warning_va (const substring_loc &fmt_loc, source_range *param_range, + int opt, const char *gmsgid, va_list *ap) +{ + bool substring_within_range; + location_t primary_loc; + location_t substring_loc = UNKNOWN_LOCATION; + source_range fmt_loc_range + = get_range_from_loc (line_table, fmt_loc.get_fmt_string_loc ()); + source_range fmt_substring_range; + const char *err = fmt_loc.get_range (&fmt_substring_range); + if (err) + /* Case 3: unable to get substring location. */ + primary_loc = fmt_loc.get_fmt_string_loc (); + else { - if (c - s >= line_width) - return 0; + substring_loc = make_location (fmt_substring_range.m_finish, + fmt_substring_range.m_start, + fmt_substring_range.m_finish); - switch (*c) + if (fmt_substring_range.m_start >= fmt_loc_range.m_start + && fmt_substring_range.m_finish <= fmt_loc_range.m_finish) + /* Case 1. */ { - 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; - - default: - return 0; - } - break; - - case '"': - /* We found the end of the string too early. */ - return 0; - - default: - c++, offset--; - break; + substring_within_range = true; + primary_loc = substring_loc; } + else + /* Case 2. */ + { + substring_within_range = false; + primary_loc = fmt_loc.get_fmt_string_loc (); + } + } + + rich_location richloc (line_table, primary_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; + + diagnostic_info diagnostic; + diagnostic_set_info (&diagnostic, gmsgid, ap, &richloc, DK_WARNING); + diagnostic.option_index = opt; + bool warned = report_diagnostic (&diagnostic); + + if (substring_loc && !substring_within_range) + /* Case 2. */ + if (warned) + inform (substring_loc, "format string is defined here"); + + return warned; } -/* Return a location that encodes the same location as LOC but shifted - by OFFSET bytes. */ +/* Variadic call to format_warning_va. */ -static location_t -location_from_offset (location_t loc, int offset) +ATTRIBUTE_GCC_DIAG (4,0) +static bool +format_warning (const substring_loc &fmt_loc, + 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_loc, param_range, opt, gmsgid, &ap); + va_end (ap); + + return warned; +} - 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); +/* Emit a warning as per format_warning_va, but construct the substring_loc + for the character at offset (CHAR_IDX - 1) within a string constant + FORMAT_STRING_CST at FMT_STRING_LOC. */ - return linemap_position_for_loc_and_offset (line_table, loc, column); +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, ...) +{ + va_list ap; + va_start (ap, gmsgid); + tree string_type = TREE_TYPE (format_string_cst); + + /* The callers are of the form: + format_warning (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + where format_chars has already been incremented, so that + CHAR_IDX is one character beyond where the warning should + be emitted. Fix it. */ + char_idx -= 1; + + substring_loc fmt_loc (fmt_string_loc, string_type, char_idx, char_idx); + bool warned = format_warning_va (fmt_loc, 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 +1112,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 +1126,12 @@ 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 (const substring_loc &fmt_loc, + format_wanted_type *); +static void format_type_warning (const substring_loc &fmt_loc, + 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 +1607,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 +1784,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 +1799,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 +1851,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 +1862,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 +1899,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 +2025,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 +2106,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 +2194,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 +2214,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 +2228,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 +2263,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 +2380,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 +2396,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 +2418,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 +2525,22 @@ 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; + substring_loc fmt_loc (fmt_param_loc, + TREE_TYPE (format_string_cst), + offset_to_format_start, + offset_to_format_end); + check_format_types (fmt_loc, 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 +2551,12 @@ check_format_info_main (format_check_results *res, finish_dollar_format_checking (res, fki->flags & (int) FMT_FLAG_DOLLAR_GAP_POINTER_OK); } - /* 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_LOC is the + location of the format conversion. */ static void -check_format_types (location_t loc, format_wanted_type *types) +check_format_types (const substring_loc &fmt_loc, + format_wanted_type *types) { for (; types != 0; types = types->next) { @@ -2471,7 +2583,7 @@ 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, NULL, types, wanted_type, NULL); continue; } @@ -2481,6 +2593,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 +2667,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, param_range_ptr, + types, wanted_type, orig_cur_type); break; } } @@ -2613,20 +2736,24 @@ 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, 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. 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 (const substring_loc &fmt_loc, + source_range *param_range, + format_wanted_type *type, tree wanted_type, tree arg_type) { int kind = type->kind; @@ -2635,7 +2762,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 +2795,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, 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, 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, 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, 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..9e86b52 --- /dev/null +++ b/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c @@ -0,0 +1,222 @@ +/* { 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_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 "" } */ +} + +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 "" } */ +} + +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 "" } */ +} + +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 "" } */ +} + +void test_macro (const char *msg) +{ +#define INT_FMT "%i" /* { dg-message "19: format string is defined here" } */ + printf("hello " INT_FMT " world", msg); /* { dg-warning "10: format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */ +/* { dg-begin-multiline-output "" } + printf("hello " INT_FMT " world", msg); + ^~~~~~~~ + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } + #define INT_FMT "%i" + ~^ + { dg-end-multiline-output "" } */ +} + +void test_non_contiguous_strings (void) +{ + __builtin_printf(" %" "d ", 0.5); /* { dg-warning "20: format .%d. expects argument of type .int., but argument 2 has type .double." } */ + /* { dg-message "26: format string is defined here" "" { target *-*-* } 200 } */ + /* { dg-begin-multiline-output "" } + __builtin_printf(" %" "d ", 0.5); + ^~~~ + { dg-end-multiline-output "" } */ + /* { dg-begin-multiline-output "" } + __builtin_printf(" %" "d ", 0.5); + ~~~~^ + { dg-end-multiline-output "" } */ +} + +void test_const_arrays (void) +{ + /* TODO: ideally we'd highlight both the format string *and* the use of + it here. For now, just verify that we gracefully handle this case. */ + const char a[] = " %d "; + __builtin_printf(a, 0.5); /* { dg-warning "20: format .%d. expects argument of type .int., but argument 2 has type .double." } */ + /* { dg-begin-multiline-output "" } + __builtin_printf(a, 0.5); + ^ + { dg-end-multiline-output "" } */ +} -- 1.8.5.3