builtin-attrs.def (gcc_diag, [...]): New format attributes.
authorKaveh R. Ghazi <ghazi@caip.rutgers.edu>
Mon, 30 Jun 2003 00:31:21 +0000 (00:31 +0000)
committerKaveh Ghazi <ghazi@gcc.gnu.org>
Mon, 30 Jun 2003 00:31:21 +0000 (00:31 +0000)
gcc:
* builtin-attrs.def (gcc_diag, gcc_cdiag, gcc_cxxdiag): New
format attributes.
* c-format.c (enum format_type): Add gcc_diag_format_type,
gcc_cdiag_format_type, and gcc_cxxdiag_format_type.
(gcc_diag_length_specs, gcc_cdiag_length_specs,
gcc_cxxdiag_length_specs, gcc_diag_flag_pairs,
gcc_cdiag_flag_pairs, gcc_cxxdiag_flag_pairs, gcc_diag_flag_specs,
gcc_cdiag_flag_specs, gcc_cxxdiag_flag_specs, gcc_diag_char_table,
gcc_cdiag_char_table, gcc_cxxdiag_char_table): New.
(format_types_orig): Add new data.
(find_char_info_specifier_index, init_dynamic_diag_info): New
functions.
(handle_format_attribute): Update to handle new format attributes.

testsuite:
* gcc.dg/format/gcc_diag-1.c: New test.

From-SVN: r68689

gcc/ChangeLog
gcc/builtin-attrs.def
gcc/c-format.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/format/gcc_diag-1.c [new file with mode: 0644]

index 82c6016..77013cc 100644 (file)
@@ -1,3 +1,19 @@
+2003-06-29  Kaveh R. Ghazi  <ghazi@caip.rutgers.edu>
+
+       * builtin-attrs.def (gcc_diag, gcc_cdiag, gcc_cxxdiag): New
+       format attributes.
+       * c-format.c (enum format_type): Add gcc_diag_format_type,
+       gcc_cdiag_format_type, and gcc_cxxdiag_format_type.
+       (gcc_diag_length_specs, gcc_cdiag_length_specs,
+       gcc_cxxdiag_length_specs, gcc_diag_flag_pairs,
+       gcc_cdiag_flag_pairs, gcc_cxxdiag_flag_pairs, gcc_diag_flag_specs,
+       gcc_cdiag_flag_specs, gcc_cxxdiag_flag_specs, gcc_diag_char_table,
+       gcc_cdiag_char_table, gcc_cxxdiag_char_table): New.
+       (format_types_orig): Add new data.
+       (find_char_info_specifier_index, init_dynamic_diag_info): New
+       functions.
+       (handle_format_attribute): Update to handle new format attributes.
+
 2003-06-29  Aaron W. LaFramboise  <awlaframboise@aol.com>
 
        * config/i386/mingw32.h (__GTHREAD_HIDE_WIN32API): Define to 1.
index df0bc08..7156fce 100644 (file)
@@ -85,6 +85,9 @@ DEF_ATTR_IDENT (ATTR_NORETURN, "noreturn")
 DEF_ATTR_IDENT (ATTR_NOTHROW, "nothrow")
 DEF_ATTR_IDENT (ATTR_PRINTF, "printf")
 DEF_ATTR_IDENT (ATTR_ASM_FPRINTF, "asm_fprintf")
+DEF_ATTR_IDENT (ATTR_GCC_DIAG, "gcc_diag")
+DEF_ATTR_IDENT (ATTR_GCC_CDIAG, "gcc_cdiag")
+DEF_ATTR_IDENT (ATTR_GCC_CXXDIAG, "gcc_cxxdiag")
 DEF_ATTR_IDENT (ATTR_PURE, "pure")
 DEF_ATTR_IDENT (ATTR_SCANF, "scanf")
 DEF_ATTR_IDENT (ATTR_STRFMON, "strfmon")
