From 2926c3881e6b917dc3b35635d5028c1265c9250f Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 22 Aug 2017 21:47:51 -0400
Subject: [PATCH 4/6] C++: implement substring locations within -Wformat

With the vec<location_t> of arguments, it's now possible for
-Wformat to get at the format string's location from the C++ frontend.

This patch implements that, and wires up LANG_HOOKS_GET_SUBSTRING_LOCATION
for C++ to the implementation in c-common.c; with that, the C++
frontend's -Wformat correctly underlines the pertinent part of
format strings (and adds fix-it hints), going from e.g:

    logger->log ("%i %s %i", 100, 101, 102);
                                  ~~~     ^

to:

    logger->log ("%i %s %i", 100, 101, 102);
                     ~^           ~~~
                     %d

The patch also moves gcc.dg/format/diagnostic-ranges.c
to c-c++-common (renaming it to Wformat-diagnostic-ranges.c),
so that it can be run for both C and C++.

gcc/c-family/ChangeLog:
	* c-format.c (check_format_arg): Use "argslocs" to get
	fmt_param_loc, in preference to format_tree's location or
	input_location.

gcc/cp/ChangeLog:
	* cp-lang.c (LANG_HOOKS_GET_SUBSTRING_LOCATION): Wire this up to
	c_get_substring_location.

gcc/testsuite/ChangeLog:
	PR 52952
	* gcc.dg/format/diagnostic-ranges.c: Rename and move to...
	* c-c++-common/Wformat-diagnostic-ranges.c: ...here.
	* g++.dg/Wformat-on-method.C: Fix expected results to reflect that
	the correct parts of the format string are underlined, and fix-it
	hints are provided.
---
 gcc/c-family/c-format.c                            |   7 +-
 gcc/cp/cp-lang.c                                   |   3 +
 .../c-c++-common/Wformat-diagnostic-ranges.c       | 328 +++++++++++++++++++++
 gcc/testsuite/g++.dg/Wformat-on-method.C           |   6 +-
 gcc/testsuite/gcc.dg/format/diagnostic-ranges.c    | 328 ---------------------
 5 files changed, 341 insertions(+), 331 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/Wformat-diagnostic-ranges.c
 delete 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 d588dc1..b5c9421 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -1535,7 +1535,12 @@ 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);
+  gcc_assert (arg_num > 0);
+  int fmt_arg_idx = arg_num - 1;
+
+  location_t fmt_param_loc = arglocs.get_param_location (fmt_arg_idx);
+  if (fmt_param_loc == UNKNOWN_LOCATION)
+    fmt_param_loc = EXPR_LOC_OR_LOC (format_tree, input_location);
 
   if (VAR_P (format_tree))
     {
diff --git a/gcc/cp/cp-lang.c b/gcc/cp/cp-lang.c
index 805319a..5302daf 100644
--- a/gcc/cp/cp-lang.c
+++ b/gcc/cp/cp-lang.c
@@ -84,6 +84,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;
 
diff --git a/gcc/testsuite/c-c++-common/Wformat-diagnostic-ranges.c b/gcc/testsuite/c-c++-common/Wformat-diagnostic-ranges.c
new file mode 100644
index 0000000..80dbef9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wformat-diagnostic-ranges.c
@@ -0,0 +1,328 @@
+/* { dg-options "-Wformat -fdiagnostics-show-caret" } */
+
+/* See PR 52952. */
+
+#include "../gcc.dg/format/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 \\*' " } */
+
+/* { 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'" } */
+/* { 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' " } */
+/* { dg-begin-multiline-output "" }
+   printf("hello %i", (long)0);
+                 ~^   ~~~~~~~
+                 %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",
+                            ~^
+                            %d
+           100, 101, 102);
+                ~~~           
+   { 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",
+                            ~^
+                            %d
+           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 \\*' " } */
+
+/* { dg-begin-multiline-output "" }
+   printf("hello \x25\x69", msg);
+                 ~~~~^~~~   ~~~
+                 \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);
+                 ~~~~^~~~   ~~~
+                 \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",
+          ^~~~~~~~
+          msg);
+          ~~~
+  { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+   printf("prefix"  "\x25"  "\151"  "suffix",
+                     ~~~~~~~~^~~~
+                     \x25"  "s
+  { 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 \\*' " } */
+/* { dg-begin-multiline-output "" }
+   printf(u8"hello %i", msg);
+                   ~^   ~~~
+                   %s
+   { 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);
+                ~^       ~~~~~~~~~~~~~~~
+                %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);
+             ~^~~~    ~
+   { 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);
+                           ~^~~    ~~~
+   { 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);
+                           ~^~~    ~~~~~~~~~
+   { 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);
+                           ~~^~~    ~~~
+   { 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);
+                           ~~^~~    ~~~~~~~~~
+   { 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);
+          ^~~~~~~~                   ~~~
+   { dg-end-multiline-output "" } */
+/* { dg-begin-multiline-output "" }
+ #define INT_FMT "%i"
+                  ~^
+                  %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);
+          ^~~~~~~~~                  ~~~
+   { dg-end-multiline-output "" } */
+/* { dg-begin-multiline-output "" }
+ #define PRIu32 "u"
+                 ^
+   { dg-end-multiline-output "" } */
+#undef PRIu32
+}
+
+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 \\*' " } */
+  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 "" } */
+#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"
+                           ~^
+                           %s
+   { dg-end-multiline-output "" } */
+#undef FMT_STRING
+}
+
+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 *-*-* } .-1 } */
+  /* { dg-begin-multiline-output "" }
+   __builtin_printf(" %" "d ", 0.5);
+                    ^~~~       ~~~
+   { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   __builtin_printf(" %" "d ", 0.5);
+                      ~~~~^
+                      %" "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);
+                    ^  ~~~
+   { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/Wformat-on-method.C b/gcc/testsuite/g++.dg/Wformat-on-method.C
index 4ff22fb..aa9b9b1 100644
--- a/gcc/testsuite/g++.dg/Wformat-on-method.C
+++ b/gcc/testsuite/g++.dg/Wformat-on-method.C
@@ -15,7 +15,8 @@ void test (logger *logger)
   logger->log ("%i %s %i", 100, 101, 102); /* { dg-warning "format '%s' expects argument of type 'char\\*', but argument 4 has type 'int'" } */
 /* { dg-begin-multiline-output "" }
    logger->log ("%i %s %i", 100, 101, 102);
-                                 ~~~     ^
+                    ~^           ~~~
+                    %d
    { dg-end-multiline-output "" } */
 }
 
