From: David Malcolm Date: Mon, 8 Aug 2016 22:50:47 +0000 (+0000) Subject: c-format.c: suggest the correct format string to use (PR c/64955) X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=895aa8e11319635104e6fa0dc1468fd6aec7b5b1;p=gcc.git c-format.c: suggest the correct format string to use (PR c/64955) This adds fix-it hints to c-format.c so that it can (sometimes) suggest the format string the user should have used. The patch adds selftests for the new code in c-format.c. These selftests are thus lang-specific. This is the first time we've had lang-specific selftests, and hence the patch also adds a langhook for running them. (Note that currently the Makefile only invokes the selftests for cc1). gcc/c-family/ChangeLog: PR c/64955 * c-common.h (selftest::c_format_c_tests): New declaration. (selftest::run_c_tests): New declaration. * c-format.c: Include "selftest.h. (format_warning_va): Add param "corrected_substring" and use it to add a replacement fix-it hint. (format_warning_at_substring): Likewise. (format_warning_at_char): Update for new param of format_warning_va. (argument_parser::check_argument_type): Pass "fki" to check_format_types. (check_format_types): Add param "fki" and pass it to format_type_warning. (deref_n_times): New function. (get_modifier_for_format_len): New function. (selftest::test_get_modifier_for_format_len): New function. (get_format_for_type): New function. (format_type_warning): Add param "fki" and use it to attempt to provide hints for argument types when calling format_warning_at_substring. (selftest::get_info): New function. (selftest::assert_format_for_type_streq): New function. (ASSERT_FORMAT_FOR_TYPE_STREQ): New macro. (selftest::test_get_format_for_type_printf): New function. (selftest::test_get_format_for_type_scanf): New function. (selftest::c_format_c_tests): New function. gcc/c/ChangeLog: PR c/64955 * c-lang.c (LANG_HOOKS_RUN_LANG_SELFTESTS): If CHECKING_P, wire this up to selftest::run_c_tests. (selftest::run_c_tests): New function. gcc/ChangeLog: PR c/64955 * langhooks-def.h (LANG_HOOKS_RUN_LANG_SELFTESTS): New default do-nothing langhook. (LANG_HOOKS_INITIALIZER): Add LANG_HOOKS_RUN_LANG_SELFTESTS. * langhooks.h (struct lang_hooks): Add run_lang_selftests. * selftest-run-tests.c: Include "tree.h" and "langhooks.h". (selftest::run_tests): Call lang_hooks.run_lang_selftests. gcc/testsuite/ChangeLog: PR c/64955 * gcc.dg/format/diagnostic-ranges.c: Add fix-it hints to expected output. From-SVN: r239260 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 34cf61e7d6d..dd264b31b00 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,13 @@ +2016-08-08 David Malcolm + + PR c/64955 + * langhooks-def.h (LANG_HOOKS_RUN_LANG_SELFTESTS): New default + do-nothing langhook. + (LANG_HOOKS_INITIALIZER): Add LANG_HOOKS_RUN_LANG_SELFTESTS. + * langhooks.h (struct lang_hooks): Add run_lang_selftests. + * selftest-run-tests.c: Include "tree.h" and "langhooks.h". + (selftest::run_tests): Call lang_hooks.run_lang_selftests. + 2016-08-08 David Malcolm PR bootstrap/72844 diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index f03a078b438..5b264e9e7e2 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,32 @@ +2016-08-08 David Malcolm + + PR c/64955 + * c-common.h (selftest::c_format_c_tests): New declaration. + (selftest::run_c_tests): New declaration. + * c-format.c: Include "selftest.h. + (format_warning_va): Add param "corrected_substring" and use + it to add a replacement fix-it hint. + (format_warning_at_substring): Likewise. + (format_warning_at_char): Update for new param of + format_warning_va. + (argument_parser::check_argument_type): Pass "fki" to + check_format_types. + (check_format_types): Add param "fki" and pass it to + format_type_warning. + (deref_n_times): New function. + (get_modifier_for_format_len): New function. + (selftest::test_get_modifier_for_format_len): New function. + (get_format_for_type): New function. + (format_type_warning): Add param "fki" and use it to attempt + to provide hints for argument types when calling + format_warning_at_substring. + (selftest::get_info): New function. + (selftest::assert_format_for_type_streq): New function. + (ASSERT_FORMAT_FOR_TYPE_STREQ): New macro. + (selftest::test_get_format_for_type_printf): New function. + (selftest::test_get_format_for_type_scanf): New function. + (selftest::c_format_c_tests): New function. + 2016-08-08 David Malcolm PR c/52952 diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 7b5da5736e9..61f9ced2bc8 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -1533,4 +1533,11 @@ extern bool valid_array_size_p (location_t, tree, tree); extern bool cilk_ignorable_spawn_rhs_op (tree); extern bool cilk_recognize_spawn (tree, tree *); +#if CHECKING_P +namespace selftest { + extern void c_format_c_tests (void); + extern void run_c_tests (void); +} // namespace selftest +#endif /* #if CHECKING_P */ + #endif /* ! GCC_C_COMMON_H */ diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c index eff2ab4321c..951ffd0037c 100644 --- a/gcc/c-family/c-format.c +++ b/gcc/c-family/c-format.c @@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see #include "langhooks.h" #include "c-format.h" #include "diagnostic.h" +#include "selftest.h" /* Handle attributes associated with format checking. */ @@ -126,11 +127,21 @@ static int format_flags (int format_num); printf(fmt, msg); ^~~ ~~~ + If CORRECTED_SUBSTRING is non-NULL, use it for cases 1 and 2 to provide + a fix-it hint, suggesting that it should replace the text within the + substring range. For example: + + test.c:90:10: warning: problem with '%i' here [-Wformat=] + printf ("hello %i", msg); + ~^ + %s + Return true if a warning was emitted, false otherwise. */ -ATTRIBUTE_GCC_DIAG (4,0) +ATTRIBUTE_GCC_DIAG (5,0) static bool format_warning_va (const substring_loc &fmt_loc, source_range *param_range, + const char *corrected_substring, int opt, const char *gmsgid, va_list *ap) { bool substring_within_range = false; @@ -174,6 +185,9 @@ format_warning_va (const substring_loc &fmt_loc, source_range *param_range, richloc.add_range (param_loc, false); } + if (!err && corrected_substring && substring_within_range) + richloc.add_fixit_replace (fmt_substring_range, corrected_substring); + diagnostic_info diagnostic; diagnostic_set_info (&diagnostic, gmsgid, ap, &richloc, DK_WARNING); diagnostic.option_index = opt; @@ -182,22 +196,31 @@ format_warning_va (const substring_loc &fmt_loc, source_range *param_range, if (!err && substring_loc && !substring_within_range) /* Case 2. */ if (warned) - inform (substring_loc, "format string is defined here"); + { + rich_location substring_richloc (line_table, substring_loc); + if (corrected_substring) + substring_richloc.add_fixit_replace (fmt_substring_range, + corrected_substring); + inform_at_rich_loc (&substring_richloc, + "format string is defined here"); + } return warned; } /* Variadic call to format_warning_va. */ -ATTRIBUTE_GCC_DIAG (4,0) +ATTRIBUTE_GCC_DIAG (5,0) static bool format_warning_at_substring (const substring_loc &fmt_loc, source_range *param_range, + const char *corrected_substring, int opt, const char *gmsgid, ...) { va_list ap; va_start (ap, gmsgid); - bool warned = format_warning_va (fmt_loc, param_range, opt, gmsgid, &ap); + bool warned = format_warning_va (fmt_loc, param_range, corrected_substring, + opt, gmsgid, &ap); va_end (ap); return warned; @@ -225,7 +248,7 @@ format_warning_at_char (location_t fmt_string_loc, tree format_string_cst, 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); + bool warned = format_warning_va (fmt_loc, NULL, NULL, opt, gmsgid, &ap); va_end (ap); return warned; @@ -1126,11 +1149,13 @@ static const format_flag_spec *get_flag_spec (const format_flag_spec *, int, const char *); static void check_format_types (const substring_loc &fmt_loc, - format_wanted_type *); + format_wanted_type *, + const format_kind_info *fki); static void format_type_warning (const substring_loc &fmt_loc, source_range *param_range, format_wanted_type *, tree, - tree); + tree, + const format_kind_info *fki); /* 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 @@ -2786,7 +2811,7 @@ check_argument_type (const format_char_info *fci, 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); + check_format_types (fmt_loc, first_wanted_type, fki); } return true; @@ -2946,7 +2971,7 @@ check_format_info_main (format_check_results *res, location of the format conversion. */ static void check_format_types (const substring_loc &fmt_loc, - format_wanted_type *types) + format_wanted_type *types, const format_kind_info *fki) { for (; types != 0; types = types->next) { @@ -2973,7 +2998,7 @@ check_format_types (const substring_loc &fmt_loc, cur_param = types->param; if (!cur_param) { - format_type_warning (fmt_loc, NULL, types, wanted_type, NULL); + format_type_warning (fmt_loc, NULL, types, wanted_type, NULL, fki); continue; } @@ -3058,7 +3083,7 @@ check_format_types (const substring_loc &fmt_loc, else { format_type_warning (fmt_loc, param_range_ptr, - types, wanted_type, orig_cur_type); + types, wanted_type, orig_cur_type, fki); break; } } @@ -3127,10 +3152,115 @@ check_format_types (const substring_loc &fmt_loc, continue; /* Now we have a type mismatch. */ format_type_warning (fmt_loc, param_range_ptr, types, - wanted_type, orig_cur_type); + wanted_type, orig_cur_type, fki); + } +} + +/* Given type TYPE, attempt to dereference the type N times + (e.g. from ("int ***", 2) to "int *") + + Return the derefenced type, with any qualifiers + such as "const" stripped from the result, or + NULL if unsuccessful (e.g. TYPE is not a pointer type). */ + +static tree +deref_n_times (tree type, int n) +{ + gcc_assert (type); + + for (int i = n; i > 0; i--) + { + if (TREE_CODE (type) != POINTER_TYPE) + return NULL_TREE; + type = TREE_TYPE (type); + } + /* Strip off any "const" etc. */ + return build_qualified_type (type, 0); +} + +/* Lookup the format code for FORMAT_LEN within FLI, + returning the string code for expressing it, or NULL + if it is not found. */ + +static const char * +get_modifier_for_format_len (const format_length_info *fli, + enum format_lengths format_len) +{ + for (; fli->name; fli++) + { + if (fli->index == format_len) + return fli->name; + if (fli->double_index == format_len) + return fli->double_name; } + return NULL; +} + +#if CHECKING_P + +namespace selftest { + +static void +test_get_modifier_for_format_len () +{ + ASSERT_STREQ ("h", + get_modifier_for_format_len (printf_length_specs, FMT_LEN_h)); + ASSERT_STREQ ("hh", + get_modifier_for_format_len (printf_length_specs, FMT_LEN_hh)); + ASSERT_STREQ ("L", + get_modifier_for_format_len (printf_length_specs, FMT_LEN_L)); + ASSERT_EQ (NULL, + get_modifier_for_format_len (printf_length_specs, FMT_LEN_none)); } +} // namespace selftest + +#endif /* CHECKING_P */ + +/* Generate a string containing the format string that should be + used to format arguments of type ARG_TYPE within FKI (effectively + the inverse of the checking code). + + If successful, returns a non-NULL string which should be freed + by the called. + Otherwise, returns NULL. */ + +static char * +get_format_for_type (const format_kind_info *fki, tree arg_type) +{ + gcc_assert (arg_type); + + const format_char_info *spec; + for (spec = &fki->conversion_specs[0]; + spec->format_chars; + spec++) + { + tree effective_arg_type = deref_n_times (arg_type, + spec->pointer_count); + if (!effective_arg_type) + continue; + for (int i = 0; i < FMT_LEN_MAX; i++) + { + const format_type_detail *ftd = &spec->types[i]; + if (!ftd->type) + continue; + if (TYPE_CANONICAL (*ftd->type) + == TYPE_CANONICAL (effective_arg_type)) + { + const char *len_modifier + = get_modifier_for_format_len (fki->length_char_specs, + (enum format_lengths)i); + if (!len_modifier) + len_modifier = ""; + + return xasprintf ("%%%s%c", + len_modifier, + spec->format_chars[0]); + } + } + } + return NULL; +} /* 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 @@ -3144,9 +3274,10 @@ static void format_type_warning (const substring_loc &fmt_loc, source_range *param_range, format_wanted_type *type, - tree wanted_type, tree arg_type) + tree wanted_type, tree arg_type, + const format_kind_info *fki) { - int kind = type->kind; + enum format_specifier_kind kind = type->kind; const char *wanted_type_name = type->wanted_type_name; const char *format_start = type->format_start; int format_length = type->format_length; @@ -3185,12 +3316,18 @@ format_type_warning (const substring_loc &fmt_loc, p[pointer_count + 1] = 0; } + /* Attempt to provide hints for argument types, but not for field widths + and precisions. */ + char *format_for_type = NULL; + if (arg_type && kind == CF_KIND_FORMAT) + format_for_type = get_format_for_type (fki, arg_type); + if (wanted_type_name) { if (arg_type) format_warning_at_substring (fmt_loc, param_range, - OPT_Wformat_, + format_for_type, OPT_Wformat_, "%s %<%s%.*s%> expects argument of type %<%s%s%>, " "but argument %d has type %qT", gettext (kind_descriptions[kind]), @@ -3200,7 +3337,7 @@ format_type_warning (const substring_loc &fmt_loc, else format_warning_at_substring (fmt_loc, param_range, - OPT_Wformat_, + format_for_type, OPT_Wformat_, "%s %<%s%.*s%> expects a matching %<%s%s%> argument", gettext (kind_descriptions[kind]), (kind == CF_KIND_FORMAT ? "%" : ""), @@ -3211,7 +3348,7 @@ format_type_warning (const substring_loc &fmt_loc, if (arg_type) format_warning_at_substring (fmt_loc, param_range, - OPT_Wformat_, + format_for_type, OPT_Wformat_, "%s %<%s%.*s%> expects argument of type %<%T%s%>, " "but argument %d has type %qT", gettext (kind_descriptions[kind]), @@ -3221,12 +3358,14 @@ format_type_warning (const substring_loc &fmt_loc, else format_warning_at_substring (fmt_loc, param_range, - OPT_Wformat_, + format_for_type, 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); } + + free (format_for_type); } @@ -3747,3 +3886,96 @@ handle_format_attribute (tree *node, tree ARG_UNUSED (name), tree args, return NULL_TREE; } + +#if CHECKING_P + +namespace selftest { + +/* Selftests of location handling. */ + +/* Get the format_kind_info with the given name. */ + +static const format_kind_info * +get_info (const char *name) +{ + int idx = decode_format_type (name); + const format_kind_info *fki = &format_types[idx]; + ASSERT_STREQ (fki->name, name); + return fki; +} + +/* Verify that get_format_for_type (FKI, TYPE) is EXPECTED_FORMAT. */ + +static void +assert_format_for_type_streq (const location &loc, const format_kind_info *fki, + const char *expected_format, tree type) +{ + gcc_assert (fki); + gcc_assert (expected_format); + gcc_assert (type); + + char *actual_format = get_format_for_type (fki, type); + ASSERT_STREQ_AT (loc, expected_format, actual_format); + free (actual_format); +} + +/* Selftests for get_format_for_type. */ + +#define ASSERT_FORMAT_FOR_TYPE_STREQ(EXPECTED_FORMAT, TYPE) \ + assert_format_for_type_streq (SELFTEST_LOCATION, (fki), (EXPECTED_FORMAT), (TYPE)) + +/* Selftest for get_format_for_type for "printf"-style functions. */ + +static void +test_get_format_for_type_printf () +{ + const format_kind_info *fki = get_info ("gnu_printf"); + ASSERT_NE (fki, NULL); + + ASSERT_FORMAT_FOR_TYPE_STREQ ("%f", double_type_node); + ASSERT_FORMAT_FOR_TYPE_STREQ ("%Lf", long_double_type_node); + ASSERT_FORMAT_FOR_TYPE_STREQ ("%d", integer_type_node); + ASSERT_FORMAT_FOR_TYPE_STREQ ("%o", unsigned_type_node); + ASSERT_FORMAT_FOR_TYPE_STREQ ("%ld", long_integer_type_node); + ASSERT_FORMAT_FOR_TYPE_STREQ ("%lo", long_unsigned_type_node); + ASSERT_FORMAT_FOR_TYPE_STREQ ("%lld", long_long_integer_type_node); + ASSERT_FORMAT_FOR_TYPE_STREQ ("%llo", long_long_unsigned_type_node); + ASSERT_FORMAT_FOR_TYPE_STREQ ("%s", build_pointer_type (char_type_node)); +} + +/* Selftest for get_format_for_type for "scanf"-style functions. */ + +static void +test_get_format_for_type_scanf () +{ + const format_kind_info *fki = get_info ("gnu_scanf"); + ASSERT_NE (fki, NULL); + ASSERT_FORMAT_FOR_TYPE_STREQ ("%d", build_pointer_type (integer_type_node)); + ASSERT_FORMAT_FOR_TYPE_STREQ ("%u", build_pointer_type (unsigned_type_node)); + ASSERT_FORMAT_FOR_TYPE_STREQ ("%ld", + build_pointer_type (long_integer_type_node)); + ASSERT_FORMAT_FOR_TYPE_STREQ ("%lu", + build_pointer_type (long_unsigned_type_node)); + ASSERT_FORMAT_FOR_TYPE_STREQ + ("%lld", build_pointer_type (long_long_integer_type_node)); + ASSERT_FORMAT_FOR_TYPE_STREQ + ("%llu", build_pointer_type (long_long_unsigned_type_node)); + ASSERT_FORMAT_FOR_TYPE_STREQ ("%e", build_pointer_type (float_type_node)); + ASSERT_FORMAT_FOR_TYPE_STREQ ("%le", build_pointer_type (double_type_node)); +} + +#undef ASSERT_FORMAT_FOR_TYPE_STREQ + +/* Run all of the selftests within this file. */ + +void +c_format_c_tests () +{ + test_get_modifier_for_format_len (); + test_get_format_for_type_printf (); + test_get_format_for_type_scanf (); +} + +} // namespace selftest + +#endif /* CHECKING_P */ diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index ecae4f158bb..3161dcb6a02 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,10 @@ +2016-08-08 David Malcolm + + PR c/64955 + * c-lang.c (LANG_HOOKS_RUN_LANG_SELFTESTS): If CHECKING_P, wire + this up to selftest::run_c_tests. + (selftest::run_c_tests): New function. + 2016-08-04 Thomas Schwinge * c-parser.c (struct oacc_routine_data): Add error_seen and diff --git a/gcc/c/c-lang.c b/gcc/c/c-lang.c index 89954b749cf..b26be6ad92c 100644 --- a/gcc/c/c-lang.c +++ b/gcc/c/c-lang.c @@ -38,7 +38,29 @@ enum c_language_kind c_language = clk_c; #undef LANG_HOOKS_INIT_TS #define LANG_HOOKS_INIT_TS c_common_init_ts +#if CHECKING_P +#undef LANG_HOOKS_RUN_LANG_SELFTESTS +#define LANG_HOOKS_RUN_LANG_SELFTESTS selftest::run_c_tests +#endif /* #if CHECKING_P */ + /* Each front end provides its own lang hook initializer. */ struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; +#if CHECKING_P + +namespace selftest { + +/* Implementation of LANG_HOOKS_RUN_LANG_SELFTESTS for the C frontend. */ + +void +run_c_tests (void) +{ + c_format_c_tests (); +} + +} // namespace selftest + +#endif /* #if CHECKING_P */ + + #include "gtype-c.h" diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h index 034b3b7e84c..c17f9984184 100644 --- a/gcc/langhooks-def.h +++ b/gcc/langhooks-def.h @@ -120,6 +120,7 @@ extern bool lhd_omp_mappable_type (tree); #define LANG_HOOKS_BLOCK_MAY_FALLTHRU hook_bool_const_tree_true #define LANG_HOOKS_EH_USE_CXA_END_CLEANUP false #define LANG_HOOKS_DEEP_UNSHARING false +#define LANG_HOOKS_RUN_LANG_SELFTESTS lhd_do_nothing /* Attribute hooks. */ #define LANG_HOOKS_ATTRIBUTE_TABLE NULL @@ -319,7 +320,8 @@ extern void lhd_end_section (void); LANG_HOOKS_EH_PROTECT_CLEANUP_ACTIONS, \ LANG_HOOKS_BLOCK_MAY_FALLTHRU, \ LANG_HOOKS_EH_USE_CXA_END_CLEANUP, \ - LANG_HOOKS_DEEP_UNSHARING \ + LANG_HOOKS_DEEP_UNSHARING, \ + LANG_HOOKS_RUN_LANG_SELFTESTS \ } #endif /* GCC_LANG_HOOKS_DEF_H */ diff --git a/gcc/langhooks.h b/gcc/langhooks.h index 0593424e026..169a6784b6a 100644 --- a/gcc/langhooks.h +++ b/gcc/langhooks.h @@ -505,6 +505,9 @@ struct lang_hooks gimplification. */ bool deep_unsharing; + /* Run all lang-specific selftests. */ + void (*run_lang_selftests) (void); + /* Whenever you add entries here, make sure you adjust langhooks-def.h and langhooks.c accordingly. */ }; diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c index 85e101d42c3..9d75a8e3c31 100644 --- a/gcc/selftest-run-tests.c +++ b/gcc/selftest-run-tests.c @@ -21,6 +21,8 @@ along with GCC; see the file COPYING3. If not see #include "system.h" #include "coretypes.h" #include "selftest.h" +#include "tree.h" +#include "langhooks.h" /* This function needed to be split out from selftest.c as it references tests from the whole source tree, and so is within @@ -70,6 +72,9 @@ selftest::run_tests () /* This one relies on most of the above. */ function_tests_c_tests (); + /* Run any lang-specific selftests. */ + lang_hooks.run_lang_selftests (); + /* Finished running tests. */ long finish_time = get_run_time (); long elapsed_time = finish_time - start_time; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 93f80904ab2..777315e7be4 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2016-08-08 David Malcolm + + PR c/64955 + * gcc.dg/format/diagnostic-ranges.c: Add fix-it hints to expected + output. + 2016-08-08 Prathamesh Kulkarni * gcc.dg/tree-ssa/pr71078-1.c: Add require-effective-target diff --git a/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c b/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c index 9e86b5210f1..ff518333b6a 100644 --- a/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c +++ b/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c @@ -12,6 +12,25 @@ void test_mismatching_types (const char *msg) /* { dg-begin-multiline-output "" } printf("hello %i", msg); ~^ + %s + { dg-end-multiline-output "" } */ + + + printf("hello %s", 42); /* { dg-warning "format '%s' expects argument of type 'char \\*', but argument 2 has type 'int'" } */ +/* TODO: ideally would also underline "42". */ +/* { dg-begin-multiline-output "" } + printf("hello %s", 42); + ~^ + %d + { dg-end-multiline-output "" } */ + + + printf("hello %i", (long)0); /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'long int' " } */ +/* TODO: ideally would also underline the argument. */ +/* { dg-begin-multiline-output "" } + printf("hello %i", (long)0); + ~^ + %ld { dg-end-multiline-output "" } */ } @@ -23,6 +42,7 @@ void test_multiple_arguments (void) /* { dg-begin-multiline-output "" } printf ("arg0: %i arg1: %s arg 2: %i", ~^ + %d { dg-end-multiline-output "" } */ } @@ -33,6 +53,7 @@ void test_multiple_arguments_2 (int i, int j) /* { dg-begin-multiline-output "" } printf ("arg0: %i arg1: %s arg 2: %i", ~^ + %d 100, i + j, 102); ~~~~~ { dg-end-multiline-output "" } */ @@ -67,6 +88,7 @@ void test_hex (const char *msg) /* { dg-begin-multiline-output "" } printf("hello \x25\x69", msg); ~~~~~~~^ + %s { dg-end-multiline-output "" } */ } @@ -80,6 +102,7 @@ void test_oct (const char *msg) /* { dg-begin-multiline-output "" } printf("hello \045\151", msg); ~~~~~~~^ + %s { dg-end-multiline-output "" } */ } @@ -98,6 +121,7 @@ void test_multiple (const char *msg) /* { dg-begin-multiline-output "" } printf("prefix" "\x25" "\151" "suffix", ~~~~~~~~~~~^ + %s { dg-end-multiline-output "" } */ } @@ -108,6 +132,7 @@ void test_u8 (const char *msg) /* { dg-begin-multiline-output "" } printf(u8"hello %i", msg); ~^ + %s { dg-end-multiline-output "" } */ } @@ -117,6 +142,7 @@ void test_param (long long_i, long long_j) /* { dg-begin-multiline-output "" } printf ("foo %s bar", long_i + long_j); ~^ ~~~~~~~~~~~~~~~ + %ld { dg-end-multiline-output "" } */ } @@ -192,13 +218,14 @@ void test_macro (const char *msg) /* { dg-begin-multiline-output "" } #define INT_FMT "%i" ~^ + %s { 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-message "26: format string is defined here" "" { target *-*-* } 227 } */ /* { dg-begin-multiline-output "" } __builtin_printf(" %" "d ", 0.5); ^~~~ @@ -206,6 +233,7 @@ void test_non_contiguous_strings (void) /* { dg-begin-multiline-output "" } __builtin_printf(" %" "d ", 0.5); ~~~~^ + %f { dg-end-multiline-output "" } */ }