Commit 0d48e877 by David Malcolm Committed by David Malcolm

Support string locations for C++ in -Wformat (PR c++/56856)

-Wformat in the C++ FE doesn't work as well as it could:
(a) it doesn't report precise locations within the string literal, and
(b) it doesn't underline arguments for those arguments !CAN_HAVE_LOCATION_P,
despite having location wrapper nodes.

For example:

  Wformat-ranges.C:32:10: warning: format '%s' expects argument of type 'char*', but argument 2 has type 'int' [-Wformat=]
  32 |   printf("hello %s", 42);
     |          ^~~~~~~~~~

(a) is due to not wiring up the langhook for extracting substring
    locations.

    This patch uses the one in c-family; it also fixes string literal
    parsing so that it records string concatenations (needed for
    extracting substring locations from concatenated strings).

(b) is due to the call to maybe_constant_value here:
       fargs[j] = maybe_constant_value (argarray[j]);
    within build_over_call.

    The patch fixes this by building a vec of location_t values when
    calling check_function_arguments.
    I attempted to eliminate the maybe_constant_value call here, but
    it's needed by e.g. check_function_sentinel for detecting NULL,
    and that code is in "c-family", so it can't simply call into
    maybe_constant_value (which is in "cp").

With this patch, the output for the above example is improved to:

  Wformat-ranges.C:32:18: warning: format '%s' expects argument of type 'char*', but argument 2 has type 'int' [-Wformat=]
  32 |   printf("hello %s", 42);
     |                 ~^   ~~
     |                  |   |
     |                  |   int
     |                  char*
     |                 %d

gcc/cp/ChangeLog:
	PR c++/56856
	* call.c (build_over_call): Build a vec of locations of the
	arguments before the call to maybe_constant_value, and pass to
	check_function_arguments.
	* cp-lang.c (LANG_HOOKS_GET_SUBSTRING_LOCATION): Define as
	c_get_substring_location.
	* parser.c (cp_parser_string_literal): Capture string
	concatenation locations.

gcc/ChangeLog:
	PR c++/56856
	* input.c (expand_location_to_spelling_point): Add param "aspect"
	and use rather than hardcoding LOCATION_ASPECT_CARET.
	(get_substring_ranges_for_loc): Handle the case of a single token
	within a macro expansion.
	* input.h (expand_location_to_spelling_point): Add "aspect" param,
	defaulting to LOCATION_ASPECT_CARET.

gcc/testsuite/ChangeLog:
	PR c++/56856
	* g++.dg/ext/builtin4.C: Set expected location for warning to the
	correct location within the format string.
	* g++.dg/plugin/plugin.exp (plugin_test_list): Add the plugin and
	files for testing locations within string literal locations from
	the C frontend.
	* g++.dg/warn/Wformat-method.C: New test.
	* g++.dg/warn/Wformat-pr71863.C: New test.
	* g++.dg/warn/Wformat-ranges-c++11.C: New test.
	* g++.dg/warn/Wformat-ranges.C: New test, based on
	gcc.dg/format/diagnostic-ranges.c.
	* gcc.dg/plugin/diagnostic-test-string-literals-1.c
	(test_multitoken_macro): Generalize expected output to work with
	both C and C++.
	* gcc.dg/plugin/diagnostic-test-string-literals-2.c
	(test_stringified_token_1): Likewise.
	(test_stringified_token_3): Likewise.