@@ -26,6 +27,7 @@ void logger::test ()
   log ("%i %s %i", 100, 101, 102); /* { dg-warning "format '%s' expects argument of type 'char\\*', but argument 4 has type 'int'" } */
 /* { dg-begin-multiline-output "" }
    log ("%i %s %i", 100, 101, 102);
-                         ~~~     ^
+            ~^           ~~~
+            %d
    { dg-end-multiline-output "" } */
 }
diff --git a/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c b/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c
deleted file mode 100644
index e56e159..0000000
--- a/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c
+++ /dev/null
@@ -1,328 +0,0 @@
-/* { 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 \\*' " } */
-
-/* { 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'" } */
-/* { 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' " } */
-/* { dg-begin-multiline-output "" }
-   printf("hello %i", (long)0);
-                 ~^   ~~~~~~~
-                 %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",
-                            ~^
-                            %d
-           100, 101, 102);
-                ~~~           
-   { 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",
-                            ~^
-                            %d
-           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 \\*' " } */
-
-/* { dg-begin-multiline-output "" }
-   printf("hello \x25\x69", msg);
-                 ~~~~^~~~   ~~~
-                 \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);
-                 ~~~~^~~~   ~~~
-                 \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",
-          ^~~~~~~~
-          msg);
-          ~~~
-  { dg-end-multiline-output "" } */
-
-/* { dg-begin-multiline-output "" }
-   printf("prefix"  "\x25"  "\151"  "suffix",
-                     ~~~~~~~~^~~~
-                     \x25"  "s
-  { 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 \\*' " } */
-/* { dg-begin-multiline-output "" }
-   printf(u8"hello %i", msg);
-                   ~^   ~~~
-                   %s
-   { 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);
-                ~^       ~~~~~~~~~~~~~~~
-                %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);
-             ~^~~~    ~
-   { 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);
-                           ~^~~    ~~~
-   { 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);
-                           ~^~~    ~~~~~~~~~
-   { 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);
-                           ~~^~~    ~~~
-   { 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);
-                           ~~^~~    ~~~~~~~~~
-   { 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);
-          ^~~~~~~~                   ~~~
-   { dg-end-multiline-output "" } */
-/* { dg-begin-multiline-output "" }
- #define INT_FMT "%i"
-                  ~^
-                  %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);
-          ^~~~~~~~~                  ~~~
-   { dg-end-multiline-output "" } */
-/* { dg-begin-multiline-output "" }
- #define PRIu32 "u"
-                 ^
-   { dg-end-multiline-output "" } */
-#undef PRIu32
-}
-
-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 \\*' " } */
-  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 "" } */
-#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"
-                           ~^
-                           %s
-   { dg-end-multiline-output "" } */
-#undef FMT_STRING
-}
-
-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 *-*-* } .-1 } */
-  /* { dg-begin-multiline-output "" }
-   __builtin_printf(" %" "d ", 0.5);
-                    ^~~~       ~~~
-   { dg-end-multiline-output "" } */
-  /* { dg-begin-multiline-output "" }
-   __builtin_printf(" %" "d ", 0.5);
-                      ~~~~^
-                      %" "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);
-                    ^  ~~~
-   { dg-end-multiline-output "" } */
-}
-- 
1.8.5.3

