Support string locations for C++ in -Wformat (PR c++/56856)
authorDavid Malcolm <dmalcolm@redhat.com>
Fri, 5 Oct 2018 19:02:17 +0000 (19:02 +0000)
committerDavid Malcolm <dmalcolm@gcc.gnu.org>
Fri, 5 Oct 2018 19:02:17 +0000 (19:02 +0000)
-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

17 files changed:
gcc/ChangeLog
gcc/cp/ChangeLog
gcc/cp/call.c
gcc/cp/cp-lang.c
gcc/cp/parser.c
gcc/input.c
gcc/input.h
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/ext/builtin4.C
gcc/testsuite/g++.dg/plugin/plugin.exp
gcc/testsuite/g++.dg/warn/Wformat-method.C [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Wformat-pr71863.C [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Wformat-ranges-c++11.C [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Wformat-ranges.C [new file with mode: 0644]
gcc/testsuite/gcc.dg/format/diagnostic-ranges.c
gcc/testsuite/gcc.dg/plugin/diagnostic-test-string-literals-1.c
gcc/testsuite/gcc.dg/plugin/diagnostic-test-string-literals-2.c

index c2ebbbcf436d32fa3029a1a91b2be2c4083681c9..53c9c89ce9a4f9c072afbde331f127798965dafb 100644 (file)
@@ -1,3 +1,13 @@
+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>
 
        * config/pdp11/pdp11.c (TARGET_CXX_GUARD_TYPE): Define.
index c76340cb23f2a0e8ff73cd1e823acf0a661a3674..97d7e8d83c33677b7792dd2369eca235e1708f11 100644 (file)
@@ -1,3 +1,14 @@
+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>
 
        * lang-specs.h: Use string contatenation, not line splicing.
index b2ca667c8b42ca6cf75101293a8293bb2ddc5e5f..747f837019dc550f58d4cf00c54007c6ce524db9 100644 (file)
@@ -8188,6 +8188,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
     {
       tree *fargs = (!nargs ? argarray
                            : (tree *) alloca (nargs * sizeof (tree)));
+      auto_vec<location_t> arglocs (nargs);
       for (j = 0; j < nargs; j++)
        {
          /* 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)
            fargs[j] = TREE_OPERAND (argarray[j], 0);
          else
            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),
-                                          nargs, fargs, NULL);
+                                          nargs, fargs, &arglocs);
     }
 
   if (DECL_INHERITED_CTOR (fn))
index 26a1e6d29cf6f2c97611ead62570d17c5965b838..a0b0102f798f26907f1682626fe6d69939053fe0 100644 (file)
@@ -83,6 +83,9 @@ static tree cxx_enum_underlying_base_type (const_tree);
 #define LANG_HOOKS_RUN_LANG_SELFTESTS selftest::run_cp_tests
 #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.  */
 struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
 
index a17cc3f23f2ece1dd51a4afd47291b9f680a0175..032108abfd30bc9bbb0baaefed74285147b16f5e 100644 (file)
@@ -4034,6 +4034,7 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
   tree value;
   size_t count;
   struct obstack str_ob;
+  struct obstack loc_ob;
   cpp_string str, istr, *strs;
   cp_token *tok;
   enum cpp_ttype type, curr_type;
@@ -4090,6 +4091,7 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
     {
       location_t last_tok_loc = tok->location;
       gcc_obstack_init (&str_ob);
+      gcc_obstack_init (&loc_ob);
       count = 0;
 
       do
@@ -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 (&loc_ob, &tok->location, sizeof (location_t));
 
          last_tok_loc = tok->location;
 
@@ -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);
       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)
        {
@@ -4209,7 +4218,10 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
     value = error_mark_node;
 
   if (count > 1)
-    obstack_free (&str_ob, 0);
+    {
+      obstack_free (&str_ob, 0);
+      obstack_free (&loc_ob, 0);
+    }
 
   return cp_expr (value, loc);
 }
index d65a82dc26eac4b087acb7fd9b8edff9060761f9..b4b7136701880c2d1f6d9e2d3be309e3171b4977 100644 (file)
@@ -813,10 +813,10 @@ expand_location (source_location loc)
    "<built-in>".  */
 
 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,
-                           LOCATION_ASPECT_CARET);
+  return expand_location_1 (loc, /*expansion_point_p=*/false, aspect);
 }
 
 /* The rich_location class within libcpp requires a way to expand
@@ -1397,24 +1397,32 @@ get_substring_ranges_for_loc (cpp_reader *pfile,
       source_range src_range = get_range_from_loc (line_table, strlocs[i]);
 
       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.  */
-       return "macro expansion";
-
-      if (src_range.m_start >= LINE_MAP_MAX_LOCATION_WITH_COLS)
-       /* If so, we can't reliably determine where the token started within
-          its line.  */
-       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";
+       {
+         /* If the string token was within a macro expansion, then we can
+            cope with it for the simple case where we have a single token.
+            Otherwise, bail out.  */
+         if (src_range.m_start != src_range.m_finish)
+           return "macro expansion";
+       }
+      else
+       {
+         if (src_range.m_start >= LINE_MAP_MAX_LOCATION_WITH_COLS)
+           /* If so, we can't reliably determine where the token started within
+              its line.  */
+           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
-       = expand_location_to_spelling_point (src_range.m_start);
+       = expand_location_to_spelling_point (src_range.m_start,
+                                            LOCATION_ASPECT_START);
       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)
        return "range endpoints are in different files";
       if (start.line != finish.line)
index 4619663519a46ebc93f0d1e407f8908104873534..e5d5a0932b8c8bb095f712699dc54e52e9952544 100644 (file)
@@ -85,7 +85,10 @@ class char_span
 extern char_span location_get_source_line (const char *file_path, int line);
 
 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 (source_location);
 
index 2f3ababae7861a824b330eaeede608c32235f0fe..37a035a9ed8e0d73a80475aca0aeca351c757aad 100644 (file)
@@ -1,3 +1,23 @@
+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
index 8804b53bd7a93a204b8abecf512c2e87246ae2a0..6691434c7c98f4d78c620c74912f85011b472ee8 100644 (file)
@@ -6,5 +6,5 @@
 extern "C" int printf(const char*,...);
 
 void foo() {
-  printf("%d");                // { dg-warning "expects a matching" }
+  printf("%d");                // { dg-warning "12: expects a matching" }
 }
index d9f54ab9877e86d307e3e6b72dc234eb50d656aa..e7c3b692625442ca4af9c07a0e4945f3ac1f342c 100644 (file)
@@ -67,6 +67,11 @@ set plugin_test_list [list \
          diagnostic-test-expressions-1.C } \
     { ../../gcc.dg/plugin/diagnostic_plugin_test_inlining.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.C \
          show-template-tree-color-labels.C \
diff --git a/gcc/testsuite/g++.dg/warn/Wformat-method.C b/gcc/testsuite/g++.dg/warn/Wformat-method.C
new file mode 100644 (file)
index 0000000..8494bf1
--- /dev/null
@@ -0,0 +1,40 @@
+// { 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 "" } */
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wformat-pr71863.C b/gcc/testsuite/g++.dg/warn/Wformat-pr71863.C
new file mode 100644 (file)
index 0000000..537274c
--- /dev/null
@@ -0,0 +1,33 @@
+// { 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 "" } */
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wformat-ranges-c++11.C b/gcc/testsuite/g++.dg/warn/Wformat-ranges-c++11.C
new file mode 100644 (file)
index 0000000..a4d3fff
--- /dev/null
@@ -0,0 +1,18 @@
+// { 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 "" } */
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wformat-ranges.C b/gcc/testsuite/g++.dg/warn/Wformat-ranges.C
new file mode 100644 (file)
index 0000000..96dfa97
--- /dev/null
@@ -0,0 +1,374 @@
+/* { 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 "" } */
+}
index 84535f018d7323fe565314aba14c0859ac7320af..2c33ce2c0d569b7d86cfd4ef9490250da4eef05c 100644 (file)
@@ -314,7 +314,8 @@ void test_macro_2 (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" } */
 /* { dg-begin-multiline-output "" }
  #define FMT_STRING "hello %i world"
@@ -324,6 +325,14 @@ void test_macro_3 (const char *msg)
    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
 }
 
index bea86afad7659805c6e705fb9264563d1ca997bd..73770aa1bee1b5fbff97eab500008f2f8117c910 100644 (file)
@@ -251,7 +251,11 @@ test_multitoken_macro (void)
 /* { dg-begin-multiline-output "" }
  #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 "" }
    __emit_string_literal_range (RANGE, 4, 3, 6);
                                 ^~~~~
index e9d98f4d6befc90b2b179a141c2a2eb535600aa9..260c47d34018eed0fe248f6ab21f748274bca225 100644 (file)
@@ -12,7 +12,7 @@ test_stringified_token_1 (int x)
 {
 #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);
 
 #undef STRINGIFY
@@ -43,7 +43,7 @@ test_stringified_token_3 (int x)
 #define XSTR(s) STR(s)
 #define STR(s) #s
 #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);
 
 #undef XSTR