index dc17f0e..b971d32 100644 (file)
@@ -56,6 +56,8 @@ set_Wformat (int setting)
 /* This must be in the same order as format_types, with format_type_error
    last.  */
 enum format_type { printf_format_type, asm_fprintf_format_type,
+                  gcc_diag_format_type, gcc_cdiag_format_type,
+                  gcc_cxxdiag_format_type,
                   scanf_format_type, strftime_format_type,
                   strfmon_format_type, format_type_error };
 
@@ -523,6 +525,18 @@ static const format_length_info asm_fprintf_length_specs[] =
   { NULL, 0, 0, NULL, 0, 0 }
 };
 
+/* Length specifiers valid for GCC diagnostics.  */
+static const format_length_info gcc_diag_length_specs[] =
+{
+  { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C89 },
+  { "w", FMT_LEN_none, STD_C89, NULL, 0, 0 },
+  { NULL, 0, 0, NULL, 0, 0 }
+};
+
+/* The custom diagnostics all accept the same length specifiers.  */
+#define gcc_cdiag_length_specs gcc_diag_length_specs
+#define gcc_cxxdiag_length_specs gcc_diag_length_specs
+
 /* This differs from printf_length_specs only in that "Z" is not accepted.  */
 static const format_length_info scanf_length_specs[] =
 {
@@ -591,6 +605,32 @@ static const format_flag_pair asm_fprintf_flag_pairs[] =
   { 0, 0, 0, 0 }
 };
 
+static const format_flag_pair gcc_diag_flag_pairs[] =
+{
+  { 0, 0, 0, 0 }
+};
+
+#define gcc_cdiag_flag_pairs gcc_diag_flag_pairs
+#define gcc_cxxdiag_flag_pairs gcc_diag_flag_pairs
+
+static const format_flag_spec gcc_diag_flag_specs[] =
+{
+  { 'p',  0, 0, N_("precision"),       N_("precision in printf format"),       STD_C89 },
+  { 'L',  0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
+  { 0, 0, 0, NULL, NULL, 0 }
+};
+
+#define gcc_cdiag_flag_specs gcc_diag_flag_specs
+
+static const format_flag_spec gcc_cxxdiag_flag_specs[] =
+{
+  { '+',  0, 0, N_("`+' flag"),        N_("the `+' printf flag"),              STD_C89 },
+  { '#',  0, 0, N_("`#' flag"),        N_("the `#' printf flag"),              STD_C89 },
+  { 'p',  0, 0, N_("precision"),       N_("precision in printf format"),       STD_C89 },
+  { 'L',  0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
+  { 0, 0, 0, NULL, NULL, 0 }
+};
+
 static const format_flag_spec scanf_flag_specs[] =
 {
   { '*',  0, 0, N_("assignment suppression"), N_("the assignment suppression scanf feature"), STD_C89 },
@@ -755,6 +795,72 @@ static const format_char_info asm_fprintf_char_table[] =
   { NULL,  0, 0, NOLENGTHS, NULL, NULL }
 };
 
+static const format_char_info gcc_diag_char_table[] =
+{
+  /* C89 conversion specifiers.  */
+  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",  ""   },
+  { "ox",  0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",  ""   },
+  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",  ""   },
+  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",   ""   },
+  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "p",  "cR" },
+  { "p",   1, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",   "c"  },
+
+  /* Custom conversion specifiers.  */
+
+  /* %H will require "location_t" at runtime.  */
+  { "H",   0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",   ""   },
+
+  { "m",   0, STD_C89, NOARGUMENTS, "",      ""   },
+  { NULL,  0, 0, NOLENGTHS, NULL, NULL }
+};
+
+static const format_char_info gcc_cdiag_char_table[] =
+{
+  /* C89 conversion specifiers.  */
+  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",  ""   },
+  { "ox",  0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",  ""   },
+  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",  ""   },
+  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",   ""   },
+  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "p",  "cR" },
+  { "p",   1, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",   "c"  },
+
+  /* Custom conversion specifiers.  */
+
+  /* %H will require "location_t" at runtime.  */
+  { "H",   0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",   ""   },
+
+  /* These will require a "tree" at runtime.  */
+  { "DFT", 0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",   ""   },
+
+  { "m",   0, STD_C89, NOARGUMENTS, "",      ""   },
+  { NULL,  0, 0, NOLENGTHS, NULL, NULL }
+};
+
+static const format_char_info gcc_cxxdiag_char_table[] =
+{
+  /* C89 conversion specifiers.  */
+  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",  ""   },
+  { "ox",  0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",  ""   },
+  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",  ""   },
+  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",   ""   },
+  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "p",  "cR" },
+  { "p",   1, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",   "c"  },
+
+  /* Custom conversion specifiers.  */
+
+  /* %H will require "location_t" at runtime.  */
+  { "H",   0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",   ""   },
+
+  /* These will require a "tree" at runtime.  */
+  { "ADEFTV",0,STD_C89,{ T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "+#",   ""   },
+
+  /* These accept either an `int' or an `enum tree_code' (which is handled as an `int'.)  */
+  { "CLOPQ",0,STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",   ""   },
+
+  { "m",   0, STD_C89, NOARGUMENTS, "",      ""   },
+  { NULL,  0, 0, NOLENGTHS, NULL, NULL }
+};
+
 static const format_char_info scan_char_table[] =
 {
   /* C89 conversion specifiers.  */
@@ -824,6 +930,24 @@ static const format_kind_info format_types_orig[] =
     'w', 0, 'p', 0, 'L',
     NULL, NULL
   },
+  { "gcc_diag",   gcc_diag_length_specs,  gcc_diag_char_table, "", NULL, 
+    gcc_diag_flag_specs, gcc_diag_flag_pairs,
+    FMT_FLAG_ARG_CONVERT,
+    0, 0, 'p', 0, 'L',
+    NULL, &integer_type_node
+  },
+  { "gcc_cdiag",   gcc_cdiag_length_specs,  gcc_cdiag_char_table, "", NULL, 
+    gcc_cdiag_flag_specs, gcc_cdiag_flag_pairs,
+    FMT_FLAG_ARG_CONVERT,
+    0, 0, 'p', 0, 'L',
+    NULL, &integer_type_node
+  },
+  { "gcc_cxxdiag",   gcc_cxxdiag_length_specs,  gcc_cxxdiag_char_table, "+#", NULL, 
+    gcc_cxxdiag_flag_specs, gcc_cxxdiag_flag_pairs,
+    FMT_FLAG_ARG_CONVERT,
+    0, 0, 'p', 0, 'L',
+    NULL, &integer_type_node
+  },
   { "scanf",    scanf_length_specs,   scan_char_table,  "*'I", NULL, 
     scanf_flag_specs, scanf_flag_pairs,
     FMT_FLAG_ARG_CONVERT|FMT_FLAG_SCANF_A_KLUDGE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_ZERO_WIDTH_BAD|FMT_FLAG_DOLLAR_GAP_POINTER_OK,
@@ -2330,6 +2454,26 @@ check_format_types (int *status, format_wanted_type *types)
     }
 }
 
+/* Given a format_char_info array FCI, and a character C, this function
+   returns the index into the conversion_specs where that specifier's
+   data is located.  If the character isn't found it aborts.  */
+static unsigned int
+find_char_info_specifier_index (const format_char_info *fci, int c)
+{
+  unsigned int i = 0;
+  
+  while (fci->format_chars)
+    {
+      if (strchr (fci->format_chars, c))
+       return i;
+      i++; fci++;
+    }
+  
+  /* We shouldn't be looking for a non-existent specifier.  */
+  abort ();
+  
+}
+
 /* Given a format_length_info array FLI, and a character C, this
    function returns the index into the conversion_specs where that
    modifier's data is located.  If the character isn't found it
@@ -2391,6 +2535,118 @@ init_dynamic_asm_fprintf_info (void)
     }
 }
 
+/* Determine the types of "tree" and "location_t" in the code being
+   compiled for use in GCC's diagnostic custom format attributes.  You
+   must have set dynamic_format_types before calling this function.  */
+static void
+init_dynamic_diag_info (void)
+{
+  static tree t, loc, hwi;
+      
+  if (!loc || !t || !hwi)
+    {
+      static format_char_info *diag_fci, *cdiag_fci, *cxxdiag_fci;
+      static format_length_info *diag_ls;
+      unsigned int i;
+
+      /* For the GCC-diagnostics custom format specifiers to work, one
+        must have declared `tree' and/or `location_t' prior to using
+        those attributes.  If we haven't seen these declarations then
+        you shouldn't use the specifiers requiring these types.
+        However we don't force a hard ICE because we may see only one
+        or the other type.  */
+      if ((loc = maybe_get_identifier ("location_t")))
+       loc = TREE_TYPE (identifier_global_value (loc));
+
+      /* We need to grab the underlying `union tree_node' so peek into
+        an extra type level.  */
+      if ((t = maybe_get_identifier ("tree")))
+       t = TREE_TYPE (TREE_TYPE (identifier_global_value (t)));
+    
+      /* Find the underlying type for HOST_WIDE_INT.  For the %w
+        length modifier to work, one must have issued: "typedef
+        HOST_WIDE_INT __gcc_host_wide_int__;" in one's source code
+        prior to using that modifier.  */
+      if ((hwi = maybe_get_identifier ("__gcc_host_wide_int__")))
+       hwi = DECL_ORIGINAL_TYPE (identifier_global_value (hwi));
+      
+      /* Assign the new data for use.  */
+
+      /* All the GCC diag formats use the same length specs.  */
+      if (! diag_ls)
+       dynamic_format_types[gcc_diag_format_type].length_char_specs =
+         dynamic_format_types[gcc_cdiag_format_type].length_char_specs =
+         dynamic_format_types[gcc_cxxdiag_format_type].length_char_specs =
+         diag_ls = xmemdup (gcc_diag_length_specs,
+                            sizeof (gcc_diag_length_specs),
+                            sizeof (gcc_diag_length_specs)); 
+      if (hwi)
+        {
+         /* HOST_WIDE_INT must be one of 'long' or 'long long'.  */
+         i = find_length_info_modifier_index (diag_ls, 'w');
+         if (hwi == long_integer_type_node)
+           diag_ls[i].index = FMT_LEN_l;
+         else if (hwi == long_long_integer_type_node)
+           diag_ls[i].index = FMT_LEN_ll;
+         else
+           abort ();
+       }
+
+      /* Handle the __gcc_diag__ format specifics.  */
+      if (! diag_fci)
+       dynamic_format_types[gcc_diag_format_type].conversion_specs =
+         diag_fci = xmemdup (gcc_diag_char_table,
+                             sizeof(gcc_diag_char_table),
+                             sizeof(gcc_diag_char_table));
+      if (loc)
+        {
+         i = find_char_info_specifier_index (diag_fci, 'H');
+         diag_fci[i].types[0].type = &loc;
+         diag_fci[i].pointer_count = 1;
+       }
+
+      /* Handle the __gcc_cdiag__ format specifics.  */
+      if (! cdiag_fci)
+       dynamic_format_types[gcc_cdiag_format_type].conversion_specs =
+         cdiag_fci = xmemdup (gcc_cdiag_char_table,
+                              sizeof(gcc_cdiag_char_table),
+                              sizeof(gcc_cdiag_char_table));
+      if (loc)
+        {
+         i = find_char_info_specifier_index (cdiag_fci, 'H');
+         cdiag_fci[i].types[0].type = &loc;
+         cdiag_fci[i].pointer_count = 1;
+       }
+      if (t)
+        {
+         /* All specifiers taking a tree share the same struct. */
+         i = find_char_info_specifier_index (cdiag_fci, 'D');
+         cdiag_fci[i].types[0].type = &t;
+         cdiag_fci[i].pointer_count = 1;
+       }
+
+      /* Handle the __gcc_cxxdiag__ format specifics.  */
+      if (! cxxdiag_fci)
+       dynamic_format_types[gcc_cxxdiag_format_type].conversion_specs =
+         cxxdiag_fci = xmemdup (gcc_cxxdiag_char_table,
+                                sizeof(gcc_cxxdiag_char_table),
+                                sizeof(gcc_cxxdiag_char_table));
+      if (loc)
+        {
+         i = find_char_info_specifier_index (cxxdiag_fci, 'H');
+         cxxdiag_fci[i].types[0].type = &loc;
+         cxxdiag_fci[i].pointer_count = 1;
+       }
+      if (t)
+        {
+         /* All specifiers taking a tree share the same struct. */
+         i = find_char_info_specifier_index (cxxdiag_fci, 'D');
+         cxxdiag_fci[i].types[0].type = &t;
+         cxxdiag_fci[i].pointer_count = 1;
+       }
+    }
+}
+
 /* Handle a "format" attribute; arguments as in
    struct attribute_spec.handler.  */
 tree
@@ -2440,9 +2696,12 @@ handle_format_attribute (tree *node, tree name ATTRIBUTE_UNUSED, tree args,
       return NULL_TREE;
     }
 
-  /* If this is format type __asm_fprintf__, we have to initialize
-     GCC's notion of HOST_WIDE_INT for checking %wd.  */
-  if (info.format_type == asm_fprintf_format_type)
+  /* If this is a custom GCC-internal format type, we have to
+     initialize certain bits a runtime.  */
+  if (info.format_type == asm_fprintf_format_type
+      || info.format_type == gcc_diag_format_type
+      || info.format_type == gcc_cdiag_format_type
+      || info.format_type == gcc_cxxdiag_format_type)
     {
       /* Our first time through, we have to make sure that our
          format_type data is allocated dynamically and is modifiable.  */
@@ -2451,7 +2710,18 @@ handle_format_attribute (tree *node, tree name ATTRIBUTE_UNUSED, tree args,
          xmemdup (format_types_orig, sizeof (format_types_orig),
                   sizeof (format_types_orig));
 
-      init_dynamic_asm_fprintf_info();
+      /* If this is format __asm_fprintf__, we have to initialize
+         GCC's notion of HOST_WIDE_INT for checking %wd.  */
+      if (info.format_type == asm_fprintf_format_type)
+       init_dynamic_asm_fprintf_info();
+      /* If this is one of the diagnostic attributes, then we have to
+         intialize `location_t' and `tree' at runtime.  */
+      else if (info.format_type == gcc_diag_format_type
+              || info.format_type == gcc_cdiag_format_type
+              || info.format_type == gcc_cxxdiag_format_type)
+       init_dynamic_diag_info();
+      else
+       abort();
     }
 
   return NULL_TREE;
index 5226824..26f239f 100644 (file)
@@ -1,3 +1,7 @@
+2003-06-29  Kaveh R. Ghazi  <ghazi@caip.rutgers.edu>
+
+       * gcc.dg/format/gcc_diag-1.c: New test.
+
 2003-06-28  Ulrich Weigand  <uweigand@de.ibm.com>
 
        * gcc.c-torture/execute/multi-ix.c: Fix off-by-one bugs.
diff --git a/gcc/testsuite/gcc.dg/format/gcc_diag-1.c b/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
new file mode 100644 (file)
index 0000000..7cf18c1
--- /dev/null
@@ -0,0 +1,185 @@
+/* Test for GCC diagnositc formats.  */
+/* Origin: Kaveh Ghazi <ghazi@caip.rutgers.edu> */
+/* { dg-do compile } */
+/* { dg-options "-Wformat" } */
+
+#include "format.h"
+
+#define ATTRIBUTE_DIAG(F) __attribute__ ((__format__ (F, 1, 2))) __attribute__ ((__nonnull__));
+
+/* Magic identifiers must be set before the attribute is used.  */
+
+typedef long long __gcc_host_wide_int__;
+
+typedef struct location_s
+{
+  const char *file;
+  int line;
+} location_t;
+
+union tree_node;
+typedef union tree_node *tree;
+
+extern int diag (const char *, ...) ATTRIBUTE_DIAG(__gcc_diag__);
+extern int cdiag (const char *, ...) ATTRIBUTE_DIAG(__gcc_cdiag__);
+extern int cxxdiag (const char *, ...) ATTRIBUTE_DIAG(__gcc_cxxdiag__);
+
+void
+foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
+     int *n, short int *hn, long int l, unsigned long int ul,
+     long int *ln, long double ld, wint_t lc, wchar_t *ls, llong ll,
+     ullong ull, unsigned int *un, const int *cn, signed char *ss,
+     unsigned char *us, const signed char *css, unsigned int u1,
+     unsigned int u2, location_t *loc, tree t1, union tree_node *t2,
+     tree *t3, tree t4[])
+{
+  /* Acceptable C90 specifiers, flags and modifiers.  */
+  diag ("%%");
+  cdiag ("%%");
+  cxxdiag ("%%");
+  diag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
+  cdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
+  cxxdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
+  diag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
+  cdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
+  cxxdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
+  diag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
+  cdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
+  cxxdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
+  diag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
+  cdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
+  cxxdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
+  diag ("%.*s", i, s);
+  cdiag ("%.*s", i, s);
+  cxxdiag ("%.*s", i, s);
+
+  /* Extensions provided in the diagnostic framework.  */
+  diag ("%m");
+  cdiag ("%m");
+  cxxdiag ("%m");
+  diag ("%H", loc);
+  cdiag ("%H", loc);
+  cxxdiag ("%H", loc);
+
+  cdiag ("%D%F%T", t1, t1, t1);
+  cdiag ("%D%D%D%D", t1, t2, *t3, t4[5]);
+  cxxdiag ("%A%D%E%F%T%V", t1, t1, t1, t1, t1, t1);
+  cxxdiag ("%D%D%D%D", t1, t2, *t3, t4[5]);
+  cxxdiag ("%#A%#D%#E%#F%#T%#V", t1, t1, t1, t1, t1, t1);
+  cxxdiag ("%+A%+D%+E%+F%+T%+V", t1, t1, t1, t1, t1, t1);
+  cxxdiag ("%+#A%+#D%+#E%+#F%+#T%+#V", t1, t1, t1, t1, t1, t1);
+  cxxdiag ("%C%L%O%P%Q", i, i, i, i, i);
+
+  /* Bad stuff with extensions.  */
+  diag ("%m", i); /* { dg-warning "format" "extra arg" } */
+  cdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
+  cxxdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
+  diag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
+  cdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
+  cxxdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
+  diag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
+  cdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
+  cxxdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
+  diag ("%H"); /* { dg-warning "format" "missing arg" } */
+  cdiag ("%H"); /* { dg-warning "format" "missing arg" } */
+  cxxdiag ("%H"); /* { dg-warning "format" "missing arg" } */
+  diag ("%H", i); /* { dg-warning "format" "wrong arg" } */
+  cdiag ("%H", i); /* { dg-warning "format" "wrong arg" } */
+  cxxdiag ("%H", i); /* { dg-warning "format" "wrong arg" } */
+  diag ("%H", p); /* { dg-warning "format" "wrong arg" } */
+  cdiag ("%H", p); /* { dg-warning "format" "wrong arg" } */
+  cxxdiag ("%H", p); /* { dg-warning "format" "wrong arg" } */
+  diag ("%#H", loc); /* { dg-warning "format" "bogus modifier" } */
+  cdiag ("%#H", loc); /* { dg-warning "format" "bogus modifier" } */
+  cxxdiag ("%#H", loc); /* { dg-warning "format" "bogus modifier" } */
+  diag ("%+H", loc); /* { dg-warning "format" "bogus modifier" } */
+  cdiag ("%+H", loc); /* { dg-warning "format" "bogus modifier" } */
+  cxxdiag ("%+H", loc); /* { dg-warning "format" "bogus modifier" } */
+  diag ("%D", t1); /* { dg-warning "format" "bogus tree" } */
+  cdiag ("%A", t1); /* { dg-warning "format" "bogus tree" } */
+  cdiag ("%#D", t1); /* { dg-warning "format" "bogus modifier" } */
+  cdiag ("%+D", t1); /* { dg-warning "format" "bogus modifier" } */
+  cxxdiag ("%C"); /* { dg-warning "format" "missing arg" } */
+  cxxdiag ("%C", l); /* { dg-warning "format" "wrong arg" } */
+  cxxdiag ("%C", i, i); /* { dg-warning "format" "extra arg" } */
+  cxxdiag ("%#C", i); /* { dg-warning "format" "bogus modifier" } */
+  cxxdiag ("%+C", i); /* { dg-warning "format" "bogus modifier" } */
+  cdiag ("%D"); /* { dg-warning "format" "missing arg" } */
+  cxxdiag ("%D"); /* { dg-warning "format" "missing arg" } */
+  cdiag ("%D", i); /* { dg-warning "format" "wrong arg" } */
+  cxxdiag ("%D", i); /* { dg-warning "format" "wrong arg" } */
+  cdiag ("%D", t1, t1); /* { dg-warning "format" "extra arg" } */
+  cxxdiag ("%D", t1, t1); /* { dg-warning "format" "extra arg" } */
+
+  /* Standard specifiers not accepted in the diagnostic framework.  */
+  diag ("%X\n", u); /* { dg-warning "format" "HEX" } */
+  diag ("%f\n", d); /* { dg-warning "format" "float" } */
+  diag ("%e\n", d); /* { dg-warning "format" "float" } */
+  diag ("%E\n", d); /* { dg-warning "format" "float" } */
+  diag ("%g\n", d); /* { dg-warning "format" "float" } */
+  diag ("%G\n", d); /* { dg-warning "format" "float" } */
+  diag ("%n\n", n); /* { dg-warning "format" "counter" } */
+  diag ("%hd\n", i); /* { dg-warning "format" "conversion" } */
+
+  /* Various tests of bad argument types.  */
+  diag ("%-d", i); /* { dg-warning "format" "bad flag" } */
+  cdiag ("%-d", i); /* { dg-warning "format" "bad flag" } */
+  cxxdiag ("%-d", i); /* { dg-warning "format" "bad flag" } */
+  diag ("% d", i); /* { dg-warning "format" "bad flag" } */
+  cdiag ("% d", i); /* { dg-warning "format" "bad flag" } */
+  cxxdiag ("% d", i); /* { dg-warning "format" "bad flag" } */
+  diag ("%#o", u); /* { dg-warning "format" "bad flag" } */
+  cdiag ("%#o", u); /* { dg-warning "format" "bad flag" } */
+  cxxdiag ("%#o", u); /* { dg-warning "format" "bad flag" } */
+  diag ("%0d", i); /* { dg-warning "format" "bad flag" } */
+  cdiag ("%0d", i); /* { dg-warning "format" "bad flag" } */
+  cxxdiag ("%0d", i); /* { dg-warning "format" "bad flag" } */
+  diag ("%08d", i); /* { dg-warning "format" "bad flag" } */
+  cdiag ("%08d", i); /* { dg-warning "format" "bad flag" } */
+  cxxdiag ("%08d", i); /* { dg-warning "format" "bad flag" } */
+  diag ("%+d\n", i); /* { dg-warning "format" "bad flag" } */
+  cdiag ("%+d\n", i); /* { dg-warning "format" "bad flag" } */
+  cxxdiag ("%+d\n", i); /* { dg-warning "format" "bad flag" } */
+  diag ("%3d\n", i); /* { dg-warning "format" "bad flag" } */
+  cdiag ("%3d\n", i); /* { dg-warning "format" "bad flag" } */
+  cxxdiag ("%3d\n", i); /* { dg-warning "format" "bad flag" } */
+  diag ("%-3d\n", i); /* { dg-warning "format" "bad flag" } */
+  cdiag ("%-3d\n", i); /* { dg-warning "format" "bad flag" } */
+  cxxdiag ("%-3d\n", i); /* { dg-warning "format" "bad flag" } */
+  diag ("%.7d\n", i); /* { dg-warning "format" "bad flag" } */
+  cdiag ("%.7d\n", i); /* { dg-warning "format" "bad flag" } */
+  cxxdiag ("%.7d\n", i); /* { dg-warning "format" "bad flag" } */
+  diag ("%+9.4d\n", i); /* { dg-warning "format" "bad flag" } */
+  cdiag ("%+9.4d\n", i); /* { dg-warning "format" "bad flag" } */
+  cxxdiag ("%+9.4d\n", i); /* { dg-warning "format" "bad flag" } */
+  diag ("%.3ld\n", l); /* { dg-warning "format" "bad flag" } */
+  cdiag ("%.3ld\n", l); /* { dg-warning "format" "bad flag" } */
+  cxxdiag ("%.3ld\n", l); /* { dg-warning "format" "bad flag" } */
+  diag ("%d %lu\n", i, ul);
+  diag ("%d", l); /* { dg-warning "format" "bad argument types" } */
+  diag ("%wd", l); /* { dg-warning "format" "bad argument types" } */
+  diag ("%d", ll); /* { dg-warning "format" "bad argument types" } */
+  diag ("%*s", i, s); /* { dg-warning "format" "bad * argument types" } */
+  diag ("%*.*s", i, i, s); /* { dg-warning "format" "bad * argument types" } */
+  diag ("%*d\n", i1, i); /* { dg-warning "format" "bad * argument types" } */
+  diag ("%.*d\n", i2, i); /* { dg-warning "format" "bad * argument types" } */
+  diag ("%*.*ld\n", i1, i2, l); /* { dg-warning "format" "bad * argument types" } */
+  diag ("%ld", i); /* { dg-warning "format" "bad argument types" } */
+  diag ("%s", n); /* { dg-warning "format" "bad argument types" } */
+
+  /* Wrong number of arguments.  */
+  diag ("%d%d", i); /* { dg-warning "arguments" "wrong number of args" } */
+  diag ("%d", i, i); /* { dg-warning "arguments" "wrong number of args" } */
+  /* Miscellaneous bogus constructions.  */
+  diag (""); /* { dg-warning "zero-length" "warning for empty format" } */
+  diag ("\0"); /* { dg-warning "embedded" "warning for embedded NUL" } */
+  diag ("%d\0", i); /* { dg-warning "embedded" "warning for embedded NUL" } */
+  diag ("%d\0%d", i, i); /* { dg-warning "embedded|too many" "warning for embedded NUL" } */
+  diag (NULL); /* { dg-warning "null" "null format string warning" } */
+  diag ("%"); /* { dg-warning "trailing" "trailing % warning" } */
+  diag ((const char *)L"foo"); /* { dg-warning "wide" "wide string" } */
+  diag ("%s", (char *)0); /* { dg-warning "null" "%s with NULL" } */
+
+  /* Make sure we still get warnings for regular printf.  */
+  printf ("%d\n", ll); /* { dg-warning "format" "bad argument types" } */
+}