From-SVN: r264887
parent 1f58c814
2018-10-05 David Malcolm <dmalcolm@redhat.com>
PR c++/56856
* input.c (expand_location_to_spelling_point): Add param "aspect"
and use rather than hardcoding LOCATION_ASPECT_CARET.
(get_substring_ranges_for_loc): Handle the case of a single token
within a macro expansion.
* input.h (expand_location_to_spelling_point): Add "aspect" param,
defaulting to LOCATION_ASPECT_CARET.
2018-10-05 Paul Koning <ni1d@arrl.net> 2018-10-05 Paul Koning <ni1d@arrl.net>
* config/pdp11/pdp11.c (TARGET_CXX_GUARD_TYPE): Define. * config/pdp11/pdp11.c (TARGET_CXX_GUARD_TYPE): Define.
2018-10-05 David Malcolm <dmalcolm@redhat.com>
PR c++/56856
* call.c (build_over_call): Build a vec of locations of the
arguments before the call to maybe_constant_value, and pass to
check_function_arguments.
* cp-lang.c (LANG_HOOKS_GET_SUBSTRING_LOCATION): Define as
c_get_substring_location.
* parser.c (cp_parser_string_literal): Capture string
concatenation locations.
2018-10-04 Nathan Sidwell <nathan@acm.org> 2018-10-04 Nathan Sidwell <nathan@acm.org>
* lang-specs.h: Use string contatenation, not line splicing. * lang-specs.h: Use string contatenation, not line splicing.
......
...@@ -8188,6 +8188,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) ...@@ -8188,6 +8188,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
{ {
tree *fargs = (!nargs ? argarray tree *fargs = (!nargs ? argarray
: (tree *) alloca (nargs * sizeof (tree))); : (tree *) alloca (nargs * sizeof (tree)));
auto_vec<location_t> arglocs (nargs);
for (j = 0; j < nargs; j++) for (j = 0; j < nargs; j++)
{ {
/* For -Wformat undo the implicit passing by hidden reference /* For -Wformat undo the implicit passing by hidden reference
...@@ -8197,10 +8198,11 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) ...@@ -8197,10 +8198,11 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
fargs[j] = TREE_OPERAND (argarray[j], 0); fargs[j] = TREE_OPERAND (argarray[j], 0);
else else
fargs[j] = maybe_constant_value (argarray[j]); fargs[j] = maybe_constant_value (argarray[j]);
arglocs.quick_push (EXPR_LOC_OR_LOC (argarray[j], input_location));
} }
warned_p = check_function_arguments (input_location, fn, TREE_TYPE (fn), warned_p = check_function_arguments (input_location, fn, TREE_TYPE (fn),
nargs, fargs, NULL); nargs, fargs, &arglocs);
} }
if (DECL_INHERITED_CTOR (fn)) if (DECL_INHERITED_CTOR (fn))
......
...@@ -83,6 +83,9 @@ static tree cxx_enum_underlying_base_type (const_tree); ...@@ -83,6 +83,9 @@ static tree cxx_enum_underlying_base_type (const_tree);
#define LANG_HOOKS_RUN_LANG_SELFTESTS selftest::run_cp_tests #define LANG_HOOKS_RUN_LANG_SELFTESTS selftest::run_cp_tests
#endif /* #if CHECKING_P */ #endif /* #if CHECKING_P */
#undef LANG_HOOKS_GET_SUBSTRING_LOCATION
#define LANG_HOOKS_GET_SUBSTRING_LOCATION c_get_substring_location
/* Each front end provides its own lang hook initializer. */ /* Each front end provides its own lang hook initializer. */
struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
......
...@@ -4034,6 +4034,7 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok, ...@@ -4034,6 +4034,7 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
tree value; tree value;
size_t count; size_t count;
struct obstack str_ob; struct obstack str_ob;
struct obstack loc_ob;
cpp_string str, istr, *strs; cpp_string str, istr, *strs;
cp_token *tok; cp_token *tok;
enum cpp_ttype type, curr_type; enum cpp_ttype type, curr_type;
...@@ -4090,6 +4091,7 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok, ...@@ -4090,6 +4091,7 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
{ {
location_t last_tok_loc = tok->location; location_t last_tok_loc = tok->location;
gcc_obstack_init (&str_ob); gcc_obstack_init (&str_ob);
gcc_obstack_init (&loc_ob);
count = 0; count = 0;
do do
...@@ -4135,6 +4137,7 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok, ...@@ -4135,6 +4137,7 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
} }
obstack_grow (&str_ob, &str, sizeof (cpp_string)); obstack_grow (&str_ob, &str, sizeof (cpp_string));
obstack_grow (&loc_ob, &tok->location, sizeof (location_t));
last_tok_loc = tok->location; last_tok_loc = tok->location;
...@@ -4173,6 +4176,12 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok, ...@@ -4173,6 +4176,12 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
{ {
value = build_string (istr.len, (const char *)istr.text); value = build_string (istr.len, (const char *)istr.text);
free (CONST_CAST (unsigned char *, istr.text)); free (CONST_CAST (unsigned char *, istr.text));
if (count > 1)
{
location_t *locs = (location_t *)obstack_finish (&loc_ob);
gcc_assert (g_string_concat_db);
g_string_concat_db->record_string_concatenation (count, locs);
}
switch (type) switch (type)
{ {
...@@ -4209,7 +4218,10 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok, ...@@ -4209,7 +4218,10 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
value = error_mark_node; value = error_mark_node;
if (count > 1) if (count > 1)
obstack_free (&str_ob, 0); {
obstack_free (&str_ob, 0);
obstack_free (&loc_ob, 0);
}
return cp_expr (value, loc); return cp_expr (value, loc);
} }
...@@ -813,10 +813,10 @@ expand_location (source_location loc) ...@@ -813,10 +813,10 @@ expand_location (source_location loc)
"<built-in>". */ "<built-in>". */
expanded_location expanded_location
expand_location_to_spelling_point (source_location loc) expand_location_to_spelling_point (source_location loc,
enum location_aspect aspect)
{ {
return expand_location_1 (loc, /*expansion_point_p=*/false, return expand_location_1 (loc, /*expansion_point_p=*/false, aspect);
LOCATION_ASPECT_CARET);
} }
/* The rich_location class within libcpp requires a way to expand /* The rich_location class within libcpp requires a way to expand
...@@ -1397,24 +1397,32 @@ get_substring_ranges_for_loc (cpp_reader *pfile, ...@@ -1397,24 +1397,32 @@ get_substring_ranges_for_loc (cpp_reader *pfile,
source_range src_range = get_range_from_loc (line_table, strlocs[i]); source_range src_range = get_range_from_loc (line_table, strlocs[i]);
if (src_range.m_start >= LINEMAPS_MACRO_LOWEST_LOCATION (line_table)) if (src_range.m_start >= LINEMAPS_MACRO_LOWEST_LOCATION (line_table))
/* If the string is within a macro expansion, we can't get at the {
end location. */ /* If the string token was within a macro expansion, then we can
return "macro expansion"; cope with it for the simple case where we have a single token.
Otherwise, bail out. */
if (src_range.m_start >= LINE_MAP_MAX_LOCATION_WITH_COLS) if (src_range.m_start != src_range.m_finish)
/* If so, we can't reliably determine where the token started within return "macro expansion";
its line. */ }
return "range starts after LINE_MAP_MAX_LOCATION_WITH_COLS"; else
{
if (src_range.m_finish >= LINE_MAP_MAX_LOCATION_WITH_COLS) if (src_range.m_start >= LINE_MAP_MAX_LOCATION_WITH_COLS)
/* If so, we can't reliably determine where the token finished within /* If so, we can't reliably determine where the token started within
its line. */ its line. */
return "range ends after LINE_MAP_MAX_LOCATION_WITH_COLS"; return "range starts after LINE_MAP_MAX_LOCATION_WITH_COLS";
if (src_range.m_finish >= LINE_MAP_MAX_LOCATION_WITH_COLS)
/* If so, we can't reliably determine where the token finished
within its line. */
return "range ends after LINE_MAP_MAX_LOCATION_WITH_COLS";
}
expanded_location start expanded_location start
= expand_location_to_spelling_point (src_range.m_start); = expand_location_to_spelling_point (src_range.m_start,
LOCATION_ASPECT_START);
expanded_location finish expanded_location finish
= expand_location_to_spelling_point (src_range.m_finish); = expand_location_to_spelling_point (src_range.m_finish,
LOCATION_ASPECT_FINISH);
if (start.file != finish.file) if (start.file != finish.file)
return "range endpoints are in different files"; return "range endpoints are in different files";
if (start.line != finish.line) if (start.line != finish.line)
......
...@@ -85,7 +85,10 @@ class char_span ...@@ -85,7 +85,10 @@ class char_span
extern char_span location_get_source_line (const char *file_path, int line); extern char_span location_get_source_line (const char *file_path, int line);
extern bool location_missing_trailing_newline (const char *file_path); extern bool location_missing_trailing_newline (const char *file_path);
extern expanded_location expand_location_to_spelling_point (source_location); extern expanded_location
expand_location_to_spelling_point (source_location,
enum location_aspect aspect
= LOCATION_ASPECT_CARET);
extern source_location expansion_point_location_if_in_system_header (source_location); extern source_location expansion_point_location_if_in_system_header (source_location);
extern source_location expansion_point_location (source_location); extern source_location expansion_point_location (source_location);
......
2018-10-05 David Malcolm <dmalcolm@redhat.com> 2018-10-05 David Malcolm <dmalcolm@redhat.com>
PR c++/56856
* g++.dg/ext/builtin4.C: Set expected location for warning to the
correct location within the format string.
* g++.dg/plugin/plugin.exp (plugin_test_list): Add the plugin and
files for testing locations within string literal locations from
the C frontend.
* g++.dg/warn/Wformat-method.C: New test.
* g++.dg/warn/Wformat-pr71863.C: New test.
* g++.dg/warn/Wformat-ranges-c++11.C: New test.
* g++.dg/warn/Wformat-ranges.C: New test, based on
gcc.dg/format/diagnostic-ranges.c.
* gcc.dg/plugin/diagnostic-test-string-literals-1.c
(test_multitoken_macro): Generalize expected output to work with
both C and C++.
* gcc.dg/plugin/diagnostic-test-string-literals-2.c
(test_stringified_token_1): Likewise.
(test_stringified_token_3): Likewise.
2018-10-05 David Malcolm <dmalcolm@redhat.com>
* lib/multiline.exp (proc dg-end-multiline-output): Check argument * lib/multiline.exp (proc dg-end-multiline-output): Check argument
count. If there's a 3rd argument, use dg-process-target on it, count. If there's a 3rd argument, use dg-process-target on it,
bailing out, or recording expected failures as "maybe_x". bailing out, or recording expected failures as "maybe_x".
......
...@@ -6,5 +6,5 @@ ...@@ -6,5 +6,5 @@
extern "C" int printf(const char*,...); extern "C" int printf(const char*,...);
void foo() { void foo() {
printf("%d"); // { dg-warning "expects a matching" } printf("%d"); // { dg-warning "12: expects a matching" }
} }
...@@ -67,6 +67,11 @@ set plugin_test_list [list \ ...@@ -67,6 +67,11 @@ set plugin_test_list [list \
diagnostic-test-expressions-1.C } \ diagnostic-test-expressions-1.C } \
{ ../../gcc.dg/plugin/diagnostic_plugin_test_inlining.c \ { ../../gcc.dg/plugin/diagnostic_plugin_test_inlining.c \
diagnostic-test-inlining-1.C } \ diagnostic-test-inlining-1.C } \
{ ../../gcc.dg/plugin/diagnostic_plugin_test_string_literals.c \
../../gcc.dg/plugin/diagnostic-test-string-literals-1.c \
../../gcc.dg/plugin/diagnostic-test-string-literals-2.c \
../../gcc.dg/plugin/diagnostic-test-string-literals-3.c \
../../gcc.dg/plugin/diagnostic-test-string-literals-4.c } \
{ show_template_tree_color_plugin.c \ { show_template_tree_color_plugin.c \
show-template-tree-color.C \ show-template-tree-color.C \
show-template-tree-color-labels.C \ show-template-tree-color-labels.C \
......
// { dg-options "-Wformat -fdiagnostics-show-caret" }
class logger
{
public:
void log (const char *fmt, ...) __attribute__((format (printf, 2, 3)));
};
void test ()
{
logger out;
out.log ("before %s after", 42); // { dg-warning "argument 3 has type 'int'" }
/* { dg-begin-multiline-output "" }
out.log ("before %s after", 42);
~^ ~~
| |
char* int
%d
{ dg-end-multiline-output "" } */
}
template <typename T>
class logger_2
{
public:
void log (const char *fmt, ...) __attribute__((format (printf, 2, 3)));
};
void test_2 ()
{
logger_2<int> out;
out.log ("before %s after", 42); // { dg-warning "argument 3 has type 'int'" }
/* { dg-begin-multiline-output "" }
out.log ("before %s after", 42);
~^ ~~
| |
char* int
%d
{ dg-end-multiline-output "" } */
}
// { dg-options "-Wformat -fdiagnostics-show-caret" }
void test_1 (void)
{
__builtin_printf ("%s%s", 42, 43); // { dg-warning "argument 2 has type 'int'" }
// { dg-warning "argument 3 has type 'int'" "" { target *-*-* } .-1 }
/* { dg-begin-multiline-output "" }
__builtin_printf ("%s%s", 42, 43);
~^ ~~
| |
char* int
%d
{ dg-end-multiline-output "" } */
/* { dg-begin-multiline-output "" }
__builtin_printf ("%s%s", 42, 43);
~^ ~~
| |
char* int
%d
{ dg-end-multiline-output "" } */
}
void test_2 (void)
{
__builtin_printf ("before %s after", 6 * 7); // { dg-warning "argument 2 has type 'int'" }
/* { dg-begin-multiline-output "" }
__builtin_printf ("before %s after", 6 * 7);
~^ ~~~~~
| |
char* int
%d
{ dg-end-multiline-output "" } */
}
// { dg-do compile { target c++11 } }
/* { dg-options "-Wformat -fdiagnostics-show-caret" } */
/* C++11-specific format tests. */
#define printf __builtin_printf
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\\*' " } */
/* { dg-begin-multiline-output "" }
printf(u8"hello %i", msg);
~^ ~~~
| |
int const char*
%s
{ dg-end-multiline-output "" } */
}
/* { dg-options "-Wformat -fdiagnostics-show-caret" } */
/* This is a copy of gcc.dg/format/diagnostic-ranges.c
with the following changes:
- removal of "format.h"
- "char \\*" -> "char\\*" (space removal)
- move of test_u8 to Wformat-ranges-c++11.C. */
#define printf __builtin_printf
typedef __SIZE_TYPE__ size_t;
typedef __SIZE_TYPE__ ssize_t;
extern ssize_t strfmon (char *__restrict __s, size_t __maxsize,
const char *__restrict, ...)
__attribute__ ((__format__ (__strfmon__, 3, 4)));
/* See PR 52952. */
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\\*' " } */
/* { dg-begin-multiline-output "" }
printf("hello %i", msg);
~^ ~~~
| |
int const char*
%s
{ dg-end-multiline-output "" } */
printf("hello %s", 42); /* { dg-warning "format '%s' expects argument of type 'char\\*', but argument 2 has type 'int'" } */
/* { dg-begin-multiline-output "" }
printf("hello %s", 42);
~^ ~~
| |
| int
char*
%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' " } */
/* { dg-begin-multiline-output "" }
printf("hello %i", (long)0);
~^ ~~~~~~~
| |
int long int
%li
{ 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);
/* { dg-begin-multiline-output "" }
printf ("arg0: %i arg1: %s arg 2: %i",
~^
|
char*
%d
100, 101, 102);
~~~
|
int
{ 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",
~^
|
char*
%d
100, i + j, 102);
~~~~~
|
int
{ dg-end-multiline-output "" } */
}
void multiline_format_string (void) {
printf ("before the fmt specifier"
"%"
"d" /* { dg-warning "12: format '%d' expects a matching 'int' argument" } */
"after the fmt specifier");
/* { dg-begin-multiline-output "" }
"%"
~~
"d"
~^
|
int
{ 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\\*' " } */
/* { dg-begin-multiline-output "" }
printf("hello \x25\x69", msg);
~~~~^~~~ ~~~
| |
int const char*
\x25s
{ 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\\*' " } */
/* { dg-begin-multiline-output "" }
printf("hello \045\151", msg);
~~~~^~~~ ~~~
| |
int const char*
\045s
{ 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",
~~~~~~~~^~~~
|
int
\x25" "s
msg);
~~~
|
const char*
{ 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);
~^ ~~~~~~~~~~~~~~~
| |
char* long int
%ld
{ dg-end-multiline-output "" } */
}
void test_field_width_specifier (long l, int i1, int i2)
{
printf (" %*.*d ", l, i1, i2); /* { dg-warning "14: field width specifier '\\*' expects argument of type 'int', but argument 2 has type 'long int'" } */
/* { dg-begin-multiline-output "" }
printf (" %*.*d ", l, i1, i2);
~^~~~ ~
| |
int long int
{ dg-end-multiline-output "" } */
}
/* PR c/72857. */
void test_field_width_specifier_2 (char *d, long foo, long bar)
{
__builtin_sprintf (d, " %*ld ", foo, foo); /* { dg-warning "28: field width specifier '\\*' expects argument of type 'int', but argument 3 has type 'long int'" } */
/* { dg-begin-multiline-output "" }
__builtin_sprintf (d, " %*ld ", foo, foo);
~^~~ ~~~
| |
int long int
{ dg-end-multiline-output "" } */
__builtin_sprintf (d, " %*ld ", foo + bar, foo); /* { dg-warning "28: field width specifier '\\*' expects argument of type 'int', but argument 3 has type 'long int'" } */
/* { dg-begin-multiline-output "" }
__builtin_sprintf (d, " %*ld ", foo + bar, foo);
~^~~ ~~~~~~~~~
| |
int long int
{ dg-end-multiline-output "" } */
}
void test_field_precision_specifier (char *d, long foo, long bar)
{
__builtin_sprintf (d, " %.*ld ", foo, foo); /* { dg-warning "29: field precision specifier '\\.\\*' expects argument of type 'int', but argument 3 has type 'long int'" } */
/* { dg-begin-multiline-output "" }
__builtin_sprintf (d, " %.*ld ", foo, foo);
~~^~~ ~~~
| |
int long int
{ dg-end-multiline-output "" } */
__builtin_sprintf (d, " %.*ld ", foo + bar, foo); /* { dg-warning "29: field precision specifier '\\.\\*' expects argument of type 'int', but argument 3 has type 'long int'" } */
/* { dg-begin-multiline-output "" }
__builtin_sprintf (d, " %.*ld ", foo + bar, foo);
~~^~~ ~~~~~~~~~
| |
int long int
{ 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 "13: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);
^~~~~~~~~~~~~~~~~~~~~~~~~ ~~~
|
const char*
{ dg-end-multiline-output "" } */
/* { dg-begin-multiline-output "" }
#define INT_FMT "%i"
~^
|
int
%s
{ dg-end-multiline-output "" } */
#undef INT_FMT
}
void test_macro_2 (const char *msg)
{
#define PRIu32 "u" /* { dg-message "17: format string is defined here" } */
printf("hello %" PRIu32 " world", msg); /* { dg-warning "10: format '%u' expects argument of type 'unsigned int', but argument 2 has type 'const char\\*' " } */
/* { dg-begin-multiline-output "" }
printf("hello %" PRIu32 " world", msg);
^~~~~~~~~~~~~~~~~~~~~~~~~ ~~~
|
const char*
{ dg-end-multiline-output "" } */
/* { dg-begin-multiline-output "" }
#define PRIu32 "u"
^
|
unsigned int
{ dg-end-multiline-output "" } */
#undef PRIu32
}
void test_macro_3 (const char *msg)
{
#define FMT_STRING "hello %i world" // { dg-line test_macro_3_macro_line }
/* { dg-warning "20: format '%i' expects argument of type 'int', but argument 2 has type 'const char\\*'" "" { target *-*-* } .-1 } */
printf(FMT_STRING, msg); /* { dg-message "10: in expansion of macro 'FMT_STRING" } */
/* { dg-begin-multiline-output "" }
#define FMT_STRING "hello %i world"
^~~~~~~~~~~~~~~~
{ dg-end-multiline-output "" } */
/* { dg-begin-multiline-output "" }
printf(FMT_STRING, msg);
^~~~~~~~~~
{ dg-end-multiline-output "" } */
/* { dg-message "28: format string is defined here" "" { target *-*-* } test_macro_3_macro_line } */
/* { dg-begin-multiline-output "" }
#define FMT_STRING "hello %i world"
~^
|
int
%s
{ dg-end-multiline-output "" } */
#undef FMT_STRING
}
void test_macro_4 (const char *msg)
{
#define FMT_STRING "hello %i world" /* { dg-warning "20: format '%i' expects argument of type 'int', but argument 2 has type 'const char\\*' " } */
printf(FMT_STRING "\n", msg); /* { dg-message "10: in expansion of macro 'FMT_STRING" } */
/* { dg-begin-multiline-output "" }
#define FMT_STRING "hello %i world"
^
{ dg-end-multiline-output "" } */
/* { dg-begin-multiline-output "" }
printf(FMT_STRING "\n", msg);
^~~~~~~~~~
{ dg-end-multiline-output "" } */
/* { dg-begin-multiline-output "" }
#define FMT_STRING "hello %i world"
~^
|
int
%s
{ dg-end-multiline-output "" } */
#undef FMT_STRING
}
void test_non_contiguous_strings (void)
{
__builtin_printf(" %" "d ", 0.5); /* { dg-warning "26: format .%d. expects argument of type .int., but argument 2 has type .double." } */
/* { dg-begin-multiline-output "" }
__builtin_printf(" %" "d ", 0.5);
~~~~^ ~~~
| |
int double
%" "f
{ 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);
^ ~~~
|
double
{ dg-end-multiline-output "" } */
}
...@@ -314,7 +314,8 @@ void test_macro_2 (const char *msg) ...@@ -314,7 +314,8 @@ void test_macro_2 (const char *msg)
void test_macro_3 (const char *msg) void test_macro_3 (const char *msg)
{ {
#define FMT_STRING "hello %i world" /* { dg-warning "20: format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */ #define FMT_STRING "hello %i world" /* { dg-line test_macro_3_macro_line } */
/* { dg-warning "20: format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*'" "" { target *-*-*} .-1 } */
printf(FMT_STRING, msg); /* { dg-message "10: in expansion of macro 'FMT_STRING" } */ printf(FMT_STRING, msg); /* { dg-message "10: in expansion of macro 'FMT_STRING" } */
/* { dg-begin-multiline-output "" } /* { dg-begin-multiline-output "" }
#define FMT_STRING "hello %i world" #define FMT_STRING "hello %i world"
...@@ -324,6 +325,14 @@ void test_macro_3 (const char *msg) ...@@ -324,6 +325,14 @@ void test_macro_3 (const char *msg)
printf(FMT_STRING, msg); printf(FMT_STRING, msg);
^~~~~~~~~~ ^~~~~~~~~~
{ dg-end-multiline-output "" } */ { dg-end-multiline-output "" } */
/* { dg-message "28: format string is defined here" "" { target *-*-* } test_macro_3_macro_line } */
/* { dg-begin-multiline-output "" }
#define FMT_STRING "hello %i world"
~^
|
int
%s
{ dg-end-multiline-output "" } */
#undef FMT_STRING #undef FMT_STRING
} }
......
...@@ -251,7 +251,11 @@ test_multitoken_macro (void) ...@@ -251,7 +251,11 @@ test_multitoken_macro (void)
/* { dg-begin-multiline-output "" } /* { dg-begin-multiline-output "" }
#define RANGE ("0123456789") #define RANGE ("0123456789")
^~~~~~~~~~~~~~ ^~~~~~~~~~~~~~
{ dg-end-multiline-output "" } */ { dg-end-multiline-output "" { target c } } */
/* { dg-begin-multiline-output "" }
#define RANGE ("0123456789")
~^~~~~~~~~~~~~
{ dg-end-multiline-output "" { target c++ } } */
/* { dg-begin-multiline-output "" } /* { dg-begin-multiline-output "" }
__emit_string_literal_range (RANGE, 4, 3, 6); __emit_string_literal_range (RANGE, 4, 3, 6);
^~~~~ ^~~~~
......
...@@ -12,7 +12,7 @@ test_stringified_token_1 (int x) ...@@ -12,7 +12,7 @@ test_stringified_token_1 (int x)
{ {
#define STRINGIFY(EXPR) #EXPR #define STRINGIFY(EXPR) #EXPR
__emit_string_literal_range (STRINGIFY(x > 0), /* { dg-error "unable to read substring location: macro expansion" } */ __emit_string_literal_range (STRINGIFY(x > 0), /* { dg-error "macro expansion|cpp_interpret_string_1 failed" } */
0, 0, 4); 0, 0, 4);
#undef STRINGIFY #undef STRINGIFY
...@@ -43,7 +43,7 @@ test_stringified_token_3 (int x) ...@@ -43,7 +43,7 @@ test_stringified_token_3 (int x)
#define XSTR(s) STR(s) #define XSTR(s) STR(s)
#define STR(s) #s #define STR(s) #s
#define FOO 123456789 #define FOO 123456789
__emit_string_literal_range (XSTR (FOO), /* { dg-error "unable to read substring location: macro expansion" } */ __emit_string_literal_range (XSTR (FOO), /* { dg-error "macro expansion|cpp_interpret_string_1 failed" } */
2, 2, 3); 2, 2, 3);
#undef XSTR #undef XSTR
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment