public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* PATCH: Add format string suggestions to -Wformat warnings
@ 2007-08-14 23:51 Dan Hipschman
  2007-08-15  0:00 ` Andrew Pinski
  2007-08-15  0:39 ` Joseph S. Myers
  0 siblings, 2 replies; 8+ messages in thread
From: Dan Hipschman @ 2007-08-14 23:51 UTC (permalink / raw)
  To: gcc-patches


Hi.

This patch adds suggestions for format specifiers, based on type, to warnings
with the -Wformat flag.  For example:

$ cat foo.c
#include <stdio.h>
int main (void)
{
  printf ("%d", 0.0);
  printf ("%lu", 0u);
  printf ("%lu", sizeof 0);
  return 0;
}

$ gcc-svn -std=c99 -Wformat foo.c
foo.c: In function 'main':
foo.c:4: warning: format '%d' expects type 'int', but argument 2 has type 'double'
foo.c:4: warning: acceptable formats for this type are: a, A, e, E, f, F, g, G
foo.c:5: warning: format '%lu' expects type 'long unsigned int', but argument 2 has type 'unsigned int'
foo.c:5: warning: acceptable formats for this type are: o, u, x, X
foo.c:6: warning: format '%lu' expects type 'long unsigned int', but argument 2 has type 'unsigned int'
foo.c:6: warning: acceptable formats for this type are: zo, zu, zx, zX

It is sensitive to the standard:

$ gcc-svn -std=c89 -Wformat foo.c
foo.c: In function 'main':
foo.c:4: warning: format '%d' expects type 'int', but argument 2 has type 'double'
foo.c:4: warning: acceptable formats for this type are: e, E, f, g, G
foo.c:5: warning: format '%lu' expects type 'long unsigned int', but argument 2 has type 'unsigned int'
foo.c:5: warning: acceptable formats for this type are: o, u, x, X
foo.c:6: warning: format '%lu' expects type 'long unsigned int', but argument 2 has type 'unsigned int'
foo.c:6: warning: acceptable formats for this type are: o, u, x, X

See the included testcases for more complete examples.  It works with %n, and
checks the type for constness.  This only adds suggestions for printf, but it
shouldn't be hard to add suggestions for scanf and the others.  It works with
sizeof, pointer differences and wide string literals, but to do this I had to
make separate type nodes for size_type_node, ptrdiff_type_node and
wchar_type_node, since currently these type nodes are indistinguishable from
the primitive types they alias.  (For example, "sizeof x" is compiled into a
constant with the same type as size_type_node, which may be unsigned int, but
after that it's impossible to tell that that constant came from a sizeof.
TYPE_IS_SIZETYPE doesn't work: see the bottom of c_sizeof_or_alignof_type.)
Simply copying the type nodes seemed like the least-possibly harmful way to do
this, and I've bootstrapped and run "make -k check" on i686-linux-gnu with no
new problems.  I should note that there is one failure in the testcases
included with this patch, which is basically this:

#include <stdio.h>
#include <wchar.h>
int main (void)
{
  wint_t w = L'x';
  printf ("%c", w);
  return 0;
}

which produces no warnings, when I think is should.  This is a problem with
the code in svn, though, since suggestions are only made if a warning is issued
because the types aren't acceptable.  I can try to fix that in a separate
patch.

Dan


gcc/
2007-08-14  Dan Hipschman  <dsh@google.com>

	* c-format.h (format_suggestions): New structure.  New FMT_SUG_* flags.
	* c-format.c (format_wanted_type): Add suggestions field.
	(printf_suggestions): New global.
	(format_types_orig): Add suggestions field initializers.
	(format_type_warning): Add suggestions and arg parameters.  Remove
	arg_type parameter.  Use format suggestions in warnings.
	(check_format_info_main): Use suggestions.
	(check_format_types): Pass orig_cur_param instead of orig_cur_type to
	format_type_warning.
	* c-common.c (copy_type_with_name): New function.
	(c_common_nodes_and_builtins): Use it.

testsuite/
2007-08-14  Dan Hipschman  <dsh@google.com>

	* gcc.dg/format/ext-suggest-1.c: New test.
	* gcc.dg/format/ext-suggest-2.c: New test.
	* gcc.dg/format/dfp-suggest-1.c: New test.
	* gcc.dg/format/typedef-suggest-1.c: New test.
	* gcc.dg/format/xopen-1.c: Add an excess-errors check for suggestions.
	* gcc.dg/format/branch-1.c: Likewise.
	* gcc.dg/format/diag-1.c: Likewise.
	* gcc.dg/format/multattr-3.c: Likewise.
	* gcc.dg/format/dfp-printf-1.c: Likewise.
	* gcc.dg/format/unnamed-1.c: Likewise.

Index: gcc/testsuite/gcc.dg/format/ext-suggest-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/ext-suggest-1.c	(revision 0)
+++ gcc/testsuite/gcc.dg/format/ext-suggest-1.c	(revision 0)
@@ -0,0 +1,63 @@
+/* Copyright (C) 2007 Free Software Foundation.  */
+/* Contributed by Dan Hipschman <dsh@google.com>.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu99 -Wformat" } */
+
+#include "format.h"
+
+void
+foo (signed char hhd, unsigned char c, short hd, unsigned short hu, size_t zu,
+     ssize_t zd, ptrdiff_t td, intmax_t jd, uintmax_t ju, wchar_t *ls,
+     wint_t lc, void *p, float f, long double Lf, const char *s,
+     const void *p2)
+{
+  char a[2] = " ";
+  char *td1 = &a[1], *td2 = &a[0];
+  int n;
+  long ln;
+  long long lln;
+  printf ("%p", hhd); /* { dg-warning ": hhd, hhi" } */
+  printf ("%p", c); /* { dg-warning ": c, hho, hhu, hhx, hhX" } */
+  printf ("%p", a[0]); /* { dg-warning ": d, i" } */
+  printf ("%p", hd); /* { dg-warning ": hd, hi" } */
+  printf ("%p", hu); /* { dg-warning ": ho, hu, hx, hX" } */
+  printf ("%p", 0); /* { dg-warning ": d, i" } */
+  printf ("%p", 0u); /* { dg-warning ": o, u, x, X" } */
+  printf ("%p", 0L); /* { dg-warning ": ld, li" } */
+  printf ("%p", 0uL); /* { dg-warning ": lo, lu, lx, lX" } */
+  printf ("%p", 0LL); /* { dg-warning ": lld, lli" } */
+  printf ("%d", 0L); /* { dg-warning ": ld, li" } */
+  printf ("%u", 0uL); /* { dg-warning ": lo, lu, lx, lX" } */
+  printf ("%p", 0uLL); /* { dg-warning ": llo, llu, llx, llX" } */
+  printf ("%p", zu); /* { dg-warning ": zo, zu, zx, zX" } */
+  printf ("%p", sizeof 0); /* { dg-warning ": zo, zu, zx, zX" } */
+  printf ("%p", zd); /* { dg-warning ": zd, zi" } */
+  printf ("%p", td); /* { dg-warning ": td, ti" } */
+  printf ("%p", td1 - td2); /* { dg-warning ": td, ti" } */
+  printf ("%p", jd); /* { dg-warning ": jd, ji" } */
+  printf ("%p", ju); /* { dg-warning ": jo, ju, jx, jX" } */
+  printf ("%d", a); /* { dg-warning ": s" } */
+  printf ("%c", "foo"); /* { dg-warning ": s" } */
+  printf ("%d", s); /* { dg-warning ": s" } */
+  printf ("%c", ls); /* { dg-warning ": ls" } */
+  printf ("%s", L"foo"); /* { dg-warning ": ls" } */
+  printf ("%c", lc); /* { dg-warning ": lc" } */
+  printf ("%c", L'x'); /* { dg-warning ": lc" } */
+  printf ("%d", p); /* { dg-warning ": p" } */
+  printf ("%x", p2); /* { dg-warning ": p" } */
+  printf ("%d", f); /* { dg-warning ": a, A, e, E, f, F, g, G" } */
+  printf ("%d", 0.0); /* { dg-warning ": a, A, e, E, f, F, g, G" } */
+  printf ("%f", Lf); /* { dg-warning ": La, LA, Le, LE, Lf, LF, Lg, LG" } */
+  printf ("%f", &hhd); /* { dg-warning ": hhn" } */
+  printf ("%f", (const signed char *) &hhd);
+  printf ("%f", &hd); /* { dg-warning ": hn" } */
+  printf ("%f", (const short *) &hd);
+  printf ("%f", &n); /* { dg-warning ": n" } */
+  printf ("%f", (const int *) &n);
+  printf ("%f", &ln); /* { dg-warning ": ln" } */
+  printf ("%f", (const long *) &ln);
+  printf ("%f", &lln); /* { dg-warning ": lln" } */
+  printf ("%f", (const long long *) &lln);
+}
+
+/* { dg-excess-errors "expects .* but argument .* has" } */
Index: gcc/testsuite/gcc.dg/format/typedef-suggest-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/typedef-suggest-1.c	(revision 0)
+++ gcc/testsuite/gcc.dg/format/typedef-suggest-1.c	(revision 0)
@@ -0,0 +1,44 @@
+/* Copyright (C) 2007 Free Software Foundation.  */
+/* Contributed by Dan Hipschman <dsh@google.com>.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu99 -Wformat" } */
+
+extern void foo (const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+
+/* See if we can fool the compiler.  */
+typedef double size_t;
+typedef double ptrdiff_t;
+typedef double wchar_t;
+typedef double wint_t;
+typedef double ssize_t;
+typedef char *intmax_t;
+typedef void *uintmax_t;
+
+char a[2];
+
+void
+bar (size_t s1, ptrdiff_t p1, wchar_t c1, wchar_t *pc1, wint_t i1, ssize_t ss1,
+     intmax_t m1, uintmax_t m2)
+{
+  size_t s2 = 0.0;
+  foo ("%p", sizeof 0); /* { dg-warning ": zo, zu, zx, zX" } */
+  foo ("%p", s1); /* { dg-warning ": a, A, e, E, f, F, g, G" } */
+  foo ("%p", s2); /* { dg-warning ": a, A, e, E, f, F, g, G" } */
+  foo ("%zu", s2); /* { dg-warning ": a, A, e, E, f, F, g, G" } */
+  foo ("%p", &a[0] - &a[1]); /* { dg-warning ": td, ti" } */
+  foo ("%p", p1); /* { dg-warning ": a, A, e, E, f, F, g, G" } */
+  foo ("%ti", p1); /* { dg-warning ": a, A, e, E, f, F, g, G" } */
+  foo ("%s", L'x'); /* { dg-warning ": lc" } */
+  foo ("%s", c1); /* { dg-warning ": a, A, e, E, f, F, g, G" } */
+  foo ("%lc", c1); /* { dg-warning ": a, A, e, E, f, F, g, G" } */
+  foo ("%s", i1); /* { dg-warning ": a, A, e, E, f, F, g, G" } */
+  foo ("%lc", i1); /* { dg-warning ": a, A, e, E, f, F, g, G" } */
+  foo ("%s", L"x"); /* { dg-warning ": ls" } */
+  foo ("%s", pc1);
+  foo ("%s", ss1); /* { dg-warning ": a, A, e, E, f, F, g, G" } */
+  foo ("%zd", ss1); /* { dg-warning ": a, A, e, E, f, F, g, G" } */
+  foo ("%i", m1); /* { dg-warning ": s" } */
+  foo ("%ju", m2); /* { dg-warning ": p" } */
+}
+
+/* { dg-excess-errors "expects .* but argument .* has" } */
Index: gcc/testsuite/gcc.dg/format/xopen-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/xopen-1.c	(revision 127489)
+++ gcc/testsuite/gcc.dg/format/xopen-1.c	(working copy)
@@ -123,3 +123,5 @@ foo (int i, unsigned int u, wint_t lc, w
   printf ("%1$d%1$d", i);
   scanf ("%1$d%1$d", ip); /* { dg-warning "more than once" "multiple use of scanf argument" } */
 }
+
+/* { dg-excess-errors "acceptable formats for .* are" } */
Index: gcc/testsuite/gcc.dg/format/branch-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/branch-1.c	(revision 127489)
+++ gcc/testsuite/gcc.dg/format/branch-1.c	(working copy)
@@ -25,3 +25,5 @@ foo (long l, int nfoo)
   printf (NULL, "foo"); /* { dg-warning "too many" "NULL extra args" } */
   /* { dg-warning "null" "null format arg" { target *-*-* } 25 } */
 }
+
+/* { dg-excess-errors "acceptable formats for .* are" } */
Index: gcc/testsuite/gcc.dg/format/diag-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/diag-1.c	(revision 127489)
+++ gcc/testsuite/gcc.dg/format/diag-1.c	(working copy)
@@ -16,3 +16,5 @@ foo (double d)
      'unsigned int' or similar.  */
   printf ("%zu", d); /* { dg-warning "size_t" "size_t format warning" } */
 }
+
+/* { dg-excess-errors "acceptable formats for .* are" } */
Index: gcc/testsuite/gcc.dg/format/multattr-3.c
===================================================================
--- gcc/testsuite/gcc.dg/format/multattr-3.c	(revision 127489)
+++ gcc/testsuite/gcc.dg/format/multattr-3.c	(working copy)
@@ -26,3 +26,5 @@ foo (long l, int nfoo)
   printf (ngettext ("%d foo", (nfoo > 0) ? "%ld foos" : "no foos", nfoo), nfoo); /* { dg-warning "long int" "wrong type" } */
   printf (ngettext ("%d foo", (nfoo > 0) ? "%d foos" : "%ld foos", nfoo), nfoo); /* { dg-warning "long int" "wrong type" } */
 }
+
+/* { dg-excess-errors "acceptable formats for .* are" } */
Index: gcc/testsuite/gcc.dg/format/ext-suggest-2.c
===================================================================
--- gcc/testsuite/gcc.dg/format/ext-suggest-2.c	(revision 0)
+++ gcc/testsuite/gcc.dg/format/ext-suggest-2.c	(revision 0)
@@ -0,0 +1,60 @@
+/* Copyright (C) 2007 Free Software Foundation.  */
+/* Contributed by Dan Hipschman <dsh@google.com>.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu89 -Wformat" } */
+
+#include "format.h"
+
+void
+foo (signed char hhd, unsigned char c, short hd, unsigned short hu, size_t zu,
+     ssize_t zd, ptrdiff_t td, wchar_t *ls, wint_t lc, void *p, float f,
+     long double Lf, const char *s, const void *p2)
+{
+  char a[2] = " ";
+  char *td1 = &a[1], *td2 = &a[0];
+  int n;
+  long ln;
+  long long lln;
+  printf ("%p", hhd); /* { dg-warning ": d, i" } */
+  printf ("%p", c); /* { dg-warning ": c" } */
+  printf ("%p", a[0]); /* { dg-warning ": d, i" } */
+  printf ("%p", hd); /* { dg-warning ": hd, hi" } */
+  printf ("%p", hu); /* { dg-warning ": ho, hu, hx, hX" } */
+  printf ("%p", 0); /* { dg-warning ": d, i" } */
+  printf ("%p", 0u); /* { dg-warning ": o, u, x, X" } */
+  printf ("%p", 0L); /* { dg-warning ": ld, li" } */
+  printf ("%p", 0uL); /* { dg-warning ": lo, lu, lx, lX" } */
+  printf ("%p", 0LL);
+  printf ("%d", 0L); /* { dg-warning ": ld, li" } */
+  printf ("%u", 0uL); /* { dg-warning ": lo, lu, lx, lX" } */
+  printf ("%p", 0uLL);
+  printf ("%p", zu); /* { dg-warning ": l?o, l?u, l?x, l?X" } */
+  printf ("%p", sizeof 0); /* { dg-warning ": l?o, l?u, l?x, l?X" } */
+  printf ("%p", zd); /* { dg-warning ": l?d, l?i" } */
+  printf ("%p", td); /* { dg-warning ": l?d, l?i" } */
+  printf ("%p", td1 - td2); /* { dg-warning ": l?d, l?i" } */
+  printf ("%d", a); /* { dg-warning ": s" } */
+  printf ("%c", "foo"); /* { dg-warning ": s" } */
+  printf ("%d", s); /* { dg-warning ": s" } */
+  printf ("%c", ls);
+  printf ("%s", L"foo");
+  printf ("%c", lc); /* { dg-warning ": l?d, l?i" } */
+  printf ("%c", L'x'); /* { dg-warning ": l?d, l?i" } */
+  printf ("%d", p); /* { dg-warning ": p" } */
+  printf ("%x", p2); /* { dg-warning ": p" } */
+  printf ("%d", f); /* { dg-warning ": e, E, f, g, G" } */
+  printf ("%d", 0.0); /* { dg-warning ": e, E, f, g, G" } */
+  printf ("%f", Lf);
+  printf ("%f", &hhd);
+  printf ("%f", (const signed char *) &hhd);
+  printf ("%f", &hd); /* { dg-warning ": hn" } */
+  printf ("%f", (const short *) &hd);
+  printf ("%f", &n); /* { dg-warning ": n" } */
+  printf ("%f", (const int *) &n);
+  printf ("%f", &ln); /* { dg-warning ": ln" } */
+  printf ("%f", (const long *) &ln);
+  printf ("%f", &lln);
+  printf ("%f", (const long long *) &lln);
+}
+
+/* { dg-excess-errors "expects .* but argument .* has" } */
Index: gcc/testsuite/gcc.dg/format/dfp-printf-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/dfp-printf-1.c	(revision 127489)
+++ gcc/testsuite/gcc.dg/format/dfp-printf-1.c	(working copy)
@@ -120,3 +120,5 @@ foo (_Decimal32 x, _Decimal64 y, _Decima
   printf ("%0-#DDg\n", z); /* { dg-warning "flag ignored" "ignore flag" } */
   printf ("% DDG\n", z);
 }
+
+/* { dg-excess-errors "acceptable formats for .* are" } */
Index: gcc/testsuite/gcc.dg/format/unnamed-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/unnamed-1.c	(revision 127489)
+++ gcc/testsuite/gcc.dg/format/unnamed-1.c	(working copy)
@@ -22,3 +22,5 @@ f (TItype x)
   printf("%d", 141592653589793238462643383279502884197169399375105820974944); /* { dg-warning "expects type" } */
   /* { dg-warning "unsigned only|too large" "constant" { target *-*-* } 22 } */
 }
+
+/* { dg-excess-errors "acceptable formats for .* are" } */
Index: gcc/testsuite/gcc.dg/format/dfp-suggest-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/dfp-suggest-1.c	(revision 0)
+++ gcc/testsuite/gcc.dg/format/dfp-suggest-1.c	(revision 0)
@@ -0,0 +1,17 @@
+/* Copyright (C) 2007 Free Software Foundation.  */
+/* Contributed by Dan Hipschman <dsh@google.com>.  */
+/* { dg-do compile } */
+/* { dg-require-effective-target dfp } */
+/* { dg-options "-std=gnu89 -Wformat" } */
+
+#include "format.h"
+
+void
+foo (_Decimal32 Hf, _Decimal64 Df, _Decimal128 DDf)
+{
+  printf ("%f", Hf); /* { dg-warning ": He, HE, Hf, HF, Hg, HG" } */
+  printf ("%f", Df); /* { dg-warning ": De, DE, Df, DF, Dg, DG" } */
+  printf ("%f", DDf); /* { dg-warning ": DDe, DDE, DDf, DDF, DDg, DDG" } */
+}
+
+/* { dg-excess-errors "expects .* but argument .* has" } */
Index: gcc/c-format.c
===================================================================
--- gcc/c-format.c	(revision 127489)
+++ gcc/c-format.c	(working copy)
@@ -267,6 +267,8 @@ typedef struct format_wanted_type
   /* Whether the argument, dereferenced once, is read from and so
      must not be a NULL pointer.  */
   int reading_from_flag;
+  /* Format conversion suggestions, if available, otherwise NULL.  */
+  const format_suggestions *suggestions;
   /* If warnings should be of the form "field precision should have
      type 'int'", the name to use (in this case "field precision"),
      otherwise NULL, for "format expects type 'long'" type
@@ -498,6 +500,43 @@ static const format_flag_pair strfmon_fl
   { 0, 0, 0, 0 }
 };
 
+static const format_suggestions printf_suggestions[] =
+{
+  { &wchar_type_node, "wchar_t", FMT_SUG_POINTER, STD_C94, "ls" },
+  { &wchar_type_node, "wchar_t", 0, STD_C94, "lc" },
+  { &wint_type_node, "wint_t", 0, STD_C94, "lc" },
+  { &signed_size_type_node, "ssize_t", 0, STD_C99, "zd, zi" },
+  { &size_type_node, "size_t", 0, STD_C99, "zo, zu, zx, zX" },
+  { &ptrdiff_type_node, "ptrdiff_t", 0, STD_C99, "td, ti" },
+  { &intmax_type_node, "intmax_t", 0, STD_C99, "jd, ji" },
+  { &uintmax_type_node, "uintmax_t", 0, STD_C99, "jo, ju, jx, jX" },
+  { &dfloat32_type_node, "_Decimal32", 0, STD_EXT, "He, HE, Hf, HF, Hg, HG" },
+  { &dfloat64_type_node, "_Decimal64", 0, STD_EXT, "De, DE, Df, DF, Dg, DG" },
+  { &dfloat128_type_node, "_Decimal128", 0, STD_EXT, "DDe, DDE, DDf, DDF, DDg, DDG" },
+  { &unsigned_char_type_node, NULL, 0, STD_C99, "c, hho, hhu, hhx, hhX" },
+  { &unsigned_char_type_node, NULL, 0, STD_C89, "c" },
+  { &signed_char_type_node, NULL, 0, STD_C99, "hhd, hhi" },
+  { &short_unsigned_type_node, NULL, 0, STD_C89, "ho, hu, hx, hX" },
+  { &short_integer_type_node, NULL, 0, STD_C89, "hd, hi" },
+  { &unsigned_type_node, NULL, 0, STD_C89, "o, u, x, X" },
+  { &integer_type_node, NULL, 0, STD_C89, "d, i" },
+  { &long_integer_type_node, NULL, 0, STD_C89, "ld, li" },
+  { &long_long_integer_type_node, NULL, 0, STD_C9L, "lld, lli" },
+  { &char_type_node, NULL, FMT_SUG_POINTER, STD_C89, "s" },
+  { &long_unsigned_type_node, NULL, 0, STD_C89, "lo, lu, lx, lX" },
+  { &long_long_unsigned_type_node, NULL, 0, STD_C9L, "llo, llu, llx, llX" },
+  { &double_type_node, NULL, 0, STD_C99, "a, A, e, E, f, F, g, G" },
+  { &long_double_type_node, NULL, 0, STD_C99, "La, LA, Le, LE, Lf, LF, Lg, LG" },
+  { &double_type_node, NULL, 0, STD_C89, "e, E, f, g, G" },
+  { &long_double_type_node, NULL, 0, STD_C89, "Le, LE, Lf, Lg, LG" },
+  { &signed_char_type_node, NULL, FMT_SUG_POINTER|FMT_SUG_WRITE, STD_C99, "hhn" },
+  { &short_integer_type_node, NULL, FMT_SUG_POINTER|FMT_SUG_WRITE, STD_C89, "hn" },
+  { &integer_type_node, NULL, FMT_SUG_POINTER|FMT_SUG_WRITE, STD_C89, "n" },
+  { &long_integer_type_node, NULL, FMT_SUG_POINTER|FMT_SUG_WRITE, STD_C89, "ln" },
+  { &long_long_integer_type_node, NULL, FMT_SUG_POINTER|FMT_SUG_WRITE, STD_C9L, "lln" },
+  { &void_type_node, NULL, FMT_SUG_POINTER, STD_C89, "p" },
+  { NULL, NULL, 0, 0, NULL }
+};
 
 static const format_char_info print_char_table[] =
 {
@@ -713,60 +752,60 @@ static const format_char_info monetary_c
 static const format_kind_info format_types_orig[] =
 {
   { "printf",   printf_length_specs,  print_char_table, " +#0-'I", NULL,
-    printf_flag_specs, printf_flag_pairs,
+    printf_flag_specs, printf_flag_pairs, printf_suggestions,
     FMT_FLAG_ARG_CONVERT|FMT_FLAG_DOLLAR_MULTIPLE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_EMPTY_PREC_OK,
     'w', 0, 'p', 0, 'L',
     &integer_type_node, &integer_type_node
   },
   { "asm_fprintf",   asm_fprintf_length_specs,  asm_fprintf_char_table, " +#0-", NULL,
-    asm_fprintf_flag_specs, asm_fprintf_flag_pairs,
+    asm_fprintf_flag_specs, asm_fprintf_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT|FMT_FLAG_EMPTY_PREC_OK,
     'w', 0, 'p', 0, 'L',
     NULL, NULL
   },
   { "gcc_diag",   gcc_diag_length_specs,  gcc_diag_char_table, "q+", NULL,
-    gcc_diag_flag_specs, gcc_diag_flag_pairs,
+    gcc_diag_flag_specs, gcc_diag_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT,
     0, 0, 'p', 0, 'L',
     NULL, &integer_type_node
   },
   { "gcc_tdiag",   gcc_tdiag_length_specs,  gcc_tdiag_char_table, "q+", NULL,
-    gcc_tdiag_flag_specs, gcc_tdiag_flag_pairs,
+    gcc_tdiag_flag_specs, gcc_tdiag_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT,
     0, 0, 'p', 0, 'L',
     NULL, &integer_type_node
   },
   { "gcc_cdiag",   gcc_cdiag_length_specs,  gcc_cdiag_char_table, "q+", NULL,
-    gcc_cdiag_flag_specs, gcc_cdiag_flag_pairs,
+    gcc_cdiag_flag_specs, gcc_cdiag_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT,
     0, 0, 'p', 0, 'L',
     NULL, &integer_type_node
   },
   { "gcc_cxxdiag",   gcc_cxxdiag_length_specs,  gcc_cxxdiag_char_table, "q+#", NULL,
-    gcc_cxxdiag_flag_specs, gcc_cxxdiag_flag_pairs,
+    gcc_cxxdiag_flag_specs, gcc_cxxdiag_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT,
     0, 0, 'p', 0, 'L',
     NULL, &integer_type_node
   },
   { "gcc_gfc", gcc_gfc_length_specs, gcc_gfc_char_table, "", NULL,
-    NULL, gcc_gfc_flag_pairs,
+    NULL, gcc_gfc_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT,
     0, 0, 0, 0, 0,
     NULL, NULL
   },
   { "scanf",    scanf_length_specs,   scan_char_table,  "*'I", NULL,
-    scanf_flag_specs, scanf_flag_pairs,
+    scanf_flag_specs, scanf_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT|FMT_FLAG_SCANF_A_KLUDGE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_ZERO_WIDTH_BAD|FMT_FLAG_DOLLAR_GAP_POINTER_OK,
     'w', 0, 0, '*', 'L',
     NULL, NULL
   },
   { "strftime", NULL,                 time_char_table,  "_-0^#", "EO",
-    strftime_flag_specs, strftime_flag_pairs,
+    strftime_flag_specs, strftime_flag_pairs, NULL,
     FMT_FLAG_FANCY_PERCENT_OK, 'w', 0, 0, 0, 0,
     NULL, NULL
   },
   { "strfmon",  strfmon_length_specs, monetary_char_table, "=^+(!-", NULL,
-    strfmon_flag_specs, strfmon_flag_pairs,
+    strfmon_flag_specs, strfmon_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT, 'w', '#', 'p', 0, 'L',
     NULL, NULL
   }
@@ -834,7 +873,8 @@ static const format_flag_spec *get_flag_
 
 static void check_format_types (format_wanted_type *, const char *, int);
 static void format_type_warning (const char *, const char *, int, tree,
-				 int, const char *, tree, int);
+				 int, const char *, tree, int,
+				 const format_suggestions *);
 
 /* Decode a format type from a string, returning the type, or
    format_type_error if not valid, in which case the caller should print an
@@ -1616,6 +1656,7 @@ check_format_info_main (format_check_res
 		  width_wanted_type.name = _("field width");
 		  width_wanted_type.param = cur_param;
 		  width_wanted_type.arg_num = arg_num;
+		  width_wanted_type.suggestions = NULL;
 		  width_wanted_type.next = NULL;
 		  if (last_wanted_type != 0)
 		    last_wanted_type->next = &width_wanted_type;
@@ -1718,6 +1759,7 @@ check_format_info_main (format_check_res
 		  precision_wanted_type.name = _("field precision");
 		  precision_wanted_type.param = cur_param;
 		  precision_wanted_type.arg_num = arg_num;
+		  precision_wanted_type.suggestions = NULL;
 		  precision_wanted_type.next = NULL;
 		  if (last_wanted_type != 0)
 		    last_wanted_type->next = &precision_wanted_type;
@@ -2077,6 +2119,7 @@ check_format_info_main (format_check_res
 	      wanted_type_ptr->name = NULL;
 	      wanted_type_ptr->param = cur_param;
 	      wanted_type_ptr->arg_num = arg_num;
+	      wanted_type_ptr->suggestions = fki->suggestions;
 	      wanted_type_ptr->next = NULL;
 	      if (last_wanted_type != 0)
 		last_wanted_type->next = wanted_type_ptr;
@@ -2123,7 +2166,7 @@ check_format_types (format_wanted_type *
     {
       tree cur_param;
       tree cur_type;
-      tree orig_cur_type;
+      tree orig_cur_param;
       tree wanted_type;
       int arg_num;
       int i;
@@ -2132,7 +2175,7 @@ check_format_types (format_wanted_type *
       cur_type = TREE_TYPE (cur_param);
       if (cur_type == error_mark_node)
 	continue;
-      orig_cur_type = cur_type;
+      orig_cur_param = cur_param;
       char_type_flag = 0;
       wanted_type = types->wanted_type;
       arg_num = types->arg_num;
@@ -2211,8 +2254,8 @@ check_format_types (format_wanted_type *
 	    {
 	      format_type_warning (types->name, format_start, format_length,
 				   wanted_type, types->pointer_count,
-				   types->wanted_type_name, orig_cur_type,
-				   arg_num);
+				   types->wanted_type_name, orig_cur_param,
+				   arg_num, types->suggestions);
 	      break;
 	    }
 	}
@@ -2260,7 +2303,8 @@ check_format_types (format_wanted_type *
       /* Now we have a type mismatch.  */
       format_type_warning (types->name, format_start, format_length,
 			   wanted_type, types->pointer_count,
-			   types->wanted_type_name, orig_cur_type, arg_num);
+			   types->wanted_type_name, orig_cur_param, arg_num,
+			   types->suggestions);
     }
 }
 
@@ -2272,15 +2316,20 @@ check_format_types (format_wanted_type *
    FORMAT_LENGTH is its length.  WANTED_TYPE is the type the argument
    should have after POINTER_COUNT pointer dereferences.
    WANTED_NAME_NAME is a possibly more friendly name of WANTED_TYPE,
-   or NULL if the ordinary name of the type should be used.  ARG_TYPE
-   is the type of the actual argument.  ARG_NUM is the number of that
-   argument.  */
+   or NULL if the ordinary name of the type should be used.  ARG
+   is the actual argument.  ARG_NUM is the number of that
+   argument.  SUGGESTIONS is used to suggest format conversions that
+   are acceptable for the argument type provided.  It may be NULL.  */
 static void
 format_type_warning (const char *descr, const char *format_start,
 		     int format_length, tree wanted_type, int pointer_count,
-		     const char *wanted_type_name, tree arg_type, int arg_num)
+		     const char *wanted_type_name, tree arg, int arg_num,
+		     const format_suggestions *suggestions)
 {
+  const char *suggest = NULL;
+  tree arg_type;
   char *p;
+  arg_type = TREE_TYPE (arg);
   /* If ARG_TYPE is a typedef with a misleading name (for example,
      size_t but not the standard size_t expected by printf %zu), avoid
      printing the typedef name.  */
@@ -2310,6 +2359,71 @@ format_type_warning (const char *descr, 
       memset (p + 1, '*', pointer_count);
       p[pointer_count + 1] = 0;
     }
+
+  if (suggestions)
+    {
+      tree type = TREE_TYPE (arg);
+      for ( ; suggestions->type; ++suggestions)
+	{
+	  tree sug_type;
+	  tree test_type;
+	  if (C_STD_VER < ADJ_STD (suggestions->std)
+	      && (suggestions->std != STD_EXT || flag_iso))
+	    continue;
+	  if (suggestions->flags & FMT_SUG_POINTER)
+	    {
+	      if (TREE_CODE (type) == POINTER_TYPE)
+		test_type = TREE_TYPE (type);
+	      else
+		continue;
+	    }
+	  else
+	    test_type = type;
+	  if (suggestions->flags & FMT_SUG_WRITE)
+	    {
+	      gcc_assert (suggestions->flags & FMT_SUG_POINTER);
+	      if (TYPE_READONLY (test_type))
+		continue;
+	    }
+	  sug_type = *suggestions->type;
+	  if (suggestions->name)
+	    {
+	      if ((test_type == size_type_node
+		   || test_type == ptrdiff_type_node
+		   || test_type == wchar_type_node)
+		  && test_type == sug_type)
+		break;
+	      else
+		{
+		  const char *type_name;
+		  if (TYPE_NAME (test_type)
+		      && TREE_CODE (TYPE_NAME (test_type)) == TYPE_DECL)
+		    type_name = get_name (TYPE_NAME (test_type));
+		  else
+		    type_name = NULL;
+		  if (!type_name || strcmp (type_name, suggestions->name) != 0)
+		    continue;
+		}
+	    }
+	  test_type = TYPE_MAIN_VARIANT (test_type);
+	  if (lang_hooks.types_compatible_p (test_type, sug_type))
+	    break;
+	  if (!(suggestions->flags & FMT_SUG_POINTER))
+	    {
+	      tree pro_type = lang_hooks.types.type_promotes_to (sug_type);
+	      if (lang_hooks.types_compatible_p (pro_type, test_type)
+		  && (TREE_CODE (arg) == NOP_EXPR
+		      || TREE_CODE (arg) == CONVERT_EXPR))
+		{
+		  tree orig_arg_type = TREE_TYPE (TREE_OPERAND (arg, 0));
+		  if (lang_hooks.types_compatible_p (sug_type, orig_arg_type))
+		    break;
+		}
+	    }
+	}
+      suggest = suggestions->desc;
+    }
+
   if (wanted_type_name)
     {
       if (descr)
@@ -2333,6 +2447,9 @@ format_type_warning (const char *descr, 
 		 "but argument %d has type %qT",
 		 format_length, format_start, wanted_type, p, arg_num, arg_type);
     }
+  if (suggest)
+    warning (OPT_Wformat, "acceptable formats for this type are: %s",
+	     suggest);
 }
 
 
Index: gcc/c-format.h
===================================================================
--- gcc/c-format.h	(revision 127489)
+++ gcc/c-format.h	(working copy)
@@ -200,6 +200,33 @@ typedef struct
 } format_flag_pair;
 
 
+/* Flags used to specify types in format string suggestions.  */
+enum
+{
+  /* Set if the required type is a pointer to the type specified in the
+     format_suggestions structure.  */
+  FMT_SUG_POINTER = 1,
+  /* Set if the required type must be writable.  The FMT_SUG_POINTER flag
+     must also be set.  */
+  FMT_SUG_WRITE = 2
+};
+
+/* Structure mapping a type to format conversion suggestions.  */
+typedef struct
+{
+  /* The type wanted by the conversions.  */
+  tree *type;
+  /* A name for this type if it's typedef'd, otherwise NULL.  */
+  const char *name;
+  /* Flags specified by the FMT_SUG_ constants.  */
+  unsigned flags;
+  /* The standard to which the conversions belong.  */
+  enum format_std_version std;
+  /* The conversion suggestions for use with this type.  */
+  const char *desc;
+} format_suggestions;
+
+
 /* Structure describing a particular kind of format processed by GCC.  */
 typedef struct
 {
@@ -218,6 +245,8 @@ typedef struct
   const format_flag_spec *flag_specs;
   /* Details of bad combinations of flags.  */
   const format_flag_pair *bad_flag_pairs;
+  /* Suggestions for conversions based on type.  */
+  const format_suggestions *suggestions;
   /* Flags applicable to this kind of format.  */
   int flags;
   /* Flag character to treat a width as, or 0 if width not used.  */
Index: gcc/c-common.c
===================================================================
--- gcc/c-common.c	(revision 127489)
+++ gcc/c-common.c	(working copy)
@@ -3480,6 +3480,19 @@ c_define_builtins (tree va_list_ref_type
     mudflap_init ();
 }
 
+
+/* Look up the type with identifier NAME and return a copy of it.  We want
+   some of the standard, but non-primitive, types, such as size_t, to be
+   copies of the types they represent, so we can still tell them apart.  */
+
+static tree
+copy_type_with_name (const char *name)
+{
+  return
+    copy_node (TREE_TYPE (identifier_global_value (get_identifier (name))));
+}
+
+
 /* Build tree nodes and builtin functions common to both C and C++ language
    frontends.  */
 
@@ -3569,8 +3582,7 @@ c_common_nodes_and_builtins (void)
   /* `unsigned long' is the standard type for sizeof.
      Note that stddef.h uses `unsigned long',
      and this must agree, even if long and int are the same size.  */
-  size_type_node =
-    TREE_TYPE (identifier_global_value (get_identifier (SIZE_TYPE)));
+  size_type_node = copy_type_with_name (SIZE_TYPE);
   signed_size_type_node = c_common_signed_type (size_type_node);
   set_sizetype (size_type_node);
 
@@ -3653,8 +3665,7 @@ c_common_nodes_and_builtins (void)
 			  (char_type_node, TYPE_QUAL_CONST));
 
   /* This is special for C++ so functions can be overloaded.  */
-  wchar_type_node = get_identifier (MODIFIED_WCHAR_TYPE);
-  wchar_type_node = TREE_TYPE (identifier_global_value (wchar_type_node));
+  wchar_type_node = copy_type_with_name (MODIFIED_WCHAR_TYPE);
   wchar_type_size = TYPE_PRECISION (wchar_type_node);
   if (c_dialect_cxx ())
     {
@@ -3683,8 +3694,7 @@ c_common_nodes_and_builtins (void)
     TREE_TYPE (identifier_global_value (get_identifier (UINTMAX_TYPE)));
 
   default_function_type = build_function_type (integer_type_node, NULL_TREE);
-  ptrdiff_type_node
-    = TREE_TYPE (identifier_global_value (get_identifier (PTRDIFF_TYPE)));
+  ptrdiff_type_node = copy_type_with_name (PTRDIFF_TYPE);
   unsigned_ptrdiff_type_node = c_common_unsigned_type (ptrdiff_type_node);
 
   lang_hooks.decls.pushdecl

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: PATCH: Add format string suggestions to -Wformat warnings
  2007-08-14 23:51 PATCH: Add format string suggestions to -Wformat warnings Dan Hipschman
@ 2007-08-15  0:00 ` Andrew Pinski
  2007-08-15  0:39 ` Joseph S. Myers
  1 sibling, 0 replies; 8+ messages in thread
From: Andrew Pinski @ 2007-08-15  0:00 UTC (permalink / raw)
  To: dsh, gcc-patches

On 8/14/07, Dan Hipschman <dsh@google.com> wrote:
> foo.c:6: warning: format '%lu' expects type 'long unsigned int', but argument 2 has type 'unsigned int'
> foo.c:6: warning: acceptable formats for this type are: zo, zu, zx, zX

Really I think this should be a "note:" and not a "warning".

When that is done, you need to change the testcase to take into
account that change as dg-warning no longer accepts "note:" messages;
only dg-message does.

Thanks,
Andrew Pinski

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: PATCH: Add format string suggestions to -Wformat warnings
  2007-08-14 23:51 PATCH: Add format string suggestions to -Wformat warnings Dan Hipschman
  2007-08-15  0:00 ` Andrew Pinski
@ 2007-08-15  0:39 ` Joseph S. Myers
  2007-08-21 22:58   ` Dan Hipschman
  1 sibling, 1 reply; 8+ messages in thread
From: Joseph S. Myers @ 2007-08-15  0:39 UTC (permalink / raw)
  To: Dan Hipschman; +Cc: gcc-patches

On Tue, 14 Aug 2007, Dan Hipschman wrote:

> TYPE_IS_SIZETYPE doesn't work: see the bottom of c_sizeof_or_alignof_type.)
> Simply copying the type nodes seemed like the least-possibly harmful way to do
> this, and I've bootstrapped and run "make -k check" on i686-linux-gnu with no

Going round the back of the type machinery with copy_node is dangerous; 
the type should be a normal variant of the underlying type, just like the 
standard typedefs in the headers, which means using 
build_variant_type_copy.  When sizeof used to return the internal sizetype 
there were many subtle problems caused by this; to avoid returning to such 
problems, the types need to continue to be normal type variants.

At that point, you then need to deal with (unsigned)sizeof(x), which 
should suggest formats for "unsigned" not those for "size_t" even if the 
two happen to be the same (and so the compiler may decide the cast is 
redundant); and typedefs descended from the standard size_t should act 
like size_t.  (If the program used autoconf or similar to ascertain the 
type of size_t and created its own typedef based on that, it's fine as 
long as the formats are correct since then you don't get warnings, but 
there's probably no way to get good format suggestions.)  There's no one 
obvious right answer to how to treat the results of arithmetic such as 
unsigned+size_t.

(You could avoid all the complexity of tracking whether a particular 
expression is ultimately derived from a standard typedef by giving less 
precise suggestions, and then reconsider that tracking later for a 
separate patch.)

dg-excess-errors should only be used very sparingly with xfail.  When 
there are specific diagnostics expected on specific lines, you should 
update the tests to match each new diagnostic individually rather than 
using dg-excess-errors to match them all together.

As well as using inform () to output notes instead of warnings, it might 
make sense to throttle the suggestions to at most one per format string 
(if there are many format warnings from a string, it's probably someone 
missing out an argument or a format rather than systematically getting a 
series of formats wrong).

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: PATCH: Add format string suggestions to -Wformat warnings
  2007-08-15  0:39 ` Joseph S. Myers
@ 2007-08-21 22:58   ` Dan Hipschman
  2007-09-13 23:20     ` Joseph S. Myers
  0 siblings, 1 reply; 8+ messages in thread
From: Dan Hipschman @ 2007-08-21 22:58 UTC (permalink / raw)
  To: Joseph S. Myers; +Cc: gcc-patches

On Wed, Aug 15, 2007 at 12:38:44AM +0000, Joseph S. Myers wrote:
> Going round the back of the type machinery with copy_node is dangerous; 
> the type should be a normal variant of the underlying type, just like the 
> standard typedefs in the headers, which means using 
> build_variant_type_copy.  When sizeof used to return the internal sizetype 
> there were many subtle problems caused by this; to avoid returning to such 
> problems, the types need to continue to be normal type variants.

Thanks very much for the help.  I'm using build_variant_type_copy in this
version of the patch.

> At that point, you then need to deal with (unsigned)sizeof(x), which 
> should suggest formats for "unsigned" not those for "size_t" even if the 
> two happen to be the same (and so the compiler may decide the cast is 

Done.  I added a call to convert in build_c_cast, even if the main variant
of the casted to type is the same as the main variant of the original type.
If the two types have the same actual address, convert will still ignore the
cast, but if not it will generate a NOP_EXPR so we can see the name of the
type being cast to.  There's a testcase in ext-suggest-1.c.

> redundant); and typedefs descended from the standard size_t should act 
> like size_t.  (If the program used autoconf or similar to ascertain the 

Done.  There's a test for it in typedef-suggest-2.c.

> (You could avoid all the complexity of tracking whether a particular 
> expression is ultimately derived from a standard typedef by giving less 
> precise suggestions, and then reconsider that tracking later for a 
> separate patch.)

I think size_t and those sort of things are the most important ones to
inform people about, so I'd rather get it right.

> dg-excess-errors should only be used very sparingly with xfail.  When 
> there are specific diagnostics expected on specific lines, you should 
> update the tests to match each new diagnostic individually rather than 
> using dg-excess-errors to match them all together.

OK, I've done this, too.

> As well as using inform () to output notes instead of warnings, it might 
> make sense to throttle the suggestions to at most one per format string 
> (if there are many format warnings from a string, it's probably someone 
> missing out an argument or a format rather than systematically getting a 
> series of formats wrong).

I'm using inform now.  I'm not sure about limiting the notes to one per
format string.  I thought about limiting them to one per type, but because
the types may have different names (if they're typedef'd), I found it was
sometimes unclear what was supposed to be used when a note was missing.  I
did add the name of the type in the note, though:

$ cat foo.c
#include <stdio.h>
void f (unsigned char c)
{
  printf ("%e%p", sizeof 0, c);
}

$ gcc-svn -std=gnu99 -Wformat -c foo.c
foo.c: In function 'f':
foo.c:4: warning: format '%e' expects type 'double', but argument 2 has type 'unsigned int'
foo.c:4: note: acceptable formats for type 'size_t' are: zo, zu, zx, zX
foo.c:4: warning: format '%p' expects type 'void *', but argument 3 has type 'int'
foo.c:4: note: acceptable formats for type 'unsigned char' are: c, hho, hhu, hhx, hhX

It would be nice if the types given by the warning matched the types given by
notice, but I think that's too much for one patch.

Here's the revised patch.  I've done a bootstrap and regression test on
i686-linux-gnu.


gcc/
2007-08-21  Dan Hipschman  <dsh@google.com>

	* c-format.h: New FMT_SUG_* flags.
	(format_suggestions): New structure.
	(format_kind_info): Add suggestions field.
	* c-format.c (format_wanted_type): Add suggestions field.
	(printf_suggestions): New global.
	(format_types_orig): Add suggestions field initializers.
	(check_format_info_main): Use suggestions.
	(arg_readonly): New function.
	(check_format_types): Use it.  Pass orig_cur_param instead of
	orig_cur_type to format_type_warning.
	(format_type_warning): Add suggestions and arg parameters.  Remove
	arg_type parameter.  Use format suggestions in warnings.
	* c-typeck.c (build_c_cast): Add a call to convert even if main
	variants are the same.
	* c-common.c (copy_type_with_name): New function.
	(c_common_nodes_and_builtins): Use it.

testsuite/
2007-08-21  Dan Hipschman  <dsh@google.com>

	* gcc.dg/format/dfp-suggest-1.c: New test.
	* gcc.dg/format/ext-suggest-1.c: New test.
	* gcc.dg/format/ext-suggest-2.c: New test.
	* gcc.dg/format/typedef-suggest-1.c: New test.
	* gcc.dg/format/typedef-suggest-2.c: New test.
	* gcc.dg/format/array-1.c: Update to handle format suggestion notes.
	* gcc.dg/format/branch-1.c: Likewise.
	* gcc.dg/format/builtin-1.c: Likewise.
	* gcc.dg/format/c90-printf-1.c: Likewise.
	* gcc.dg/format/c90-printf-3.c: Likewise.
	* gcc.dg/format/c99-printf-1.c: Likewise.
	* gcc.dg/format/c99-printf-3.c: Likewise.
	* gcc.dg/format/cast-1.c: Likewise.
	* gcc.dg/format/dfp-printf-1.c: Likewise.
	* gcc.dg/format/diag-1.c: Likewise.
	* gcc.dg/format/ext-1.c: Likewise.
	* gcc.dg/format/ext-5.c: Likewise.
	* gcc.dg/format/ext-6.c: Likewise.
	* gcc.dg/format/multattr-3.c: Likewise.
	* gcc.dg/format/unnamed-1.c: Likewise.
	* gcc.dg/format/va-1.c: Likewise.
	* gcc.dg/format/xopen-1.c: Likewise.

Index: gcc/testsuite/gcc.dg/format/builtin-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/builtin-1.c	(revision 127669)
+++ gcc/testsuite/gcc.dg/format/builtin-1.c	(working copy)
@@ -11,12 +11,12 @@ void
 foo (int i)
 {
   __builtin_fprintf (stdout, "%d", i);
-  __builtin_fprintf (stdout, "%ld", i); /* { dg-warning "format" "__builtin_fprintf" } */
+  __builtin_fprintf (stdout, "%ld", i); /* { dg-message "format|: d, i" } */
   __builtin_printf ("%d", i);
-  __builtin_printf ("%ld", i); /* { dg-warning "format" "__builtin_printf" } */
+  __builtin_printf ("%ld", i); /* { dg-message "format|: d, i" } */
 
   __builtin_fprintf_unlocked (stdout, "%d", i);
-  __builtin_fprintf_unlocked (stdout, "%ld", i); /* { dg-warning "format" "__builtin_fprintf_unlocked" } */
+  __builtin_fprintf_unlocked (stdout, "%ld", i); /* { dg-message "format|: d, i" } */
   __builtin_printf_unlocked ("%d", i);
-  __builtin_printf_unlocked ("%ld", i); /* { dg-warning "format" "__builtin_printf_unlocked" } */
+  __builtin_printf_unlocked ("%ld", i); /* { dg-message "format|: d, i" } */
 }
Index: gcc/testsuite/gcc.dg/format/ext-suggest-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/ext-suggest-1.c	(revision 0)
+++ gcc/testsuite/gcc.dg/format/ext-suggest-1.c	(revision 0)
@@ -0,0 +1,60 @@
+/* Copyright (C) 2007 Free Software Foundation.  */
+/* Contributed by Dan Hipschman <dsh@google.com>.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu99 -Wformat" } */
+
+#include "format.h"
+
+void
+foo (signed char hhd, unsigned char c, short hd, unsigned short hu, size_t zu,
+     ssize_t zd, ptrdiff_t td, intmax_t jd, uintmax_t ju, wchar_t *ls,
+     void *p, float f, long double Lf, const char *s, const void *p2)
+{
+  char a[2] = " ";
+  char *td1 = &a[1], *td2 = &a[0];
+  int n;
+  long ln;
+  long long lln;
+  printf ("%p", hhd); /* { dg-message "expects|: hhd, hhi" } */
+  printf ("%p", c); /* { dg-message "expects|: c, hho, hhu, hhx, hhX" } */
+  printf ("%p", a[0]); /* { dg-message "expects|: d, i" } */
+  printf ("%p", hd); /* { dg-message "expects|: hd, hi" } */
+  printf ("%p", hu); /* { dg-message "expects|: ho, hu, hx, hX" } */
+  printf ("%p", 0); /* { dg-message "expects|: d, i" } */
+  printf ("%p", 0u); /* { dg-message "expects|: o, u, x, X" } */
+  printf ("%p", (unsigned) sizeof 0); /* { dg-message "expects|: o, u, x, X" } */
+  printf ("%p", 0L); /* { dg-message "expects|: ld, li" } */
+  printf ("%p", 0uL); /* { dg-message "expects|: lo, lu, lx, lX" } */
+  printf ("%p", 0LL); /* { dg-message "expects|: lld, lli" } */
+  printf ("%d", 0L); /* { dg-message "expects|: ld, li" } */
+  printf ("%u", 0uL); /* { dg-message "expects|: lo, lu, lx, lX" } */
+  printf ("%p", 0uLL); /* { dg-message "expects|: llo, llu, llx, llX" } */
+  printf ("%p", zu); /* { dg-message "expects|: zo, zu, zx, zX" } */
+  printf ("%p", sizeof 0); /* { dg-message "expects|: zo, zu, zx, zX" } */
+  printf ("%p", zd); /* { dg-message "expects|: zd, zi" } */
+  printf ("%p", td); /* { dg-message "expects|: td, ti" } */
+  printf ("%p", td1 - td2); /* { dg-message "expects|: td, ti" } */
+  printf ("%p", jd); /* { dg-message "expects|: jd, ji" } */
+  printf ("%p", ju); /* { dg-message "expects|: jo, ju, jx, jX" } */
+  printf ("%d", a); /* { dg-message "expects|: s" } */
+  printf ("%c", "foo"); /* { dg-message "expects|: s" } */
+  printf ("%d", s); /* { dg-message "expects|: s" } */
+  printf ("%c", ls); /* { dg-message "expects|: ls" } */
+  printf ("%s", L"foo"); /* { dg-message "expects|: ls" } */
+  printf ("%c", L'x'); /* { dg-message "expects|: lc" } */
+  printf ("%d", p); /* { dg-message "expects|: p" } */
+  printf ("%x", p2); /* { dg-message "expects|: p" } */
+  printf ("%d", f); /* { dg-message "expects|: a, A, e, E, f, F, g, G" } */
+  printf ("%d", 0.0); /* { dg-message "expects|: a, A, e, E, f, F, g, G" } */
+  printf ("%f", Lf); /* { dg-message "expects|: La, LA, Le, LE, Lf, LF, Lg, LG" } */
+  printf ("%f", &hhd); /* { dg-message "expects|: hhn" } */
+  printf ("%f", (const signed char *) &hhd); /* { dg-warning "expects" } */
+  printf ("%f", &hd); /* { dg-message "expects|: hn" } */
+  printf ("%f", (const short *) &hd); /* { dg-warning "expects" } */
+  printf ("%f", &n); /* { dg-message "expects|: n" } */
+  printf ("%f", (const int *) &n); /* { dg-warning "expects" } */
+  printf ("%f", &ln); /* { dg-message "expects|: ln" } */
+  printf ("%f", (const long *) &ln); /* { dg-warning "expects" } */
+  printf ("%f", &lln); /* { dg-message "expects|: lln" } */
+  printf ("%f", (const long long *) &lln); /* { dg-warning "expects" } */
+}
Index: gcc/testsuite/gcc.dg/format/typedef-suggest-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/typedef-suggest-1.c	(revision 0)
+++ gcc/testsuite/gcc.dg/format/typedef-suggest-1.c	(revision 0)
@@ -0,0 +1,42 @@
+/* Copyright (C) 2007 Free Software Foundation.  */
+/* Contributed by Dan Hipschman <dsh@google.com>.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu99 -Wformat" } */
+
+extern void foo (const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+
+/* See if we can fool the compiler.  */
+typedef double size_t;
+typedef double ptrdiff_t;
+typedef double wchar_t;
+typedef double wint_t;
+typedef double ssize_t;
+typedef char *intmax_t;
+typedef void *uintmax_t;
+
+char a[2];
+
+void
+bar (size_t s1, ptrdiff_t p1, wchar_t c1, wchar_t *pc1, wint_t i1, ssize_t ss1,
+     intmax_t m1, uintmax_t m2)
+{
+  size_t s2 = 0.0;
+  foo ("%p", sizeof 0); /* { dg-message "expects|: zo, zu, zx, zX" } */
+  foo ("%p", s1); /* { dg-message "expects|: a, A, e, E, f, F, g, G" } */
+  foo ("%p", s2); /* { dg-message "expects|: a, A, e, E, f, F, g, G" } */
+  foo ("%zu", s2); /* { dg-message "expects|: a, A, e, E, f, F, g, G" } */
+  foo ("%p", &a[0] - &a[1]); /* { dg-message "expects|: td, ti" } */
+  foo ("%p", p1); /* { dg-message "expects|: a, A, e, E, f, F, g, G" } */
+  foo ("%ti", p1); /* { dg-message "expects|: a, A, e, E, f, F, g, G" } */
+  foo ("%s", L'x'); /* { dg-message "expects|: lc" } */
+  foo ("%s", c1); /* { dg-message "expects|: a, A, e, E, f, F, g, G" } */
+  foo ("%lc", c1); /* { dg-message "expects|: a, A, e, E, f, F, g, G" } */
+  foo ("%s", i1); /* { dg-message "expects|: a, A, e, E, f, F, g, G" } */
+  foo ("%lc", i1); /* { dg-message "expects|: a, A, e, E, f, F, g, G" } */
+  foo ("%s", L"x"); /* { dg-message "expects|: ls" } */
+  foo ("%s", pc1); /* { dg-warning "expects" } */
+  foo ("%s", ss1); /* { dg-message "expects|: a, A, e, E, f, F, g, G" } */
+  foo ("%zd", ss1); /* { dg-message "expects|: a, A, e, E, f, F, g, G" } */
+  foo ("%i", m1); /* { dg-message "expects|: s" } */
+  foo ("%ju", m2); /* { dg-message "expects|: p" } */
+}
Index: gcc/testsuite/gcc.dg/format/ext-6.c
===================================================================
--- gcc/testsuite/gcc.dg/format/ext-6.c	(revision 127669)
+++ gcc/testsuite/gcc.dg/format/ext-6.c	(working copy)
@@ -13,17 +13,17 @@ foo (int i, char *s, size_t n, int *ip, 
      va_list v9, va_list v10, va_list v11, va_list v12, va_list v13)
 {
   fprintf (stdout, "%d", i);
-  fprintf (stdout, "%ld", i); /* { dg-warning "format" "fprintf" } */
+  fprintf (stdout, "%ld", i); /* { dg-message "format|: d, i" "fprintf" } */
   printf ("%d", i);
-  printf ("%ld", i); /* { dg-warning "format" "printf" } */
+  printf ("%ld", i); /* { dg-message "format|: d, i" "printf" } */
   fprintf_unlocked (stdout, "%d", i);
-  fprintf_unlocked (stdout, "%ld", i); /* { dg-warning "format" "fprintf_unlocked" } */
+  fprintf_unlocked (stdout, "%ld", i); /* { dg-message "format|: d, i" "fprintf_unlocked" } */
   printf_unlocked ("%d", i);
-  printf_unlocked ("%ld", i); /* { dg-warning "format" "printf_unlocked" } */
+  printf_unlocked ("%ld", i); /* { dg-message "format|: d, i" "printf_unlocked" } */
   sprintf (s, "%d", i);
-  sprintf (s, "%ld", i); /* { dg-warning "format" "sprintf" } */
+  sprintf (s, "%ld", i); /* { dg-message "format|: d, i" "sprintf" } */
   snprintf (s, n, "%d", i);
-  snprintf (s, n, "%ld", i); /* { dg-warning "format" "snprintf" } */
+  snprintf (s, n, "%ld", i); /* { dg-message "format|: d, i" "snprintf" } */
   vfprintf (stdout, "%d", v0);
   vfprintf (stdout, "%Y", v1); /* { dg-warning "format" "vfprintf" } */
   vprintf ("%d", v2);
Index: gcc/testsuite/gcc.dg/format/xopen-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/xopen-1.c	(revision 127669)
+++ gcc/testsuite/gcc.dg/format/xopen-1.c	(working copy)
@@ -89,7 +89,7 @@ foo (int i, unsigned int u, wint_t lc, w
   /* The use of operand number $ formats is an X/Open extension.  */
   scanf ("%1$d", ip);
   printf ("%1$d", i);
-  printf ("%1$d", l); /* { dg-warning "arg 2|argument 2" "mismatched args with $ format" } */
+  printf ("%1$d", l); /* { dg-message "arg 2|argument 2|: ld, li" "mismatched args with $ format" } */
   printf ("%3$*2$.*1$ld", i2, i, l);
   printf ("%4$ld%7$ld%5$d%6$d%3$d%1$d%2$d", i, i, i, l, i, i, l);
   scanf ("%4$ld%7$ld%5$d%6$d%3$d%1$d%2$d", ip, ip, ip, lp, ip, ip, lp);
Index: gcc/testsuite/gcc.dg/format/cast-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/cast-1.c	(revision 127669)
+++ gcc/testsuite/gcc.dg/format/cast-1.c	(working copy)
@@ -10,7 +10,7 @@
 void
 f (int x)
 {
-  printf("%s", x); /* { dg-warning "format" } */
-  printf((char *)(size_t)"%s", x); /* { dg-warning "format" } */
+  printf("%s", x); /* { dg-message "format|: d, i" } */
+  printf((char *)(size_t)"%s", x); /* { dg-message "format|: d, i" } */
   printf((char *)(char)"%s", x); /* { dg-warning "cast from pointer to integer of different size" } */
 }
Index: gcc/testsuite/gcc.dg/format/branch-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/branch-1.c	(revision 127669)
+++ gcc/testsuite/gcc.dg/format/branch-1.c	(working copy)
@@ -9,18 +9,18 @@ void
 foo (long l, int nfoo)
 {
   printf ((nfoo > 1) ? "%d foos" : "%d foo", nfoo);
-  printf ((l > 1) ? "%d foos" : "%d foo", l); /* { dg-warning "int" "wrong type in conditional expr" } */
-  printf ((l > 1) ? "%ld foos" : "%d foo", l); /* { dg-warning "int" "wrong type in conditional expr" } */
-  printf ((l > 1) ? "%d foos" : "%ld foo", l); /* { dg-warning "int" "wrong type in conditional expr" } */
+  printf ((l > 1) ? "%d foos" : "%d foo", l); /* { dg-message "int|: ld, li" "wrong type in conditional expr" } */
+  printf ((l > 1) ? "%ld foos" : "%d foo", l); /* { dg-message "int|: ld, li" "wrong type in conditional expr" } */
+  printf ((l > 1) ? "%d foos" : "%ld foo", l); /* { dg-message "int|: ld, li" "wrong type in conditional expr" } */
   /* Should allow one case to have extra arguments.  */
   printf ((nfoo > 1) ? "%d foos" : "1 foo", nfoo);
   printf ((nfoo > 1) ? "many foos" : "1 foo", nfoo); /* { dg-warning "too many" "too many args in all branches" } */
   printf ((nfoo > 1) ? "%d foos" : "", nfoo);
   printf ((nfoo > 1) ? "%d foos" : ((nfoo > 0) ? "1 foo" : "no foos"), nfoo);
   printf ((nfoo > 1) ? "%d foos" : ((nfoo > 0) ? "%d foo" : "%d foos"), nfoo);
-  printf ((nfoo > 1) ? "%d foos" : ((nfoo > 0) ? "%d foo" : "%ld foos"), nfoo); /* { dg-warning "long int" "wrong type" } */
-  printf ((nfoo > 1) ? "%ld foos" : ((nfoo > 0) ? "%d foo" : "%d foos"), nfoo); /* { dg-warning "long int" "wrong type" } */
-  printf ((nfoo > 1) ? "%d foos" : ((nfoo > 0) ? "%ld foo" : "%d foos"), nfoo); /* { dg-warning "long int" "wrong type" } */
+  printf ((nfoo > 1) ? "%d foos" : ((nfoo > 0) ? "%d foo" : "%ld foos"), nfoo); /* { dg-message "long int|: d, i" "wrong type" } */
+  printf ((nfoo > 1) ? "%ld foos" : ((nfoo > 0) ? "%d foo" : "%d foos"), nfoo); /* { dg-message "long int|: d, i" "wrong type" } */
+  printf ((nfoo > 1) ? "%d foos" : ((nfoo > 0) ? "%ld foo" : "%d foos"), nfoo); /* { dg-message "long int|: d, i" "wrong type" } */
   /* Extra arguments to NULL should be complained about.  */
   printf (NULL, "foo"); /* { dg-warning "too many" "NULL extra args" } */
   /* { dg-warning "null" "null format arg" { target *-*-* } 25 } */
Index: gcc/testsuite/gcc.dg/format/diag-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/diag-1.c	(revision 127669)
+++ gcc/testsuite/gcc.dg/format/diag-1.c	(working copy)
@@ -9,10 +9,10 @@ void
 foo (double d)
 {
   /* This should get a message referring to `hh', not to `H'.  */
-  printf ("%hhf", d); /* { dg-warning "hh" "%hhf warning" } */
+  printf ("%hhf", d); /* { dg-message "hh|: a, A, e, E, f, F, g, G" "%hhf warning" } */
   /* This should get a message referring to `ll', not to `q'.  */
-  printf ("%llf", d); /* { dg-warning "ll" "%llf warning" } */
+  printf ("%llf", d); /* { dg-message "ll|: a, A, e, E, f, F, g, G" "%llf warning" } */
   /* This should get a message referring to 'size_t', not to
      'unsigned int' or similar.  */
-  printf ("%zu", d); /* { dg-warning "size_t" "size_t format warning" } */
+  printf ("%zu", d); /* { dg-message "size_t|: a, A, e, E, f, F, g, G" "size_t format warning" } */
 }
Index: gcc/testsuite/gcc.dg/format/c99-printf-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/c99-printf-1.c	(revision 127669)
+++ gcc/testsuite/gcc.dg/format/c99-printf-1.c	(working copy)
@@ -192,6 +192,6 @@ foo (int i, unsigned int u, double d, ch
      by the standard, but a bad idea, so GCC should diagnose if what
      is used is not signed char *.)
   */
-  printf ("%hhn", s); /* { dg-warning "format" "%hhn plain char" } */
+  printf ("%hhn", s); /* { dg-message "format|: s" "%hhn plain char" } */
   printf ("%hhn", us); /* { dg-warning "format" "%hhn unsigned char" } */
 }
Index: gcc/testsuite/gcc.dg/format/c99-printf-3.c
===================================================================
--- gcc/testsuite/gcc.dg/format/c99-printf-3.c	(revision 127669)
+++ gcc/testsuite/gcc.dg/format/c99-printf-3.c	(working copy)
@@ -12,16 +12,16 @@ foo (int i, char *s, size_t n, va_list v
      va_list v4, va_list v5, va_list v6, va_list v7)
 {
   fprintf (stdout, "%d", i);
-  fprintf (stdout, "%ld", i); /* { dg-warning "format" "fprintf" } */
+  fprintf (stdout, "%ld", i); /* { dg-message "format|: d, i" "fprintf" } */
   printf ("%d", i);
-  printf ("%ld", i); /* { dg-warning "format" "printf" } */
+  printf ("%ld", i); /* { dg-message "format|: d, i" "printf" } */
   /* The "unlocked" functions shouldn't warn in c99 mode.  */
   fprintf_unlocked (stdout, "%ld", i); /* { dg-bogus "format" "fprintf_unlocked" } */
   printf_unlocked ("%ld", i); /* { dg-bogus "format" "printf_unlocked" } */
   sprintf (s, "%d", i);
-  sprintf (s, "%ld", i); /* { dg-warning "format" "sprintf" } */
+  sprintf (s, "%ld", i); /* { dg-message "format|: d, i" "sprintf" } */
   snprintf (s, n, "%d", i);
-  snprintf (s, n, "%ld", i); /* { dg-warning "format" "snprintf" } */
+  snprintf (s, n, "%ld", i); /* { dg-message "format|: d, i" "snprintf" } */
   vfprintf (stdout, "%d", v0);
   vfprintf (stdout, "%Y", v1); /* { dg-warning "format" "vfprintf" } */
   vprintf ("%d", v0);
Index: gcc/testsuite/gcc.dg/format/array-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/array-1.c	(revision 127669)
+++ gcc/testsuite/gcc.dg/format/array-1.c	(working copy)
@@ -23,18 +23,18 @@ foo (int i, long l)
   static const char q2[] = "bar%d";
   printf (a1);
   printf (a2, i);
-  printf (a2, l); /* { dg-warning "format" "wrong type with array" } */
+  printf (a2, l); /* { dg-message "format|: ld, li" } */
   printf (b1); /* { dg-warning "unterminated" "unterminated array" } */
   printf (b2); /* { dg-warning "unterminated" "unterminated array" } */
   printf (c1);
   printf (c2, i);
-  printf (c2, l); /* { dg-warning "format" "wrong type with array" } */
+  printf (c2, l); /* { dg-message "format|: ld, li" } */
   printf (p1);
   printf (p2, i);
-  printf (p2, l); /* { dg-warning "format" "wrong type with array" } */
+  printf (p2, l); /* { dg-message "format|: ld, li" } */
   printf (q1);
   printf (q2, i);
-  printf (q2, l); /* { dg-warning "format" "wrong type with array" } */
+  printf (q2, l); /* { dg-message "format|: ld, li" } */
   /* Volatile or non-constant arrays must not be checked.  */
   printf (d); /* { dg-warning "not a string literal" "non-const" } */
   printf ((const char *)e); /* { dg-warning "not a string literal" "volatile" } */
Index: gcc/testsuite/gcc.dg/format/multattr-3.c
===================================================================
--- gcc/testsuite/gcc.dg/format/multattr-3.c	(revision 127669)
+++ gcc/testsuite/gcc.dg/format/multattr-3.c	(working copy)
@@ -13,16 +13,16 @@ void
 foo (long l, int nfoo)
 {
   printf (ngettext ("%d foo", "%d foos", nfoo), nfoo);
-  printf (ngettext ("%d foo", "%d foos", l), l); /* { dg-warning "int" "wrong type in conditional expr" } */
-  printf (ngettext ("%d foo", "%ld foos", l), l); /* { dg-warning "int" "wrong type in conditional expr" } */
-  printf (ngettext ("%ld foo", "%d foos", l), l); /* { dg-warning "int" "wrong type in conditional expr" } */
+  printf (ngettext ("%d foo", "%d foos", l), l); /* { dg-message "int|: ld, li" "wrong type in conditional expr" } */
+  printf (ngettext ("%d foo", "%ld foos", l), l); /* { dg-message "int|: ld, li" "wrong type in conditional expr" } */
+  printf (ngettext ("%ld foo", "%d foos", l), l); /* { dg-message "int|: ld, li" "wrong type in conditional expr" } */
   /* Should allow one case to have extra arguments.  */
   printf (ngettext ("1 foo", "%d foos", nfoo), nfoo);
   printf (ngettext ("1 foo", "many foos", nfoo), nfoo); /* { dg-warning "too many" "too many args in all branches" } */
   printf (ngettext ("", "%d foos", nfoo), nfoo);
   printf (ngettext ("1 foo", (nfoo > 0) ? "%d foos" : "no foos", nfoo), nfoo);
   printf (ngettext ("%d foo", (nfoo > 0) ? "%d foos" : "no foos", nfoo), nfoo);
-  printf (ngettext ("%ld foo", (nfoo > 0) ? "%d foos" : "no foos", nfoo), nfoo); /* { dg-warning "long int" "wrong type" } */
-  printf (ngettext ("%d foo", (nfoo > 0) ? "%ld foos" : "no foos", nfoo), nfoo); /* { dg-warning "long int" "wrong type" } */
-  printf (ngettext ("%d foo", (nfoo > 0) ? "%d foos" : "%ld foos", nfoo), nfoo); /* { dg-warning "long int" "wrong type" } */
+  printf (ngettext ("%ld foo", (nfoo > 0) ? "%d foos" : "no foos", nfoo), nfoo); /* { dg-message "long int|: d, i" "wrong type" } */
+  printf (ngettext ("%d foo", (nfoo > 0) ? "%ld foos" : "no foos", nfoo), nfoo); /* { dg-message "long int|: d, i" "wrong type" } */
+  printf (ngettext ("%d foo", (nfoo > 0) ? "%d foos" : "%ld foos", nfoo), nfoo); /* { dg-message "long int|: d, i" "wrong type" } */
 }
Index: gcc/testsuite/gcc.dg/format/ext-suggest-2.c
===================================================================
--- gcc/testsuite/gcc.dg/format/ext-suggest-2.c	(revision 0)
+++ gcc/testsuite/gcc.dg/format/ext-suggest-2.c	(revision 0)
@@ -0,0 +1,56 @@
+/* Copyright (C) 2007 Free Software Foundation.  */
+/* Contributed by Dan Hipschman <dsh@google.com>.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu89 -Wformat" } */
+
+#include "format.h"
+
+void
+foo (signed char hhd, unsigned char c, short hd, unsigned short hu, size_t zu,
+     ssize_t zd, ptrdiff_t td, void *p, float f, long double Lf, const char *s,
+     const void *p2)
+{
+  char a[2] = " ";
+  char *td1 = &a[1], *td2 = &a[0];
+  int n;
+  long ln;
+  long long lln;
+  printf ("%p", hhd); /* { dg-message "expects|: d, i" } */
+  printf ("%p", c); /* { dg-message "expects|: c" } */
+  printf ("%p", a[0]); /* { dg-message "expects|: d, i" } */
+  printf ("%p", hd); /* { dg-message "expects|: hd, hi" } */
+  printf ("%p", hu); /* { dg-message "expects|: ho, hu, hx, hX" } */
+  printf ("%p", 0); /* { dg-message "expects|: d, i" } */
+  printf ("%p", 0u); /* { dg-message "expects|: o, u, x, X" } */
+  printf ("%p", 0L); /* { dg-message "expects|: ld, li" } */
+  printf ("%p", 0uL); /* { dg-message "expects|: lo, lu, lx, lX" } */
+  printf ("%p", 0LL); /* { dg-warning "expects" } */
+  printf ("%d", 0L); /* { dg-message "expects|: ld, li" } */
+  printf ("%u", 0uL); /* { dg-message "expects|: lo, lu, lx, lX" } */
+  printf ("%p", 0uLL); /* { dg-warning "expects" } */
+  printf ("%p", zu); /* { dg-message "expects|: l?o, l?u, l?x, l?X" } */
+  printf ("%p", sizeof 0); /* { dg-message "expects|: l?o, l?u, l?x, l?X" } */
+  printf ("%p", zd); /* { dg-message "expects|: l?d, l?i" } */
+  printf ("%p", td); /* { dg-message "expects|: l?d, l?i" } */
+  printf ("%p", td1 - td2); /* { dg-message "expects|: l?d, l?i" } */
+  printf ("%d", a); /* { dg-message "expects|: s" } */
+  printf ("%c", "foo"); /* { dg-message "expects|: s" } */
+  printf ("%d", s); /* { dg-message "expects|: s" } */
+  printf ("%s", L"foo"); /* { dg-warning "expects" } */
+  printf ("%c", L'x'); /* { dg-message "expects|: l?d, l?i" } */
+  printf ("%d", p); /* { dg-message "expects|: p" } */
+  printf ("%x", p2); /* { dg-message "expects|: p" } */
+  printf ("%d", f); /* { dg-message "expects|: e, E, f, g, G" } */
+  printf ("%d", 0.0); /* { dg-message "expects|: e, E, f, g, G" } */
+  printf ("%f", Lf); /* { dg-warning "expects|: Le, LE, Lf, Lg, LG" } */
+  printf ("%f", &hhd); /* { dg-warning "expects" } */
+  printf ("%f", (const signed char *) &hhd); /* { dg-warning "expects" } */
+  printf ("%f", &hd); /* { dg-message "expects|: hn" } */
+  printf ("%f", (const short *) &hd); /* { dg-warning "expects" } */
+  printf ("%f", &n); /* { dg-message "expects|: n" } */
+  printf ("%f", (const int *) &n); /* { dg-warning "expects" } */
+  printf ("%f", &ln); /* { dg-message "expects|: ln" } */
+  printf ("%f", (const long *) &ln); /* { dg-warning "expects" } */
+  printf ("%f", &lln); /* { dg-warning "expects" } */
+  printf ("%f", (const long long *) &lln); /* { dg-warning "expects" } */
+}
Index: gcc/testsuite/gcc.dg/format/typedef-suggest-2.c
===================================================================
--- gcc/testsuite/gcc.dg/format/typedef-suggest-2.c	(revision 0)
+++ gcc/testsuite/gcc.dg/format/typedef-suggest-2.c	(revision 0)
@@ -0,0 +1,20 @@
+/* Copyright (C) 2007 Free Software Foundation.  */
+/* Contributed by Dan Hipschman <dsh@google.com>.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu99 -Wformat" } */
+
+extern void foo (const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+
+typedef __SIZE_TYPE__ size_t;
+typedef size_t foo_t;
+typedef size_t ptrdiff_t;
+typedef typeof (sizeof 0) ssize_t;
+
+void
+bar (foo_t z1, ptrdiff_t z2, ssize_t z3, typeof (sizeof 0) z4)
+{
+  foo ("%n", z1); /* { dg-message "expects|: zo, zu, zx, zX" } */
+  foo ("%n", z2); /* { dg-message "expects|: zo, zu, zx, zX" } */
+  foo ("%n", z3); /* { dg-message "expects|: zo, zu, zx, zX" } */
+  foo ("%n", z4); /* { dg-message "expects|: zo, zu, zx, zX" } */
+}
Index: gcc/testsuite/gcc.dg/format/c90-printf-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/c90-printf-1.c	(revision 127669)
+++ gcc/testsuite/gcc.dg/format/c90-printf-1.c	(working copy)
@@ -176,13 +176,13 @@ foo (int i, int i1, int i2, unsigned int
   printf ("%-08g", d); /* { dg-warning "flags|ignored" "0 flag ignored with - flag" } */
   printf ("%-08G", d); /* { dg-warning "flags|ignored" "0 flag ignored with - flag" } */
   /* Various tests of bad argument types.  */
-  printf ("%d", l); /* { dg-warning "format" "bad argument types" } */
+  printf ("%d", l); /* { dg-message "format|: ld, li" "bad argument types" } */
   printf ("%*.*d", l, i2, i); /* { dg-warning "field" "bad * argument types" } */
   printf ("%*.*d", i1, l, i); /* { dg-warning "field" "bad * argument types" } */
-  printf ("%ld", i); /* { dg-warning "format" "bad argument types" } */
-  printf ("%s", n); /* { dg-warning "format" "bad argument types" } */
-  printf ("%p", i); /* { dg-warning "format" "bad argument types" } */
-  printf ("%n", p); /* { dg-warning "format" "bad argument types" } */
+  printf ("%ld", i); /* { dg-message "format|: d, i" "bad argument types" } */
+  printf ("%s", n); /* { dg-message "format|: n" "bad argument types" } */
+  printf ("%p", i); /* { dg-message "format|: d, i" "bad argument types" } */
+  printf ("%n", p); /* { dg-message "format|: p" "bad argument types" } */
   /* With -pedantic, we want some further checks for pointer targets:
      %p should allow only pointers to void (possibly qualified) and
      to character types (possibly qualified), but not function pointers
@@ -198,7 +198,7 @@ foo (int i, int i1, int i2, unsigned int
   */
   printf ("%p", foo); /* { dg-warning "format" "bad argument types" } */
   printf ("%n", un); /* { dg-warning "format" "bad argument types" } */
-  printf ("%p", n); /* { dg-warning "format" "bad argument types" } */
+  printf ("%p", n); /* { dg-message "format|: n" "bad argument types" } */
   /* Allow character pointers with %p.  */
   printf ("%p%p%p%p", s, ss, us, css);
   /* %s allows any character type.  */
@@ -207,7 +207,7 @@ foo (int i, int i1, int i2, unsigned int
      and seems useful to keep, even if some standard versions might be
      read to permit it.
   */
-  printf ("%s", p); /* { dg-warning "format" "bad argument types" } */
+  printf ("%s", p); /* { dg-message "format|: p" "bad argument types" } */
   /* The historical behavior is to allow signed / unsigned types
      interchangably as arguments.  For values representable in both types,
      such usage may be correct.  For now preserve the behavior of GCC
Index: gcc/testsuite/gcc.dg/format/c90-printf-3.c
===================================================================
--- gcc/testsuite/gcc.dg/format/c90-printf-3.c	(revision 127669)
+++ gcc/testsuite/gcc.dg/format/c90-printf-3.c	(working copy)
@@ -13,14 +13,14 @@ foo (int i, char *s, size_t n, va_list v
      va_list v4, va_list v5, va_list v6, va_list v7, va_list v8)
 {
   fprintf (stdout, "%d", i);
-  fprintf (stdout, "%ld", i); /* { dg-warning "format" "fprintf" } */
+  fprintf (stdout, "%ld", i); /* { dg-message "format|: d, i" "fprintf" } */
   printf ("%d", i);
-  printf ("%ld", i); /* { dg-warning "format" "printf" } */
+  printf ("%ld", i); /* { dg-message "format|: d, i" "printf" } */
   /* The "unlocked" functions shouldn't warn in c90 mode.  */
   fprintf_unlocked (stdout, "%ld", i); /* { dg-bogus "format" "fprintf_unlocked" } */
   printf_unlocked ("%ld", i); /* { dg-bogus "format" "printf_unlocked" } */
   sprintf (s, "%d", i);
-  sprintf (s, "%ld", i); /* { dg-warning "format" "sprintf" } */
+  sprintf (s, "%ld", i); /* { dg-message "format|: d, i" "sprintf" } */
   vfprintf (stdout, "%d", v0);
   vfprintf (stdout, "%Y", v1); /* { dg-warning "format" "vfprintf" } */
   vprintf ("%d", v2);
Index: gcc/testsuite/gcc.dg/format/ext-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/ext-1.c	(revision 127669)
+++ gcc/testsuite/gcc.dg/format/ext-1.c	(working copy)
@@ -120,7 +120,7 @@ foo (quad_t q, u_quad_t uq, quad_t *qn, 
   /* As an extension, GCC does format checking on "unlocked"
      i.e. thread unsafe versions of these functions.  */
   fprintf_unlocked (stdout, "%d", i);
-  fprintf_unlocked (stdout, "%ld", i); /* { dg-warning "format" "fprintf_unlocked" } */
+  fprintf_unlocked (stdout, "%ld", i); /* { dg-message "format|: d, i" "fprintf_unlocked" } */
   printf_unlocked ("%d", i);
-  printf_unlocked ("%ld", i); /* { dg-warning "format" "printf_unlocked" } */
+  printf_unlocked ("%ld", i); /* { dg-message "format|: d, i" "printf_unlocked" } */
 }
Index: gcc/testsuite/gcc.dg/format/ext-5.c
===================================================================
--- gcc/testsuite/gcc.dg/format/ext-5.c	(revision 127669)
+++ gcc/testsuite/gcc.dg/format/ext-5.c	(working copy)
@@ -9,9 +9,9 @@ void
 foo (int i, long l)
 {
   printf (gettext ("%d"), i);
-  printf (gettext ("%ld"), i); /* { dg-warning "format" "gettext" } */
+  printf (gettext ("%ld"), i); /* { dg-message "format|: d, i" "gettext" } */
   printf (dgettext ("", "%d"), i);
-  printf (dgettext ("", "%ld"), i); /* { dg-warning "format" "dgettext" } */
+  printf (dgettext ("", "%ld"), i); /* { dg-message "format|: d, i" "dgettext" } */
   printf (dcgettext ("", "%d", 0), i);
-  printf (dcgettext ("", "%ld", 0), i); /* { dg-warning "format" "dcgettext" } */
+  printf (dcgettext ("", "%ld", 0), i); /* { dg-message "format|: d, i" "dcgettext" } */
 }
Index: gcc/testsuite/gcc.dg/format/va-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/va-1.c	(revision 127669)
+++ gcc/testsuite/gcc.dg/format/va-1.c	(working copy)
@@ -9,5 +9,5 @@ void
 foo (void *p)
 {
   printf ("%d", p); /* { dg-bogus "va_list" "wrong type in format warning" } */
-  /* { dg-warning "format" "format error" { target *-*-* } 11 } */
+  /* { dg-message "format|: p" "format error" { target *-*-* } 11 } */
 }
Index: gcc/testsuite/gcc.dg/format/dfp-printf-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/dfp-printf-1.c	(revision 127669)
+++ gcc/testsuite/gcc.dg/format/dfp-printf-1.c	(working copy)
@@ -42,44 +42,44 @@ foo (_Decimal32 x, _Decimal64 y, _Decima
 
   /* Check warnings for type mismatches.  */
 
-  printf ("%Hf\n", y);	/* { dg-warning "expects type" "bad use of %H" } */
-  printf ("%HF\n", y);	/* { dg-warning "expects type" "bad use of %H" } */
-  printf ("%He\n", y);	/* { dg-warning "expects type" "bad use of %H" } */
-  printf ("%HE\n", y);	/* { dg-warning "expects type" "bad use of %H" } */
-  printf ("%Hg\n", y);	/* { dg-warning "expects type" "bad use of %H" } */
-  printf ("%HG\n", y);	/* { dg-warning "expects type" "bad use of %H" } */
-  printf ("%Hf\n", z);	/* { dg-warning "expects type" "bad use of %H" } */
-  printf ("%HF\n", z);	/* { dg-warning "expects type" "bad use of %H" } */
-  printf ("%He\n", z);	/* { dg-warning "expects type" "bad use of %H" } */
-  printf ("%HE\n", z);	/* { dg-warning "expects type" "bad use of %H" } */
-  printf ("%Hg\n", z);	/* { dg-warning "expects type" "bad use of %H" } */
-  printf ("%HG\n", z);	/* { dg-warning "expects type" "bad use of %H" } */
-
-  printf ("%Df\n", x);	/* { dg-warning "expects type" "bad use of %D" } */
-  printf ("%DF\n", x);	/* { dg-warning "expects type" "bad use of %D" } */
-  printf ("%De\n", x);	/* { dg-warning "expects type" "bad use of %D" } */
-  printf ("%DE\n", x);	/* { dg-warning "expects type" "bad use of %D" } */
-  printf ("%Dg\n", x);	/* { dg-warning "expects type" "bad use of %D" } */
-  printf ("%DG\n", x);	/* { dg-warning "expects type" "bad use of %D" } */
-  printf ("%Df\n", z);	/* { dg-warning "expects type" "bad use of %D" } */
-  printf ("%DF\n", z);	/* { dg-warning "expects type" "bad use of %D" } */
-  printf ("%De\n", z);	/* { dg-warning "expects type" "bad use of %D" } */
-  printf ("%DE\n", z);	/* { dg-warning "expects type" "bad use of %D" } */
-  printf ("%Dg\n", z);	/* { dg-warning "expects type" "bad use of %D" } */
-  printf ("%DG\n", z);	/* { dg-warning "expects type" "bad use of %D" } */
-
-  printf ("%DDf\n", x);	/* { dg-warning "expects type" "bad use of %DD" } */
-  printf ("%DDF\n", x);	/* { dg-warning "expects type" "bad use of %DD" } */
-  printf ("%DDe\n", x);	/* { dg-warning "expects type" "bad use of %DD" } */
-  printf ("%DDE\n", x);	/* { dg-warning "expects type" "bad use of %DD" } */
-  printf ("%DDg\n", x);	/* { dg-warning "expects type" "bad use of %DD" } */
-  printf ("%DDG\n", x);	/* { dg-warning "expects type" "bad use of %DD" } */
-  printf ("%DDf\n", y);	/* { dg-warning "expects type" "bad use of %DD" } */
-  printf ("%DDF\n", y);	/* { dg-warning "expects type" "bad use of %DD" } */
-  printf ("%DDe\n", y);	/* { dg-warning "expects type" "bad use of %DD" } */
-  printf ("%DDE\n", y);	/* { dg-warning "expects type" "bad use of %DD" } */
-  printf ("%DDg\n", y);	/* { dg-warning "expects type" "bad use of %DD" } */
-  printf ("%DDG\n", y);	/* { dg-warning "expects type" "bad use of %DD" } */
+  printf ("%Hf\n", y);	/* { dg-message "expects type|: De, DE, Df, DF, Dg, DG" "bad use of %H" } */
+  printf ("%HF\n", y);	/* { dg-message "expects type|: De, DE, Df, DF, Dg, DG" "bad use of %H" } */
+  printf ("%He\n", y);	/* { dg-message "expects type|: De, DE, Df, DF, Dg, DG" "bad use of %H" } */
+  printf ("%HE\n", y);	/* { dg-message "expects type|: De, DE, Df, DF, Dg, DG" "bad use of %H" } */
+  printf ("%Hg\n", y);	/* { dg-message "expects type|: De, DE, Df, DF, Dg, DG" "bad use of %H" } */
+  printf ("%HG\n", y);	/* { dg-message "expects type|: De, DE, Df, DF, Dg, DG" "bad use of %H" } */
+  printf ("%Hf\n", z);	/* { dg-message "expects type|: DDe, DDE, DDf, DDF, DDg, DDG" "bad use of %H" } */
+  printf ("%HF\n", z);	/* { dg-message "expects type|: DDe, DDE, DDf, DDF, DDg, DDG" "bad use of %H" } */
+  printf ("%He\n", z);	/* { dg-message "expects type|: DDe, DDE, DDf, DDF, DDg, DDG" "bad use of %H" } */
+  printf ("%HE\n", z);	/* { dg-message "expects type|: DDe, DDE, DDf, DDF, DDg, DDG" "bad use of %H" } */
+  printf ("%Hg\n", z);	/* { dg-message "expects type|: DDe, DDE, DDf, DDF, DDg, DDG" "bad use of %H" } */
+  printf ("%HG\n", z);	/* { dg-message "expects type|: DDe, DDE, DDf, DDF, DDg, DDG" "bad use of %H" } */
+
+  printf ("%Df\n", x);	/* { dg-message "expects type|: He, HE, Hf, HF, Hg, HG" "bad use of %D" } */
+  printf ("%DF\n", x);	/* { dg-message "expects type|: He, HE, Hf, HF, Hg, HG" "bad use of %D" } */
+  printf ("%De\n", x);	/* { dg-message "expects type|: He, HE, Hf, HF, Hg, HG" "bad use of %D" } */
+  printf ("%DE\n", x);	/* { dg-message "expects type|: He, HE, Hf, HF, Hg, HG" "bad use of %D" } */
+  printf ("%Dg\n", x);	/* { dg-message "expects type|: He, HE, Hf, HF, Hg, HG" "bad use of %D" } */
+  printf ("%DG\n", x);	/* { dg-message "expects type|: He, HE, Hf, HF, Hg, HG" "bad use of %D" } */
+  printf ("%Df\n", z);	/* { dg-message "expects type|: DDe, DDE, DDf, DDF, DDg, DDG" "bad use of %D" } */
+  printf ("%DF\n", z);	/* { dg-message "expects type|: DDe, DDE, DDf, DDF, DDg, DDG" "bad use of %D" } */
+  printf ("%De\n", z);	/* { dg-message "expects type|: DDe, DDE, DDf, DDF, DDg, DDG" "bad use of %D" } */
+  printf ("%DE\n", z);	/* { dg-message "expects type|: DDe, DDE, DDf, DDF, DDg, DDG" "bad use of %D" } */
+  printf ("%Dg\n", z);	/* { dg-message "expects type|: DDe, DDE, DDf, DDF, DDg, DDG" "bad use of %D" } */
+  printf ("%DG\n", z);	/* { dg-message "expects type|: DDe, DDE, DDf, DDF, DDg, DDG" "bad use of %D" } */
+
+  printf ("%DDf\n", x);	/* { dg-message "expects type|: He, HE, Hf, HF, Hg, HG" "bad use of %DD" } */
+  printf ("%DDF\n", x);	/* { dg-message "expects type|: He, HE, Hf, HF, Hg, HG" "bad use of %DD" } */
+  printf ("%DDe\n", x);	/* { dg-message "expects type|: He, HE, Hf, HF, Hg, HG" "bad use of %DD" } */
+  printf ("%DDE\n", x);	/* { dg-message "expects type|: He, HE, Hf, HF, Hg, HG" "bad use of %DD" } */
+  printf ("%DDg\n", x);	/* { dg-message "expects type|: He, HE, Hf, HF, Hg, HG" "bad use of %DD" } */
+  printf ("%DDG\n", x);	/* { dg-message "expects type|: He, HE, Hf, HF, Hg, HG" "bad use of %DD" } */
+  printf ("%DDf\n", y);	/* { dg-message "expects type|: De, DE, Df, DF, Dg, DG" "bad use of %DD" } */
+  printf ("%DDF\n", y);	/* { dg-message "expects type|: De, DE, Df, DF, Dg, DG" "bad use of %DD" } */
+  printf ("%DDe\n", y);	/* { dg-message "expects type|: De, DE, Df, DF, Dg, DG" "bad use of %DD" } */
+  printf ("%DDE\n", y);	/* { dg-message "expects type|: De, DE, Df, DF, Dg, DG" "bad use of %DD" } */
+  printf ("%DDg\n", y);	/* { dg-message "expects type|: De, DE, Df, DF, Dg, DG" "bad use of %DD" } */
+  printf ("%DDG\n", y);	/* { dg-message "expects type|: De, DE, Df, DF, Dg, DG" "bad use of %DD" } */
 
   /* Check for warnings for bad use of H, D, and DD length specifiers.  */
 
Index: gcc/testsuite/gcc.dg/format/unnamed-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/unnamed-1.c	(revision 127669)
+++ gcc/testsuite/gcc.dg/format/unnamed-1.c	(working copy)
@@ -18,7 +18,7 @@
 void
 f (TItype x)
 {
-  printf("%d", x); /* { dg-warning "expects type" } */
+  printf("%d", x); /* { dg-message "expects type|: ld, li" } */
   printf("%d", 141592653589793238462643383279502884197169399375105820974944); /* { dg-warning "expects type" } */
   /* { dg-warning "unsigned only|too large" "constant" { target *-*-* } 22 } */
 }
Index: gcc/testsuite/gcc.dg/format/dfp-suggest-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/dfp-suggest-1.c	(revision 0)
+++ gcc/testsuite/gcc.dg/format/dfp-suggest-1.c	(revision 0)
@@ -0,0 +1,15 @@
+/* Copyright (C) 2007 Free Software Foundation.  */
+/* Contributed by Dan Hipschman <dsh@google.com>.  */
+/* { dg-do compile } */
+/* { dg-require-effective-target dfp } */
+/* { dg-options "-std=gnu89 -Wformat" } */
+
+#include "format.h"
+
+void
+foo (_Decimal32 Hf, _Decimal64 Df, _Decimal128 DDf)
+{
+  printf ("%f", Hf); /* { dg-message "expects|: He, HE, Hf, HF, Hg, HG" } */
+  printf ("%f", Df); /* { dg-message "expects|: De, DE, Df, DF, Dg, DG" } */
+  printf ("%f", DDf); /* { dg-message "expects|: DDe, DDE, DDf, DDF, DDg, DDG" } */
+}
Index: gcc/c-format.c
===================================================================
--- gcc/c-format.c	(revision 127669)
+++ gcc/c-format.c	(working copy)
@@ -267,6 +267,8 @@ typedef struct format_wanted_type
   /* Whether the argument, dereferenced once, is read from and so
      must not be a NULL pointer.  */
   int reading_from_flag;
+  /* Format conversion suggestions, if available, otherwise NULL.  */
+  format_suggestions *suggestions;
   /* If warnings should be of the form "field precision should have
      type 'int'", the name to use (in this case "field precision"),
      otherwise NULL, for "format expects type 'long'" type
@@ -498,6 +500,43 @@ static const format_flag_pair strfmon_fl
   { 0, 0, 0, 0 }
 };
 
+static format_suggestions printf_suggestions[] =
+{
+  { &wchar_type_node, "wchar_t", FMT_SUG_POINTER, STD_C94, "ls" },
+  { &wchar_type_node, "wchar_t", 0, STD_C94, "lc" },
+  { &wint_type_node, "wint_t", 0, STD_C94, "lc" },
+  { &signed_size_type_node, "ssize_t", 0, STD_C99, "zd, zi" },
+  { &size_type_node, "size_t", 0, STD_C99, "zo, zu, zx, zX" },
+  { &ptrdiff_type_node, "ptrdiff_t", 0, STD_C99, "td, ti" },
+  { &intmax_type_node, "intmax_t", 0, STD_C99, "jd, ji" },
+  { &uintmax_type_node, "uintmax_t", 0, STD_C99, "jo, ju, jx, jX" },
+  { &dfloat32_type_node, "_Decimal32", 0, STD_EXT, "He, HE, Hf, HF, Hg, HG" },
+  { &dfloat64_type_node, "_Decimal64", 0, STD_EXT, "De, DE, Df, DF, Dg, DG" },
+  { &dfloat128_type_node, "_Decimal128", 0, STD_EXT, "DDe, DDE, DDf, DDF, DDg, DDG" },
+  { &unsigned_char_type_node, NULL, 0, STD_C99, "c, hho, hhu, hhx, hhX" },
+  { &unsigned_char_type_node, NULL, 0, STD_C89, "c" },
+  { &signed_char_type_node, NULL, 0, STD_C99, "hhd, hhi" },
+  { &short_unsigned_type_node, NULL, 0, STD_C89, "ho, hu, hx, hX" },
+  { &short_integer_type_node, NULL, 0, STD_C89, "hd, hi" },
+  { &unsigned_type_node, NULL, 0, STD_C89, "o, u, x, X" },
+  { &integer_type_node, NULL, 0, STD_C89, "d, i" },
+  { &long_integer_type_node, NULL, 0, STD_C89, "ld, li" },
+  { &long_long_integer_type_node, NULL, 0, STD_C9L, "lld, lli" },
+  { &char_type_node, NULL, FMT_SUG_POINTER, STD_C89, "s" },
+  { &long_unsigned_type_node, NULL, 0, STD_C89, "lo, lu, lx, lX" },
+  { &long_long_unsigned_type_node, NULL, 0, STD_C9L, "llo, llu, llx, llX" },
+  { &double_type_node, NULL, 0, STD_C99, "a, A, e, E, f, F, g, G" },
+  { &long_double_type_node, NULL, 0, STD_C99, "La, LA, Le, LE, Lf, LF, Lg, LG" },
+  { &double_type_node, NULL, 0, STD_C89, "e, E, f, g, G" },
+  { &long_double_type_node, NULL, 0, STD_C89, "Le, LE, Lf, Lg, LG" },
+  { &signed_char_type_node, NULL, FMT_SUG_POINTER|FMT_SUG_WRITE, STD_C99, "hhn" },
+  { &short_integer_type_node, NULL, FMT_SUG_POINTER|FMT_SUG_WRITE, STD_C89, "hn" },
+  { &integer_type_node, NULL, FMT_SUG_POINTER|FMT_SUG_WRITE, STD_C89, "n" },
+  { &long_integer_type_node, NULL, FMT_SUG_POINTER|FMT_SUG_WRITE, STD_C89, "ln" },
+  { &long_long_integer_type_node, NULL, FMT_SUG_POINTER|FMT_SUG_WRITE, STD_C9L, "lln" },
+  { &void_type_node, NULL, FMT_SUG_POINTER, STD_C89, "p" },
+  { NULL, NULL, 0, 0, NULL }
+};
 
 static const format_char_info print_char_table[] =
 {
@@ -713,60 +752,60 @@ static const format_char_info monetary_c
 static const format_kind_info format_types_orig[] =
 {
   { "printf",   printf_length_specs,  print_char_table, " +#0-'I", NULL,
-    printf_flag_specs, printf_flag_pairs,
+    printf_flag_specs, printf_flag_pairs, printf_suggestions,
     FMT_FLAG_ARG_CONVERT|FMT_FLAG_DOLLAR_MULTIPLE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_EMPTY_PREC_OK,
     'w', 0, 'p', 0, 'L',
     &integer_type_node, &integer_type_node
   },
   { "asm_fprintf",   asm_fprintf_length_specs,  asm_fprintf_char_table, " +#0-", NULL,
-    asm_fprintf_flag_specs, asm_fprintf_flag_pairs,
+    asm_fprintf_flag_specs, asm_fprintf_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT|FMT_FLAG_EMPTY_PREC_OK,
     'w', 0, 'p', 0, 'L',
     NULL, NULL
   },
   { "gcc_diag",   gcc_diag_length_specs,  gcc_diag_char_table, "q+", NULL,
-    gcc_diag_flag_specs, gcc_diag_flag_pairs,
+    gcc_diag_flag_specs, gcc_diag_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT,
     0, 0, 'p', 0, 'L',
     NULL, &integer_type_node
   },
   { "gcc_tdiag",   gcc_tdiag_length_specs,  gcc_tdiag_char_table, "q+", NULL,
-    gcc_tdiag_flag_specs, gcc_tdiag_flag_pairs,
+    gcc_tdiag_flag_specs, gcc_tdiag_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT,
     0, 0, 'p', 0, 'L',
     NULL, &integer_type_node
   },
   { "gcc_cdiag",   gcc_cdiag_length_specs,  gcc_cdiag_char_table, "q+", NULL,
-    gcc_cdiag_flag_specs, gcc_cdiag_flag_pairs,
+    gcc_cdiag_flag_specs, gcc_cdiag_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT,
     0, 0, 'p', 0, 'L',
     NULL, &integer_type_node
   },
   { "gcc_cxxdiag",   gcc_cxxdiag_length_specs,  gcc_cxxdiag_char_table, "q+#", NULL,
-    gcc_cxxdiag_flag_specs, gcc_cxxdiag_flag_pairs,
+    gcc_cxxdiag_flag_specs, gcc_cxxdiag_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT,
     0, 0, 'p', 0, 'L',
     NULL, &integer_type_node
   },
   { "gcc_gfc", gcc_gfc_length_specs, gcc_gfc_char_table, "", NULL,
-    NULL, gcc_gfc_flag_pairs,
+    NULL, gcc_gfc_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT,
     0, 0, 0, 0, 0,
     NULL, NULL
   },
   { "scanf",    scanf_length_specs,   scan_char_table,  "*'I", NULL,
-    scanf_flag_specs, scanf_flag_pairs,
+    scanf_flag_specs, scanf_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT|FMT_FLAG_SCANF_A_KLUDGE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_ZERO_WIDTH_BAD|FMT_FLAG_DOLLAR_GAP_POINTER_OK,
     'w', 0, 0, '*', 'L',
     NULL, NULL
   },
   { "strftime", NULL,                 time_char_table,  "_-0^#", "EO",
-    strftime_flag_specs, strftime_flag_pairs,
+    strftime_flag_specs, strftime_flag_pairs, NULL,
     FMT_FLAG_FANCY_PERCENT_OK, 'w', 0, 0, 0, 0,
     NULL, NULL
   },
   { "strfmon",  strfmon_length_specs, monetary_char_table, "=^+(!-", NULL,
-    strfmon_flag_specs, strfmon_flag_pairs,
+    strfmon_flag_specs, strfmon_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT, 'w', '#', 'p', 0, 'L',
     NULL, NULL
   }
@@ -834,7 +873,8 @@ static const format_flag_spec *get_flag_
 
 static void check_format_types (format_wanted_type *, const char *, int);
 static void format_type_warning (const char *, const char *, int, tree,
-				 int, const char *, tree, int);
+				 int, const char *, tree, int,
+				 format_suggestions *);
 
 /* Decode a format type from a string, returning the type, or
    format_type_error if not valid, in which case the caller should print an
@@ -1616,6 +1656,7 @@ check_format_info_main (format_check_res
 		  width_wanted_type.name = _("field width");
 		  width_wanted_type.param = cur_param;
 		  width_wanted_type.arg_num = arg_num;
+		  width_wanted_type.suggestions = NULL;
 		  width_wanted_type.next = NULL;
 		  if (last_wanted_type != 0)
 		    last_wanted_type->next = &width_wanted_type;
@@ -1718,6 +1759,7 @@ check_format_info_main (format_check_res
 		  precision_wanted_type.name = _("field precision");
 		  precision_wanted_type.param = cur_param;
 		  precision_wanted_type.arg_num = arg_num;
+		  precision_wanted_type.suggestions = NULL;
 		  precision_wanted_type.next = NULL;
 		  if (last_wanted_type != 0)
 		    last_wanted_type->next = &precision_wanted_type;
@@ -2077,6 +2119,7 @@ check_format_info_main (format_check_res
 	      wanted_type_ptr->name = NULL;
 	      wanted_type_ptr->param = cur_param;
 	      wanted_type_ptr->arg_num = arg_num;
+	      wanted_type_ptr->suggestions = fki->suggestions;
 	      wanted_type_ptr->next = NULL;
 	      if (last_wanted_type != 0)
 		last_wanted_type->next = wanted_type_ptr;
@@ -2113,6 +2156,15 @@ check_format_info_main (format_check_res
 }
 
 
+/* Check if an argument cannot be written to (for example, by the %n
+   conversion).  */
+static int
+arg_readonly (tree arg)
+{
+  return CONSTANT_CLASS_P (arg) || (DECL_P (arg) && TREE_READONLY (arg));
+}
+
+
 /* Check the argument types from a single format conversion (possibly
    including width and precision arguments).  */
 static void
@@ -2123,7 +2175,7 @@ check_format_types (format_wanted_type *
     {
       tree cur_param;
       tree cur_type;
-      tree orig_cur_type;
+      tree orig_cur_param;
       tree wanted_type;
       int arg_num;
       int i;
@@ -2132,7 +2184,7 @@ check_format_types (format_wanted_type *
       cur_type = TREE_TYPE (cur_param);
       if (cur_type == error_mark_node)
 	continue;
-      orig_cur_type = cur_type;
+      orig_cur_param = cur_param;
       char_type_flag = 0;
       wanted_type = types->wanted_type;
       arg_num = types->arg_num;
@@ -2187,10 +2239,7 @@ check_format_types (format_wanted_type *
 	      if (types->writing_in_flag
 		  && i == 0
 		  && (TYPE_READONLY (cur_type)
-		      || (cur_param != 0
-			  && (CONSTANT_CLASS_P (cur_param)
-			      || (DECL_P (cur_param)
-				  && TREE_READONLY (cur_param))))))
+		      || (cur_param != 0 && arg_readonly (cur_param))))
 		warning (OPT_Wformat, "writing into constant object "
 			 "(argument %d)", arg_num);
 
@@ -2211,8 +2260,8 @@ check_format_types (format_wanted_type *
 	    {
 	      format_type_warning (types->name, format_start, format_length,
 				   wanted_type, types->pointer_count,
-				   types->wanted_type_name, orig_cur_type,
-				   arg_num);
+				   types->wanted_type_name, orig_cur_param,
+				   arg_num, types->suggestions);
 	      break;
 	    }
 	}
@@ -2260,7 +2309,8 @@ check_format_types (format_wanted_type *
       /* Now we have a type mismatch.  */
       format_type_warning (types->name, format_start, format_length,
 			   wanted_type, types->pointer_count,
-			   types->wanted_type_name, orig_cur_type, arg_num);
+			   types->wanted_type_name, orig_cur_param, arg_num,
+			   types->suggestions);
     }
 }
 
@@ -2272,15 +2322,19 @@ check_format_types (format_wanted_type *
    FORMAT_LENGTH is its length.  WANTED_TYPE is the type the argument
    should have after POINTER_COUNT pointer dereferences.
    WANTED_NAME_NAME is a possibly more friendly name of WANTED_TYPE,
-   or NULL if the ordinary name of the type should be used.  ARG_TYPE
-   is the type of the actual argument.  ARG_NUM is the number of that
-   argument.  */
+   or NULL if the ordinary name of the type should be used.  ARG
+   is the actual argument.  ARG_NUM is the number of that
+   argument.  SUGGESTIONS is used to suggest format conversions that
+   are acceptable for the argument type provided.  It may be NULL.  */
 static void
 format_type_warning (const char *descr, const char *format_start,
 		     int format_length, tree wanted_type, int pointer_count,
-		     const char *wanted_type_name, tree arg_type, int arg_num)
+		     const char *wanted_type_name, tree arg, int arg_num,
+		     format_suggestions *suggestions)
 {
+  tree arg_type;
   char *p;
+  arg_type = TREE_TYPE (arg);
   /* If ARG_TYPE is a typedef with a misleading name (for example,
      size_t but not the standard size_t expected by printf %zu), avoid
      printing the typedef name.  */
@@ -2310,6 +2364,94 @@ format_type_warning (const char *descr, 
       memset (p + 1, '*', pointer_count);
       p[pointer_count + 1] = 0;
     }
+
+  if (suggestions)
+    {
+      tree type = TREE_TYPE (arg);
+      tree strp_arg = arg;
+      STRIP_NOPS (strp_arg);
+      for ( ; suggestions->type; ++suggestions)
+	{
+	  tree sug_type;
+	  tree test_type;
+	  if (C_STD_VER < ADJ_STD (suggestions->std)
+	      && (suggestions->std != STD_EXT || flag_iso))
+	    continue;
+	  if (suggestions->flags & FMT_SUG_POINTER)
+	    {
+	      if (TREE_CODE (type) == POINTER_TYPE)
+		test_type = TREE_TYPE (type);
+	      else
+		continue;
+	    }
+	  else
+	    test_type = type;
+	  if (suggestions->flags & FMT_SUG_WRITE)
+	    {
+	      gcc_assert (suggestions->flags & FMT_SUG_POINTER);
+	      if (TYPE_READONLY (test_type)
+		  || (TREE_CODE (strp_arg) == ADDR_EXPR
+		      && arg_readonly (TREE_OPERAND (strp_arg, 0))))
+		continue;
+	    }
+	  sug_type = *suggestions->type;
+	  if (suggestions->name)
+	    {
+	      tree named_type = test_type;
+	      int match = 0;
+	      while (named_type)
+		{
+		  if ((named_type == size_type_node
+		       || named_type == ptrdiff_type_node
+		       || named_type == wchar_type_node)
+		      && named_type == sug_type)
+		    {
+		      match = 2;
+		      break;
+		    }
+		  else
+		    {
+		      if (TYPE_NAME (named_type)
+			  && TREE_CODE (TYPE_NAME (named_type)) == TYPE_DECL)
+			{
+			  const char *type_name;
+			  type_name = get_name (TYPE_NAME (named_type));
+			  named_type
+			    = DECL_ORIGINAL_TYPE (TYPE_NAME (named_type));
+			  if (type_name
+			      && strcmp (type_name, suggestions->name) == 0)
+			    {
+			      match = 1;
+			      break;
+			    }
+			}
+		      else
+			break;
+		    }
+		}
+	      if (match == 2)
+		break;
+	      else if (match == 0)
+		continue;
+	    }
+	  test_type = TYPE_MAIN_VARIANT (test_type);
+	  if (lang_hooks.types_compatible_p (test_type, sug_type))
+	    break;
+	  if (!(suggestions->flags & FMT_SUG_POINTER))
+	    {
+	      tree pro_type = lang_hooks.types.type_promotes_to (sug_type);
+	      if (lang_hooks.types_compatible_p (pro_type, test_type)
+		  && (TREE_CODE (arg) == NOP_EXPR
+		      || TREE_CODE (arg) == CONVERT_EXPR))
+		{
+		  tree orig_arg_type = TREE_TYPE (TREE_OPERAND (arg, 0));
+		  if (lang_hooks.types_compatible_p (sug_type, orig_arg_type))
+		    break;
+		}
+	    }
+	}
+    }
+
   if (wanted_type_name)
     {
       if (descr)
@@ -2333,6 +2475,17 @@ format_type_warning (const char *descr, 
 		 "but argument %d has type %qT",
 		 format_length, format_start, wanted_type, p, arg_num, arg_type);
     }
+  if (suggestions && suggestions->type)
+    {
+      if (suggestions->name)
+	inform ("acceptable formats for type %<%s%s%> are: %s", suggestions->name,
+		suggestions->flags & FMT_SUG_POINTER ? " *" : "",
+		suggestions->desc);
+      else
+	inform ("acceptable formats for type %<%T%s%> are: %s", *suggestions->type,
+		suggestions->flags & FMT_SUG_POINTER ? " *" : "",
+		suggestions->desc);
+    }
 }
 
 
Index: gcc/c-format.h
===================================================================
--- gcc/c-format.h	(revision 127669)
+++ gcc/c-format.h	(working copy)
@@ -200,6 +200,33 @@ typedef struct
 } format_flag_pair;
 
 
+/* Flags used to specify types in format string suggestions.  */
+enum
+{
+  /* Set if the required type is a pointer to the type specified in the
+     format_suggestions structure.  */
+  FMT_SUG_POINTER = 1,
+  /* Set if the required type must be writable.  The FMT_SUG_POINTER flag
+     must also be set.  */
+  FMT_SUG_WRITE = 2
+};
+
+/* Structure mapping a type to format conversion suggestions.  */
+typedef struct
+{
+  /* The type wanted by the conversions.  */
+  tree *type;
+  /* A name for this type if it's typedef'd, otherwise NULL.  */
+  const char *name;
+  /* Flags specified by the FMT_SUG_ constants.  */
+  unsigned flags;
+  /* The standard to which the conversions belong.  */
+  enum format_std_version std;
+  /* The conversion suggestions for use with this type.  */
+  const char *desc;
+} format_suggestions;
+
+
 /* Structure describing a particular kind of format processed by GCC.  */
 typedef struct
 {
@@ -218,6 +245,8 @@ typedef struct
   const format_flag_spec *flag_specs;
   /* Details of bad combinations of flags.  */
   const format_flag_pair *bad_flag_pairs;
+  /* Suggestions for conversions based on type.  */
+  format_suggestions *suggestions;
   /* Flags applicable to this kind of format.  */
   int flags;
   /* Flag character to treat a width as, or 0 if width not used.  */
Index: gcc/c-typeck.c
===================================================================
--- gcc/c-typeck.c	(revision 127669)
+++ gcc/c-typeck.c	(working copy)
@@ -3597,6 +3597,12 @@ build_c_cast (tree type, tree expr)
 	      || TREE_CODE (type) == UNION_TYPE)
 	    pedwarn ("ISO C forbids casting nonscalar to the same type");
 	}
+      /* Add a conversion even if the main variants are the same since the
+	 types seen by the user may not be.  For example, if size_t has the
+	 same type as unsigned int, then "(unsigned) sizeof x" might be
+	 redundant, but for warning messages, the user wants the type to be
+	 thought of as	unsigned int.  */
+      value = convert (type, value);
     }
   else if (TREE_CODE (type) == UNION_TYPE)
     {
Index: gcc/c-common.c
===================================================================
--- gcc/c-common.c	(revision 127669)
+++ gcc/c-common.c	(working copy)
@@ -3671,6 +3671,19 @@ c_define_builtins (tree va_list_ref_type
     mudflap_init ();
 }
 
+
+/* Look up the type with identifier NAME and return a copy of it.  We want
+   some of the standard, but non-primitive, types, such as size_t, to be
+   copies of the types they represent, so we can still tell them apart.  */
+
+static tree
+copy_type_with_name (const char *name)
+{
+  tree id = get_identifier (name);
+  return build_variant_type_copy (TREE_TYPE (identifier_global_value (id)));
+}
+
+
 /* Build tree nodes and builtin functions common to both C and C++ language
    frontends.  */
 
@@ -3760,8 +3773,7 @@ c_common_nodes_and_builtins (void)
   /* `unsigned long' is the standard type for sizeof.
      Note that stddef.h uses `unsigned long',
      and this must agree, even if long and int are the same size.  */
-  size_type_node =
-    TREE_TYPE (identifier_global_value (get_identifier (SIZE_TYPE)));
+  size_type_node = copy_type_with_name (SIZE_TYPE);
   signed_size_type_node = c_common_signed_type (size_type_node);
   set_sizetype (size_type_node);
 
@@ -3905,8 +3917,7 @@ c_common_nodes_and_builtins (void)
 			  (char_type_node, TYPE_QUAL_CONST));
 
   /* This is special for C++ so functions can be overloaded.  */
-  wchar_type_node = get_identifier (MODIFIED_WCHAR_TYPE);
-  wchar_type_node = TREE_TYPE (identifier_global_value (wchar_type_node));
+  wchar_type_node = copy_type_with_name (MODIFIED_WCHAR_TYPE);
   wchar_type_size = TYPE_PRECISION (wchar_type_node);
   if (c_dialect_cxx ())
     {
@@ -3935,8 +3946,7 @@ c_common_nodes_and_builtins (void)
     TREE_TYPE (identifier_global_value (get_identifier (UINTMAX_TYPE)));
 
   default_function_type = build_function_type (integer_type_node, NULL_TREE);
-  ptrdiff_type_node
-    = TREE_TYPE (identifier_global_value (get_identifier (PTRDIFF_TYPE)));
+  ptrdiff_type_node = copy_type_with_name (PTRDIFF_TYPE);
   unsigned_ptrdiff_type_node = c_common_unsigned_type (ptrdiff_type_node);
 
   lang_hooks.decls.pushdecl

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: PATCH: Add format string suggestions to -Wformat warnings
  2007-08-21 22:58   ` Dan Hipschman
@ 2007-09-13 23:20     ` Joseph S. Myers
  2007-09-14  1:30       ` Ollie Wild
  2008-08-14 19:43       ` Dan Hipschman
  0 siblings, 2 replies; 8+ messages in thread
From: Joseph S. Myers @ 2007-09-13 23:20 UTC (permalink / raw)
  To: Dan Hipschman; +Cc: gcc-patches

On Tue, 21 Aug 2007, Dan Hipschman wrote:

> > dg-excess-errors should only be used very sparingly with xfail.  When 
> > there are specific diagnostics expected on specific lines, you should 
> > update the tests to match each new diagnostic individually rather than 
> > using dg-excess-errors to match them all together.
> 
> OK, I've done this, too.

We're moving away from having dg-* as regular expressions to match 
multiple messages on one line to having a separate dg-* for each 
diagnostic (that way, you know if only some of the expected diagnostics on 
a line have disappeared).

Thus, rather than changing the existing dg-warning to dg-message with 
regular expressions, you should leave them alone and add new dg-message 
lines for the new diagnostics (which use line numbers to identify the line 
on which the diagnostic is expected, { dg-message "regexp" "description" { 
target *-*-* } line-number }) (the description serves to make the PASS or 
FAIL lines unique when multiple diagnostics are expected on the same line 
and would otherwise get identical PASS or FAIL lines).

The revised C front-end changes look good to me but I can't reach a final 
conclusion on them without seeing the tests revised to avoid matching 
multiple diagnostics with a single regexp.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: PATCH: Add format string suggestions to -Wformat warnings
  2007-09-13 23:20     ` Joseph S. Myers
@ 2007-09-14  1:30       ` Ollie Wild
  2007-09-14  3:51         ` Joseph S. Myers
  2008-08-14 19:43       ` Dan Hipschman
  1 sibling, 1 reply; 8+ messages in thread
From: Ollie Wild @ 2007-09-14  1:30 UTC (permalink / raw)
  To: Joseph S. Myers; +Cc: Dan Hipschman, gcc-patches

On 9/13/07, Joseph S. Myers <joseph@codesourcery.com> wrote:

> We're moving away from having dg-* as regular expressions to match
> multiple messages on one line to having a separate dg-* for each
> diagnostic (that way, you know if only some of the expected diagnostics on
> a line have disappeared).

That seems reasonable, but it contradicts feedback I've gotten from
previous patches.  For example, see
http://gcc.gnu.org/ml/gcc-patches/2007-07/msg00119.html.

For consistency's sake, it might be worth publishing a test guidelines
doc.  Or does such a beast already exist?

Ollie

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: PATCH: Add format string suggestions to -Wformat warnings
  2007-09-14  1:30       ` Ollie Wild
@ 2007-09-14  3:51         ` Joseph S. Myers
  0 siblings, 0 replies; 8+ messages in thread
From: Joseph S. Myers @ 2007-09-14  3:51 UTC (permalink / raw)
  To: Ollie Wild; +Cc: Dan Hipschman, gcc-patches

On Thu, 13 Sep 2007, Ollie Wild wrote:

> For consistency's sake, it might be worth publishing a test guidelines
> doc.  Or does such a beast already exist?

No; sourcebuild.texi is out-of-date on some things such as testing for 
whether messages are errors, warnings or otherwise.  The series of PR 
25241 patches fixed various tests matching multiple messages with a single 
regexp.

Eventually we want things fixed so that a single dg-error (etc.) will only 
ever match a single line of diagnostic output, even if more than one would 
match the regexp.  That way we can readily test for various bugs there 
have been in the past with duplicate diagnostics, which are a pain to test 
at present.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: PATCH: Add format string suggestions to -Wformat warnings
  2007-09-13 23:20     ` Joseph S. Myers
  2007-09-14  1:30       ` Ollie Wild
@ 2008-08-14 19:43       ` Dan Hipschman
  1 sibling, 0 replies; 8+ messages in thread
From: Dan Hipschman @ 2008-08-14 19:43 UTC (permalink / raw)
  To: Joseph S. Myers; +Cc: gcc-patches

On Thu, Sep 13, 2007 at 10:44:08PM +0000, Joseph S. Myers wrote:
> We're moving away from having dg-* as regular expressions to match 
> multiple messages on one line to having a separate dg-* for each 
> diagnostic (that way, you know if only some of the expected diagnostics on 
> a line have disappeared).

Sorry for the *very* long delay resending this patch.  I got a bit
distracted.  I also haven't been following GCC development as closely as
I might hope, so hopefully nothing has changed that would now make this
patch unacceptable.  The last send of this patch was here:

http://gcc.gnu.org/ml/gcc-patches/2007-08/msg00908.html

I've only separated the dg-warnings and dg-messages out to their own
lines.  Everything else is the same.  I retested on x86_64 GNU/Linux.

> The revised C front-end changes look good to me but I can't reach a final 
> conclusion on them without seeing the tests revised to avoid matching 
> multiple diagnostics with a single regexp.

The front-end changes are exactly the same, so hopefully these are still
OK.

Thanks!
Dan

gcc/
2008-08-14  Dan Hipschman  <dsh@google.com>

	* c-format.h: New FMT_SUG_* flags.
	(format_suggestions): New structure.
	(format_kind_info): Add suggestions field.
	* c-format.c (format_wanted_type): Add suggestions field.
	(printf_suggestions): New global.
	(format_types_orig): Add suggestions field initializers.
	(check_format_info_main): Use suggestions.
	(arg_readonly): New function.
	(check_format_types): Use it.  Pass orig_cur_param instead of
	orig_cur_type to format_type_warning.
	(format_type_warning): Add suggestions and arg parameters.  Remove
	arg_type parameter.  Use format suggestions in warnings.
	* c-typeck.c (build_c_cast): Add a call to convert even if main
	variants are the same.
	* c-common.c (copy_type_with_name): New function.
	(c_common_nodes_and_builtins): Use it.

testsuite/
2008-08-14  Dan Hipschman  <dsh@google.com>

	* gcc.dg/format/dfp-suggest-1.c: New test.
	* gcc.dg/format/ext-suggest-1.c: New test.
	* gcc.dg/format/ext-suggest-2.c: New test.
	* gcc.dg/format/typedef-suggest-1.c: New test.
	* gcc.dg/format/typedef-suggest-2.c: New test.
	* gcc.dg/format/array-1.c: Update to handle format suggestion notes.
	* gcc.dg/format/branch-1.c: Likewise.
	* gcc.dg/format/builtin-1.c: Likewise.
	* gcc.dg/format/c90-printf-1.c: Likewise.
	* gcc.dg/format/c90-printf-3.c: Likewise.
	* gcc.dg/format/c99-printf-1.c: Likewise.
	* gcc.dg/format/c99-printf-3.c: Likewise.
	* gcc.dg/format/cast-1.c: Likewise.
	* gcc.dg/format/dfp-printf-1.c: Likewise.
	* gcc.dg/format/diag-1.c: Likewise.
	* gcc.dg/format/ext-1.c: Likewise.
	* gcc.dg/format/ext-5.c: Likewise.
	* gcc.dg/format/ext-6.c: Likewise.
	* gcc.dg/format/multattr-3.c: Likewise.
	* gcc.dg/format/unnamed-1.c: Likewise.
	* gcc.dg/format/va-1.c: Likewise.
	* gcc.dg/format/xopen-1.c: Likewise.

Index: gcc/testsuite/gcc.dg/format/builtin-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/builtin-1.c	(revision 138884)
+++ gcc/testsuite/gcc.dg/format/builtin-1.c	(working copy)
@@ -12,11 +12,15 @@
 {
   __builtin_fprintf (stdout, "%d", i);
   __builtin_fprintf (stdout, "%ld", i); /* { dg-warning "format" "__builtin_fprintf" } */
+                                        /* { dg-message ": d, i$" "format suggestion" 14 } */
   __builtin_printf ("%d", i);
   __builtin_printf ("%ld", i); /* { dg-warning "format" "__builtin_printf" } */
+                               /* { dg-message ": d, i$" "format suggestion" 17 } */
 
   __builtin_fprintf_unlocked (stdout, "%d", i);
   __builtin_fprintf_unlocked (stdout, "%ld", i); /* { dg-warning "format" "__builtin_fprintf_unlocked" } */
+                                                 /* { dg-message ": d, i$" "format suggestion" 21 } */
   __builtin_printf_unlocked ("%d", i);
   __builtin_printf_unlocked ("%ld", i); /* { dg-warning "format" "__builtin_printf_unlocked" } */
+                                        /* { dg-message ": d, i$" "format suggestion" 24 } */
 }
Index: gcc/testsuite/gcc.dg/format/ext-6.c
===================================================================
--- gcc/testsuite/gcc.dg/format/ext-6.c	(revision 138884)
+++ gcc/testsuite/gcc.dg/format/ext-6.c	(working copy)
@@ -14,16 +14,22 @@
 {
   fprintf (stdout, "%d", i);
   fprintf (stdout, "%ld", i); /* { dg-warning "format" "fprintf" } */
+                              /* { dg-message ": d, i$" "format suggestion" 16 } */
   printf ("%d", i);
   printf ("%ld", i); /* { dg-warning "format" "printf" } */
+                     /* { dg-message ": d, i$" "format suggestion" 19 } */
   fprintf_unlocked (stdout, "%d", i);
   fprintf_unlocked (stdout, "%ld", i); /* { dg-warning "format" "fprintf_unlocked" } */
+                                       /* { dg-message ": d, i$" "format suggestion" 22 } */
   printf_unlocked ("%d", i);
   printf_unlocked ("%ld", i); /* { dg-warning "format" "printf_unlocked" } */
+                              /* { dg-message ": d, i$" "format suggestion" 25 } */
   sprintf (s, "%d", i);
   sprintf (s, "%ld", i); /* { dg-warning "format" "sprintf" } */
+                         /* { dg-message ": d, i$" "format suggestion" 28 } */
   snprintf (s, n, "%d", i);
   snprintf (s, n, "%ld", i); /* { dg-warning "format" "snprintf" } */
+                             /* { dg-message ": d, i$" "format suggestion" 31 } */
   vfprintf (stdout, "%d", v0);
   vfprintf (stdout, "%Y", v1); /* { dg-warning "format" "vfprintf" } */
   vprintf ("%d", v2);
Index: gcc/testsuite/gcc.dg/format/xopen-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/xopen-1.c	(revision 138884)
+++ gcc/testsuite/gcc.dg/format/xopen-1.c	(working copy)
@@ -90,6 +90,7 @@
   scanf ("%1$d", ip);
   printf ("%1$d", i);
   printf ("%1$d", l); /* { dg-warning "arg 2|argument 2" "mismatched args with $ format" } */
+                      /* { dg-message ": ld, li$" "format suggestion" 92 } */
   printf ("%3$*2$.*1$ld", i2, i, l);
   printf ("%4$ld%7$ld%5$d%6$d%3$d%1$d%2$d", i, i, i, l, i, i, l);
   scanf ("%4$ld%7$ld%5$d%6$d%3$d%1$d%2$d", ip, ip, ip, lp, ip, ip, lp);
@@ -110,15 +111,15 @@
      warning about unused arguments rather than the more serious one about
      argument gaps.  */
   scanf ("%3$d%1$d", ip, ip, ip); /* { dg-bogus "before used" "unused $ scanf pointer operand" } */
-  /* { dg-warning "unused" "unused $ scanf pointer operand" { target *-*-* } 112 } */
+  /* { dg-warning "unused" "unused $ scanf pointer operand" { target *-*-* } 113 } */
   /* If there are non-pointer arguments unused at the end, this is also OK.  */
   scanf ("%3$d%1$d", ip, ip, ip, i); /* { dg-bogus "before used" "unused $ scanf pointer operand" } */
-  /* { dg-warning "unused" "unused $ scanf pointer operand" { target *-*-* } 115 } */
+  /* { dg-warning "unused" "unused $ scanf pointer operand" { target *-*-* } 116 } */
   scanf ("%3$d%1$d", ip, i, ip); /* { dg-warning "before used" "unused $ scanf non-pointer operand" } */
   /* Can't check the arguments in the vscanf case, so should suppose the
      lesser problem.  */
   vscanf ("%3$d%1$d", va); /* { dg-bogus "before used" "unused $ scanf pointer operand" } */
-  /* { dg-warning "unused" "unused $ scanf pointer operand" { target *-*-* } 120 } */
+  /* { dg-warning "unused" "unused $ scanf pointer operand" { target *-*-* } 121 } */
   scanf ("%2$*d%1$d", ip, ip); /* { dg-warning "operand" "operand number with suppression" } */
   printf ("%1$d%1$d", i);
   scanf ("%1$d%1$d", ip); /* { dg-warning "more than once" "multiple use of scanf argument" } */
Index: gcc/testsuite/gcc.dg/format/c99-printf-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/c99-printf-1.c	(revision 138884)
+++ gcc/testsuite/gcc.dg/format/c99-printf-1.c	(working copy)
@@ -193,5 +193,6 @@
      is used is not signed char *.)
   */
   printf ("%hhn", s); /* { dg-warning "format" "%hhn plain char" } */
+                      /* { dg-message ": s$" "format suggestion" 195 } */
   printf ("%hhn", us); /* { dg-warning "format" "%hhn unsigned char" } */
 }
Index: gcc/testsuite/gcc.dg/format/array-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/array-1.c	(revision 138884)
+++ gcc/testsuite/gcc.dg/format/array-1.c	(working copy)
@@ -24,17 +24,21 @@
   printf (a1);
   printf (a2, i);
   printf (a2, l); /* { dg-warning "format" "wrong type with array" } */
+                  /* { dg-message ": ld, li$" "format suggestion" 26 } */
   printf (b1); /* { dg-warning "unterminated" "unterminated array" } */
   printf (b2); /* { dg-warning "unterminated" "unterminated array" } */
   printf (c1);
   printf (c2, i);
   printf (c2, l); /* { dg-warning "format" "wrong type with array" } */
+                  /* { dg-message ": ld, li$" "format suggestion" 32 } */
   printf (p1);
   printf (p2, i);
   printf (p2, l); /* { dg-warning "format" "wrong type with array" } */
+                  /* { dg-message ": ld, li$" "format suggestion" 36 } */
   printf (q1);
   printf (q2, i);
   printf (q2, l); /* { dg-warning "format" "wrong type with array" } */
+                  /* { dg-message ": ld, li$" "format suggestion" 40 } */
   /* Volatile or non-constant arrays must not be checked.  */
   printf (d); /* { dg-warning "not a string literal" "non-const" } */
   printf ((const char *)e); /* { dg-warning "not a string literal" "volatile" } */
Index: gcc/testsuite/gcc.dg/format/c90-printf-3.c
===================================================================
--- gcc/testsuite/gcc.dg/format/c90-printf-3.c	(revision 138884)
+++ gcc/testsuite/gcc.dg/format/c90-printf-3.c	(working copy)
@@ -14,13 +14,16 @@
 {
   fprintf (stdout, "%d", i);
   fprintf (stdout, "%ld", i); /* { dg-warning "format" "fprintf" } */
+                              /* { dg-message ": d, i$" "format suggestion" 16 } */
   printf ("%d", i);
   printf ("%ld", i); /* { dg-warning "format" "printf" } */
+                     /* { dg-message ": d, i$" "format suggestion" 19 } */
   /* The "unlocked" functions shouldn't warn in c90 mode.  */
   fprintf_unlocked (stdout, "%ld", i); /* { dg-bogus "format" "fprintf_unlocked" } */
   printf_unlocked ("%ld", i); /* { dg-bogus "format" "printf_unlocked" } */
   sprintf (s, "%d", i);
   sprintf (s, "%ld", i); /* { dg-warning "format" "sprintf" } */
+                         /* { dg-message ": d, i$" "format suggestion" 25 } */
   vfprintf (stdout, "%d", v0);
   vfprintf (stdout, "%Y", v1); /* { dg-warning "format" "vfprintf" } */
   vprintf ("%d", v2);
Index: gcc/testsuite/gcc.dg/format/dfp-printf-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/dfp-printf-1.c	(revision 138884)
+++ gcc/testsuite/gcc.dg/format/dfp-printf-1.c	(working copy)
@@ -43,30 +43,54 @@
   /* Check warnings for type mismatches.  */
 
   printf ("%Hf\n", y);	/* { dg-warning "expects type" "bad use of %H" } */
+                        /* { dg-message ": De, DE, Df, DF, Dg, DG$" "format suggestion" 45 } */
   printf ("%HF\n", y);	/* { dg-warning "expects type" "bad use of %H" } */
+                        /* { dg-message ": De, DE, Df, DF, Dg, DG$" "format suggestion" 47 } */
   printf ("%He\n", y);	/* { dg-warning "expects type" "bad use of %H" } */
+                        /* { dg-message ": De, DE, Df, DF, Dg, DG$" "format suggestion" 49 } */
   printf ("%HE\n", y);	/* { dg-warning "expects type" "bad use of %H" } */
+                        /* { dg-message ": De, DE, Df, DF, Dg, DG$" "format suggestion" 51 } */
   printf ("%Hg\n", y);	/* { dg-warning "expects type" "bad use of %H" } */
+                        /* { dg-message ": De, DE, Df, DF, Dg, DG$" "format suggestion" 53 } */
   printf ("%HG\n", y);	/* { dg-warning "expects type" "bad use of %H" } */
+                        /* { dg-message ": De, DE, Df, DF, Dg, DG$" "format suggestion" 55 } */
   printf ("%Hf\n", z);	/* { dg-warning "expects type" "bad use of %H" } */
+                        /* { dg-message ": DDe, DDE, DDf, DDF, DDg, DDG$" "format suggestion" 57 } */
   printf ("%HF\n", z);	/* { dg-warning "expects type" "bad use of %H" } */
+                        /* { dg-message ": DDe, DDE, DDf, DDF, DDg, DDG$" "format suggestion" 59 } */
   printf ("%He\n", z);	/* { dg-warning "expects type" "bad use of %H" } */
+                        /* { dg-message ": DDe, DDE, DDf, DDF, DDg, DDG$" "format suggestion" 61 } */
   printf ("%HE\n", z);	/* { dg-warning "expects type" "bad use of %H" } */
+                        /* { dg-message ": DDe, DDE, DDf, DDF, DDg, DDG$" "format suggestion" 63 } */
   printf ("%Hg\n", z);	/* { dg-warning "expects type" "bad use of %H" } */
+                        /* { dg-message ": DDe, DDE, DDf, DDF, DDg, DDG$" "format suggestion" 65 } */
   printf ("%HG\n", z);	/* { dg-warning "expects type" "bad use of %H" } */
+                        /* { dg-message ": DDe, DDE, DDf, DDF, DDg, DDG$" "format suggestion" 67 } */
 
   printf ("%Df\n", x);	/* { dg-warning "expects type" "bad use of %D" } */
+                        /* { dg-message ": He, HE, Hf, HF, Hg, HG$" "format suggestion" 70 } */
   printf ("%DF\n", x);	/* { dg-warning "expects type" "bad use of %D" } */
+                        /* { dg-message ": He, HE, Hf, HF, Hg, HG$" "format suggestion" 72 } */
   printf ("%De\n", x);	/* { dg-warning "expects type" "bad use of %D" } */
+                        /* { dg-message ": He, HE, Hf, HF, Hg, HG$" "format suggestion" 74 } */
   printf ("%DE\n", x);	/* { dg-warning "expects type" "bad use of %D" } */
+                        /* { dg-message ": He, HE, Hf, HF, Hg, HG$" "format suggestion" 76 } */
   printf ("%Dg\n", x);	/* { dg-warning "expects type" "bad use of %D" } */
+                        /* { dg-message ": He, HE, Hf, HF, Hg, HG$" "format suggestion" 78 } */
   printf ("%DG\n", x);	/* { dg-warning "expects type" "bad use of %D" } */
+                        /* { dg-message ": He, HE, Hf, HF, Hg, HG$" "format suggestion" 80 } */
   printf ("%Df\n", z);	/* { dg-warning "expects type" "bad use of %D" } */
+                        /* { dg-message ": DDe, DDE, DDf, DDF, DDg, DDG$" "format suggestion" 82 } */
   printf ("%DF\n", z);	/* { dg-warning "expects type" "bad use of %D" } */
+                        /* { dg-message ": DDe, DDE, DDf, DDF, DDg, DDG$" "format suggestion" 84 } */
   printf ("%De\n", z);	/* { dg-warning "expects type" "bad use of %D" } */
+                        /* { dg-message ": DDe, DDE, DDf, DDF, DDg, DDG$" "format suggestion" 86 } */
   printf ("%DE\n", z);	/* { dg-warning "expects type" "bad use of %D" } */
+                        /* { dg-message ": DDe, DDE, DDf, DDF, DDg, DDG$" "format suggestion" 88 } */
   printf ("%Dg\n", z);	/* { dg-warning "expects type" "bad use of %D" } */
+                        /* { dg-message ": DDe, DDE, DDf, DDF, DDg, DDG$" "format suggestion" 90 } */
   printf ("%DG\n", z);	/* { dg-warning "expects type" "bad use of %D" } */
+                        /* { dg-message ": DDe, DDE, DDf, DDF, DDg, DDG$" "format suggestion" 92 } */
 
   printf ("%DDf\n", x);	/* { dg-warning "expects type" "bad use of %DD" } */
   printf ("%DDF\n", x);	/* { dg-warning "expects type" "bad use of %DD" } */
@@ -75,11 +99,17 @@
   printf ("%DDg\n", x);	/* { dg-warning "expects type" "bad use of %DD" } */
   printf ("%DDG\n", x);	/* { dg-warning "expects type" "bad use of %DD" } */
   printf ("%DDf\n", y);	/* { dg-warning "expects type" "bad use of %DD" } */
+                        /* { dg-message ": De, DE, Df, DF, Dg, DG$" "format suggestion" 101 } */
   printf ("%DDF\n", y);	/* { dg-warning "expects type" "bad use of %DD" } */
+                        /* { dg-message ": De, DE, Df, DF, Dg, DG$" "format suggestion" 103 } */
   printf ("%DDe\n", y);	/* { dg-warning "expects type" "bad use of %DD" } */
+                        /* { dg-message ": De, DE, Df, DF, Dg, DG$" "format suggestion" 105 } */
   printf ("%DDE\n", y);	/* { dg-warning "expects type" "bad use of %DD" } */
+                        /* { dg-message ": De, DE, Df, DF, Dg, DG$" "format suggestion" 107 } */
   printf ("%DDg\n", y);	/* { dg-warning "expects type" "bad use of %DD" } */
+                        /* { dg-message ": De, DE, Df, DF, Dg, DG$" "format suggestion" 109 } */
   printf ("%DDG\n", y);	/* { dg-warning "expects type" "bad use of %DD" } */
+                        /* { dg-message ": De, DE, Df, DF, Dg, DG$" "format suggestion" 111 } */
 
   /* Check for warnings for bad use of H, D, and DD length specifiers.  */
 
Index: gcc/testsuite/gcc.dg/format/unnamed-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/unnamed-1.c	(revision 138884)
+++ gcc/testsuite/gcc.dg/format/unnamed-1.c	(working copy)
@@ -19,6 +19,7 @@
 f (TItype x)
 {
   printf("%d", x); /* { dg-warning "expects type" } */
+  /* { dg-message ": ld, li$" "format suggestion" 21 } */
   printf("%d", 141592653589793238462643383279502884197169399375105820974944); /* { dg-warning "expects type" } */
-  /* { dg-warning "unsigned only|too large" "constant" { target *-*-* } 22 } */
+  /* { dg-warning "unsigned only|too large" "constant" { target *-*-* } 23 } */
 }
Index: gcc/testsuite/gcc.dg/format/cast-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/cast-1.c	(revision 138884)
+++ gcc/testsuite/gcc.dg/format/cast-1.c	(working copy)
@@ -11,6 +11,8 @@
 f (int x)
 {
   printf("%s", x); /* { dg-warning "format" } */
+                   /* { dg-message ": d, i$" "format suggestion" 13 } */
   printf((char *)(size_t)"%s", x); /* { dg-warning "format" } */
+                                   /* { dg-message ": d, i$" "format suggestion" 15 } */
   printf((char *)(char)"%s", x); /* { dg-warning "cast from pointer to integer of different size" } */
 }
Index: gcc/testsuite/gcc.dg/format/branch-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/branch-1.c	(revision 138884)
+++ gcc/testsuite/gcc.dg/format/branch-1.c	(working copy)
@@ -10,8 +10,11 @@
 {
   printf ((nfoo > 1) ? "%d foos" : "%d foo", nfoo);
   printf ((l > 1) ? "%d foos" : "%d foo", l); /* { dg-warning "int" "wrong type in conditional expr" } */
+                                              /* { dg-message ": ld, li$" "format suggestion" 12 } */
   printf ((l > 1) ? "%ld foos" : "%d foo", l); /* { dg-warning "int" "wrong type in conditional expr" } */
+                                               /* { dg-message ": ld, li$" "format suggestion" 14 } */
   printf ((l > 1) ? "%d foos" : "%ld foo", l); /* { dg-warning "int" "wrong type in conditional expr" } */
+                                               /* { dg-message ": ld, li$" "format suggestion" 16 } */
   /* Should allow one case to have extra arguments.  */
   printf ((nfoo > 1) ? "%d foos" : "1 foo", nfoo);
   printf ((nfoo > 1) ? "many foos" : "1 foo", nfoo); /* { dg-warning "too many" "too many args in all branches" } */
@@ -19,9 +22,12 @@
   printf ((nfoo > 1) ? "%d foos" : ((nfoo > 0) ? "1 foo" : "no foos"), nfoo);
   printf ((nfoo > 1) ? "%d foos" : ((nfoo > 0) ? "%d foo" : "%d foos"), nfoo);
   printf ((nfoo > 1) ? "%d foos" : ((nfoo > 0) ? "%d foo" : "%ld foos"), nfoo); /* { dg-warning "long int" "wrong type" } */
+                                                                                /* { dg-message ": d, i$" "format suggestion" 24 } */
   printf ((nfoo > 1) ? "%ld foos" : ((nfoo > 0) ? "%d foo" : "%d foos"), nfoo); /* { dg-warning "long int" "wrong type" } */
+                                                                                /* { dg-message ": d, i$" "format suggestion" 26 } */
   printf ((nfoo > 1) ? "%d foos" : ((nfoo > 0) ? "%ld foo" : "%d foos"), nfoo); /* { dg-warning "long int" "wrong type" } */
+                                                                                /* { dg-message ": d, i$" "format suggestion" 28 } */
   /* Extra arguments to NULL should be complained about.  */
   printf (NULL, "foo"); /* { dg-warning "too many" "NULL extra args" } */
-  /* { dg-warning "null" "null format arg" { target *-*-* } 25 } */
+  /* { dg-warning "null" "null format arg" { target *-*-* } 31 } */
 }
Index: gcc/testsuite/gcc.dg/format/diag-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/diag-1.c	(revision 138884)
+++ gcc/testsuite/gcc.dg/format/diag-1.c	(working copy)
@@ -10,9 +10,12 @@
 {
   /* This should get a message referring to `hh', not to `H'.  */
   printf ("%hhf", d); /* { dg-warning "hh" "%hhf warning" } */
+                      /* { dg-message ": a, A, e, E, f, F, g, G$" "format suggestion" 12 } */
   /* This should get a message referring to `ll', not to `q'.  */
   printf ("%llf", d); /* { dg-warning "ll" "%llf warning" } */
+                      /* { dg-message ": a, A, e, E, f, F, g, G$" "format suggestion" 15 } */
   /* This should get a message referring to 'size_t', not to
      'unsigned int' or similar.  */
   printf ("%zu", d); /* { dg-warning "size_t" "size_t format warning" } */
+                     /* { dg-message ": a, A, e, E, f, F, g, G$" "format suggestion" 19 } */
 }
Index: gcc/testsuite/gcc.dg/format/c99-printf-3.c
===================================================================
--- gcc/testsuite/gcc.dg/format/c99-printf-3.c	(revision 138884)
+++ gcc/testsuite/gcc.dg/format/c99-printf-3.c	(working copy)
@@ -13,15 +13,19 @@
 {
   fprintf (stdout, "%d", i);
   fprintf (stdout, "%ld", i); /* { dg-warning "format" "fprintf" } */
+                              /* { dg-message ": d, i$" "format suggestion" 15 } */
   printf ("%d", i);
   printf ("%ld", i); /* { dg-warning "format" "printf" } */
+                     /* { dg-message ": d, i$" "format suggestion" 18 } */
   /* The "unlocked" functions shouldn't warn in c99 mode.  */
   fprintf_unlocked (stdout, "%ld", i); /* { dg-bogus "format" "fprintf_unlocked" } */
   printf_unlocked ("%ld", i); /* { dg-bogus "format" "printf_unlocked" } */
   sprintf (s, "%d", i);
   sprintf (s, "%ld", i); /* { dg-warning "format" "sprintf" } */
+                         /* { dg-message ": d, i$" "format suggestion" 24 } */
   snprintf (s, n, "%d", i);
   snprintf (s, n, "%ld", i); /* { dg-warning "format" "snprintf" } */
+                             /* { dg-message ": d, i$" "format suggestion" 27 } */
   vfprintf (stdout, "%d", v0);
   vfprintf (stdout, "%Y", v1); /* { dg-warning "format" "vfprintf" } */
   vprintf ("%d", v0);
Index: gcc/testsuite/gcc.dg/format/multattr-3.c
===================================================================
--- gcc/testsuite/gcc.dg/format/multattr-3.c	(revision 138884)
+++ gcc/testsuite/gcc.dg/format/multattr-3.c	(working copy)
@@ -14,8 +14,11 @@
 {
   printf (ngettext ("%d foo", "%d foos", nfoo), nfoo);
   printf (ngettext ("%d foo", "%d foos", l), l); /* { dg-warning "int" "wrong type in conditional expr" } */
+                                                 /* { dg-message ": ld, li$" "format suggestion" 16 } */
   printf (ngettext ("%d foo", "%ld foos", l), l); /* { dg-warning "int" "wrong type in conditional expr" } */
+                                                  /* { dg-message ": ld, li$" "format suggestion" 18 } */
   printf (ngettext ("%ld foo", "%d foos", l), l); /* { dg-warning "int" "wrong type in conditional expr" } */
+                                                  /* { dg-message ": ld, li$" "format suggestion" 20 } */
   /* Should allow one case to have extra arguments.  */
   printf (ngettext ("1 foo", "%d foos", nfoo), nfoo);
   printf (ngettext ("1 foo", "many foos", nfoo), nfoo); /* { dg-warning "too many" "too many args in all branches" } */
@@ -23,6 +26,9 @@
   printf (ngettext ("1 foo", (nfoo > 0) ? "%d foos" : "no foos", nfoo), nfoo);
   printf (ngettext ("%d foo", (nfoo > 0) ? "%d foos" : "no foos", nfoo), nfoo);
   printf (ngettext ("%ld foo", (nfoo > 0) ? "%d foos" : "no foos", nfoo), nfoo); /* { dg-warning "long int" "wrong type" } */
+                                                                                 /* { dg-message ": d, i$" "format suggestion" 28 } */
   printf (ngettext ("%d foo", (nfoo > 0) ? "%ld foos" : "no foos", nfoo), nfoo); /* { dg-warning "long int" "wrong type" } */
+                                                                                 /* { dg-message ": d, i$" "format suggestion" 30 } */
   printf (ngettext ("%d foo", (nfoo > 0) ? "%d foos" : "%ld foos", nfoo), nfoo); /* { dg-warning "long int" "wrong type" } */
+                                                                                 /* { dg-message ": d, i$" "format suggestion" 32 } */
 }
Index: gcc/testsuite/gcc.dg/format/c90-printf-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/c90-printf-1.c	(revision 138884)
+++ gcc/testsuite/gcc.dg/format/c90-printf-1.c	(working copy)
@@ -177,12 +177,17 @@
   printf ("%-08G", d); /* { dg-warning "flags|ignored" "0 flag ignored with - flag" } */
   /* Various tests of bad argument types.  */
   printf ("%d", l); /* { dg-warning "format" "bad argument types" } */
+                    /* { dg-message ": ld, li$" "format suggestion" 179 } */
   printf ("%*.*d", l, i2, i); /* { dg-warning "field" "bad * argument types" } */
   printf ("%*.*d", i1, l, i); /* { dg-warning "field" "bad * argument types" } */
   printf ("%ld", i); /* { dg-warning "format" "bad argument types" } */
+                     /* { dg-message ": d, i$" "format suggestion" 183 } */
   printf ("%s", n); /* { dg-warning "format" "bad argument types" } */
+                    /* { dg-message ": n$" "format suggestion" 185 } */
   printf ("%p", i); /* { dg-warning "format" "bad argument types" } */
+                    /* { dg-message ": d, i$" "format suggestion" 187 } */
   printf ("%n", p); /* { dg-warning "format" "bad argument types" } */
+                    /* { dg-message ": p$" "format suggestion" 189 } */
   /* With -pedantic, we want some further checks for pointer targets:
      %p should allow only pointers to void (possibly qualified) and
      to character types (possibly qualified), but not function pointers
@@ -199,6 +204,7 @@
   printf ("%p", foo); /* { dg-warning "format" "bad argument types" } */
   printf ("%n", un); /* { dg-warning "format" "bad argument types" } */
   printf ("%p", n); /* { dg-warning "format" "bad argument types" } */
+                    /* { dg-message ": n$" "format suggestion" 206 } */
   /* Allow character pointers with %p.  */
   printf ("%p%p%p%p", s, ss, us, css);
   /* %s allows any character type.  */
@@ -208,6 +214,7 @@
      read to permit it.
   */
   printf ("%s", p); /* { dg-warning "format" "bad argument types" } */
+                    /* { dg-message ": p$" "format suggestion" 216 } */
   /* The historical behavior is to allow signed / unsigned types
      interchangably as arguments.  For values representable in both types,
      such usage may be correct.  For now preserve the behavior of GCC
Index: gcc/testsuite/gcc.dg/format/ext-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/ext-1.c	(revision 138884)
+++ gcc/testsuite/gcc.dg/format/ext-1.c	(working copy)
@@ -121,6 +121,8 @@
      i.e. thread unsafe versions of these functions.  */
   fprintf_unlocked (stdout, "%d", i);
   fprintf_unlocked (stdout, "%ld", i); /* { dg-warning "format" "fprintf_unlocked" } */
+                                       /* { dg-message ": d, i$" "format suggestion" 123 } */
   printf_unlocked ("%d", i);
   printf_unlocked ("%ld", i); /* { dg-warning "format" "printf_unlocked" } */
+                              /* { dg-message ": d, i$" "format suggestion" 126 } */
 }
Index: gcc/testsuite/gcc.dg/format/ext-5.c
===================================================================
--- gcc/testsuite/gcc.dg/format/ext-5.c	(revision 138884)
+++ gcc/testsuite/gcc.dg/format/ext-5.c	(working copy)
@@ -10,8 +10,11 @@
 {
   printf (gettext ("%d"), i);
   printf (gettext ("%ld"), i); /* { dg-warning "format" "gettext" } */
+                               /* { dg-message ": d, i$" "format suggestion" 12 } */
   printf (dgettext ("", "%d"), i);
   printf (dgettext ("", "%ld"), i); /* { dg-warning "format" "dgettext" } */
+                                    /* { dg-message ": d, i$" "format suggestion" 15 } */
   printf (dcgettext ("", "%d", 0), i);
   printf (dcgettext ("", "%ld", 0), i); /* { dg-warning "format" "dcgettext" } */
+                                        /* { dg-message ": d, i$" "format suggestion" 18 } */
 }
Index: gcc/testsuite/gcc.dg/format/va-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/va-1.c	(revision 138884)
+++ gcc/testsuite/gcc.dg/format/va-1.c	(working copy)
@@ -10,4 +10,5 @@
 {
   printf ("%d", p); /* { dg-bogus "va_list" "wrong type in format warning" } */
   /* { dg-warning "format" "format error" { target *-*-* } 11 } */
+  /* { dg-message ": p$" "format suggestion" 11 } */
 }
Index: gcc/c-format.c
===================================================================
--- gcc/c-format.c	(revision 138884)
+++ gcc/c-format.c	(working copy)
@@ -270,6 +270,8 @@
   /* Whether the argument, dereferenced once, is read from and so
      must not be a NULL pointer.  */
   int reading_from_flag;
+  /* Format conversion suggestions, if available, otherwise NULL.  */
+  format_suggestions *suggestions;
   /* If warnings should be of the form "field precision should have
      type 'int'", the name to use (in this case "field precision"),
      otherwise NULL, for "format expects type 'long'" type
@@ -505,6 +507,43 @@
   { 0, 0, 0, 0 }
 };
 
+static format_suggestions printf_suggestions[] =
+{
+  { &wchar_type_node, "wchar_t", FMT_SUG_POINTER, STD_C94, "ls" },
+  { &wchar_type_node, "wchar_t", 0, STD_C94, "lc" },
+  { &wint_type_node, "wint_t", 0, STD_C94, "lc" },
+  { &signed_size_type_node, "ssize_t", 0, STD_C99, "zd, zi" },
+  { &size_type_node, "size_t", 0, STD_C99, "zo, zu, zx, zX" },
+  { &ptrdiff_type_node, "ptrdiff_t", 0, STD_C99, "td, ti" },
+  { &intmax_type_node, "intmax_t", 0, STD_C99, "jd, ji" },
+  { &uintmax_type_node, "uintmax_t", 0, STD_C99, "jo, ju, jx, jX" },
+  { &dfloat32_type_node, "_Decimal32", 0, STD_EXT, "He, HE, Hf, HF, Hg, HG" },
+  { &dfloat64_type_node, "_Decimal64", 0, STD_EXT, "De, DE, Df, DF, Dg, DG" },
+  { &dfloat128_type_node, "_Decimal128", 0, STD_EXT, "DDe, DDE, DDf, DDF, DDg, DDG" },
+  { &unsigned_char_type_node, NULL, 0, STD_C99, "c, hho, hhu, hhx, hhX" },
+  { &unsigned_char_type_node, NULL, 0, STD_C89, "c" },
+  { &signed_char_type_node, NULL, 0, STD_C99, "hhd, hhi" },
+  { &short_unsigned_type_node, NULL, 0, STD_C89, "ho, hu, hx, hX" },
+  { &short_integer_type_node, NULL, 0, STD_C89, "hd, hi" },
+  { &unsigned_type_node, NULL, 0, STD_C89, "o, u, x, X" },
+  { &integer_type_node, NULL, 0, STD_C89, "d, i" },
+  { &long_integer_type_node, NULL, 0, STD_C89, "ld, li" },
+  { &long_long_integer_type_node, NULL, 0, STD_C9L, "lld, lli" },
+  { &char_type_node, NULL, FMT_SUG_POINTER, STD_C89, "s" },
+  { &long_unsigned_type_node, NULL, 0, STD_C89, "lo, lu, lx, lX" },
+  { &long_long_unsigned_type_node, NULL, 0, STD_C9L, "llo, llu, llx, llX" },
+  { &double_type_node, NULL, 0, STD_C99, "a, A, e, E, f, F, g, G" },
+  { &long_double_type_node, NULL, 0, STD_C99, "La, LA, Le, LE, Lf, LF, Lg, LG" },
+  { &double_type_node, NULL, 0, STD_C89, "e, E, f, g, G" },
+  { &long_double_type_node, NULL, 0, STD_C89, "Le, LE, Lf, Lg, LG" },
+  { &signed_char_type_node, NULL, FMT_SUG_POINTER|FMT_SUG_WRITE, STD_C99, "hhn" },
+  { &short_integer_type_node, NULL, FMT_SUG_POINTER|FMT_SUG_WRITE, STD_C89, "hn" },
+  { &integer_type_node, NULL, FMT_SUG_POINTER|FMT_SUG_WRITE, STD_C89, "n" },
+  { &long_integer_type_node, NULL, FMT_SUG_POINTER|FMT_SUG_WRITE, STD_C89, "ln" },
+  { &long_long_integer_type_node, NULL, FMT_SUG_POINTER|FMT_SUG_WRITE, STD_C9L, "lln" },
+  { &void_type_node, NULL, FMT_SUG_POINTER, STD_C89, "p" },
+  { NULL, NULL, 0, 0, NULL }
+};
 
 static const format_char_info print_char_table[] =
 {
@@ -720,60 +759,60 @@
 static const format_kind_info format_types_orig[] =
 {
   { "gnu_printf",   printf_length_specs,  print_char_table, " +#0-'I", NULL,
-    printf_flag_specs, printf_flag_pairs,
+    printf_flag_specs, printf_flag_pairs, printf_suggestions,
     FMT_FLAG_ARG_CONVERT|FMT_FLAG_DOLLAR_MULTIPLE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_EMPTY_PREC_OK,
     'w', 0, 'p', 0, 'L', 0,
     &integer_type_node, &integer_type_node
   },
   { "asm_fprintf",   asm_fprintf_length_specs,  asm_fprintf_char_table, " +#0-", NULL,
-    asm_fprintf_flag_specs, asm_fprintf_flag_pairs,
+    asm_fprintf_flag_specs, asm_fprintf_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT|FMT_FLAG_EMPTY_PREC_OK,
     'w', 0, 'p', 0, 'L', 0,
     NULL, NULL
   },
   { "gcc_diag",   gcc_diag_length_specs,  gcc_diag_char_table, "q+", NULL,
-    gcc_diag_flag_specs, gcc_diag_flag_pairs,
+    gcc_diag_flag_specs, gcc_diag_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT,
     0, 0, 'p', 0, 'L', 0,
     NULL, &integer_type_node
   },
   { "gcc_tdiag",   gcc_tdiag_length_specs,  gcc_tdiag_char_table, "q+", NULL,
-    gcc_tdiag_flag_specs, gcc_tdiag_flag_pairs,
+    gcc_tdiag_flag_specs, gcc_tdiag_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT,
     0, 0, 'p', 0, 'L', 0,
     NULL, &integer_type_node
   },
   { "gcc_cdiag",   gcc_cdiag_length_specs,  gcc_cdiag_char_table, "q+", NULL,
-    gcc_cdiag_flag_specs, gcc_cdiag_flag_pairs,
+    gcc_cdiag_flag_specs, gcc_cdiag_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT,
     0, 0, 'p', 0, 'L', 0,
     NULL, &integer_type_node
   },
   { "gcc_cxxdiag",   gcc_cxxdiag_length_specs,  gcc_cxxdiag_char_table, "q+#", NULL,
-    gcc_cxxdiag_flag_specs, gcc_cxxdiag_flag_pairs,
+    gcc_cxxdiag_flag_specs, gcc_cxxdiag_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT,
     0, 0, 'p', 0, 'L', 0,
     NULL, &integer_type_node
   },
   { "gcc_gfc", gcc_gfc_length_specs, gcc_gfc_char_table, "", NULL,
-    NULL, gcc_gfc_flag_pairs,
+    NULL, gcc_gfc_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT,
     0, 0, 0, 0, 0, 0,
     NULL, NULL
   },
   { "gnu_scanf",    scanf_length_specs,   scan_char_table,  "*'I", NULL,
-    scanf_flag_specs, scanf_flag_pairs,
+    scanf_flag_specs, scanf_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT|FMT_FLAG_SCANF_A_KLUDGE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_ZERO_WIDTH_BAD|FMT_FLAG_DOLLAR_GAP_POINTER_OK,
     'w', 0, 0, '*', 'L', 'm',
     NULL, NULL
   },
   { "gnu_strftime", NULL,                 time_char_table,  "_-0^#", "EO",
-    strftime_flag_specs, strftime_flag_pairs,
+    strftime_flag_specs, strftime_flag_pairs, NULL,
     FMT_FLAG_FANCY_PERCENT_OK, 'w', 0, 0, 0, 0, 0,
     NULL, NULL
   },
   { "gnu_strfmon",  strfmon_length_specs, monetary_char_table, "=^+(!-", NULL,
-    strfmon_flag_specs, strfmon_flag_pairs,
+    strfmon_flag_specs, strfmon_flag_pairs, NULL,
     FMT_FLAG_ARG_CONVERT, 'w', '#', 'p', 0, 'L', 0,
     NULL, NULL
   }
@@ -841,7 +880,8 @@
 
 static void check_format_types (format_wanted_type *, const char *, int);
 static void format_type_warning (const char *, const char *, int, tree,
-				 int, const char *, tree, int);
+				 int, const char *, tree, int,
+				 format_suggestions *);
 
 /* Decode a format type from a string, returning the type, or
    format_type_error if not valid, in which case the caller should print an
@@ -1629,6 +1669,7 @@
 		  width_wanted_type.name = _("field width");
 		  width_wanted_type.param = cur_param;
 		  width_wanted_type.arg_num = arg_num;
+		  width_wanted_type.suggestions = NULL;
 		  width_wanted_type.next = NULL;
 		  if (last_wanted_type != 0)
 		    last_wanted_type->next = &width_wanted_type;
@@ -1731,6 +1772,7 @@
 		  precision_wanted_type.name = _("field precision");
 		  precision_wanted_type.param = cur_param;
 		  precision_wanted_type.arg_num = arg_num;
+		  precision_wanted_type.suggestions = NULL;
 		  precision_wanted_type.next = NULL;
 		  if (last_wanted_type != 0)
 		    last_wanted_type->next = &precision_wanted_type;
@@ -2101,6 +2143,7 @@
 	      wanted_type_ptr->name = NULL;
 	      wanted_type_ptr->param = cur_param;
 	      wanted_type_ptr->arg_num = arg_num;
+	      wanted_type_ptr->suggestions = fki->suggestions;
 	      wanted_type_ptr->next = NULL;
 	      if (last_wanted_type != 0)
 		last_wanted_type->next = wanted_type_ptr;
@@ -2127,6 +2170,15 @@
 }
 
 
+/* Check if an argument cannot be written to (for example, by the %n
+   conversion).  */
+static int
+arg_readonly (tree arg)
+{
+  return CONSTANT_CLASS_P (arg) || (DECL_P (arg) && TREE_READONLY (arg));
+}
+
+
 /* Check the argument types from a single format conversion (possibly
    including width and precision arguments).  */
 static void
@@ -2137,7 +2189,7 @@
     {
       tree cur_param;
       tree cur_type;
-      tree orig_cur_type;
+      tree orig_cur_param;
       tree wanted_type;
       int arg_num;
       int i;
@@ -2146,7 +2198,7 @@
       cur_type = TREE_TYPE (cur_param);
       if (cur_type == error_mark_node)
 	continue;
-      orig_cur_type = cur_type;
+      orig_cur_param = cur_param;
       char_type_flag = 0;
       wanted_type = types->wanted_type;
       arg_num = types->arg_num;
@@ -2201,10 +2253,7 @@
 	      if (types->writing_in_flag
 		  && i == 0
 		  && (TYPE_READONLY (cur_type)
-		      || (cur_param != 0
-			  && (CONSTANT_CLASS_P (cur_param)
-			      || (DECL_P (cur_param)
-				  && TREE_READONLY (cur_param))))))
+		      || (cur_param != 0 && arg_readonly (cur_param))))
 		warning (OPT_Wformat, "writing into constant object "
 			 "(argument %d)", arg_num);
 
@@ -2225,8 +2274,8 @@
 	    {
 	      format_type_warning (types->name, format_start, format_length,
 				   wanted_type, types->pointer_count,
-				   types->wanted_type_name, orig_cur_type,
-				   arg_num);
+				   types->wanted_type_name, orig_cur_param,
+				   arg_num, types->suggestions);
 	      break;
 	    }
 	}
@@ -2274,7 +2323,8 @@
       /* Now we have a type mismatch.  */
       format_type_warning (types->name, format_start, format_length,
 			   wanted_type, types->pointer_count,
-			   types->wanted_type_name, orig_cur_type, arg_num);
+			   types->wanted_type_name, orig_cur_param, arg_num,
+			   types->suggestions);
     }
 }
 
@@ -2286,15 +2336,19 @@
    FORMAT_LENGTH is its length.  WANTED_TYPE is the type the argument
    should have after POINTER_COUNT pointer dereferences.
    WANTED_NAME_NAME is a possibly more friendly name of WANTED_TYPE,
-   or NULL if the ordinary name of the type should be used.  ARG_TYPE
-   is the type of the actual argument.  ARG_NUM is the number of that
-   argument.  */
+   or NULL if the ordinary name of the type should be used.  ARG
+   is the actual argument.  ARG_NUM is the number of that
+   argument.  SUGGESTIONS is used to suggest format conversions that
+   are acceptable for the argument type provided.  It may be NULL.  */
 static void
 format_type_warning (const char *descr, const char *format_start,
 		     int format_length, tree wanted_type, int pointer_count,
-		     const char *wanted_type_name, tree arg_type, int arg_num)
+		     const char *wanted_type_name, tree arg, int arg_num,
+		     format_suggestions *suggestions)
 {
+  tree arg_type;
   char *p;
+  arg_type = TREE_TYPE (arg);
   /* If ARG_TYPE is a typedef with a misleading name (for example,
      size_t but not the standard size_t expected by printf %zu), avoid
      printing the typedef name.  */
@@ -2324,6 +2378,94 @@
       memset (p + 1, '*', pointer_count);
       p[pointer_count + 1] = 0;
     }
+
+  if (suggestions)
+    {
+      tree type = TREE_TYPE (arg);
+      tree strp_arg = arg;
+      STRIP_NOPS (strp_arg);
+      for ( ; suggestions->type; ++suggestions)
+	{
+	  tree sug_type;
+	  tree test_type;
+	  if (C_STD_VER < ADJ_STD (suggestions->std)
+	      && (suggestions->std != STD_EXT || flag_iso))
+	    continue;
+	  if (suggestions->flags & FMT_SUG_POINTER)
+	    {
+	      if (TREE_CODE (type) == POINTER_TYPE)
+		test_type = TREE_TYPE (type);
+	      else
+		continue;
+	    }
+	  else
+	    test_type = type;
+	  if (suggestions->flags & FMT_SUG_WRITE)
+	    {
+	      gcc_assert (suggestions->flags & FMT_SUG_POINTER);
+	      if (TYPE_READONLY (test_type)
+		  || (TREE_CODE (strp_arg) == ADDR_EXPR
+		      && arg_readonly (TREE_OPERAND (strp_arg, 0))))
+		continue;
+	    }
+	  sug_type = *suggestions->type;
+	  if (suggestions->name)
+	    {
+	      tree named_type = test_type;
+	      int match = 0;
+	      while (named_type)
+		{
+		  if ((named_type == size_type_node
+		       || named_type == ptrdiff_type_node
+		       || named_type == wchar_type_node)
+		      && named_type == sug_type)
+		    {
+		      match = 2;
+		      break;
+		    }
+		  else
+		    {
+		      if (TYPE_NAME (named_type)
+			  && TREE_CODE (TYPE_NAME (named_type)) == TYPE_DECL)
+			{
+			  const char *type_name;
+			  type_name = get_name (TYPE_NAME (named_type));
+			  named_type
+			    = DECL_ORIGINAL_TYPE (TYPE_NAME (named_type));
+			  if (type_name
+			      && strcmp (type_name, suggestions->name) == 0)
+			    {
+			      match = 1;
+			      break;
+			    }
+			}
+		      else
+			break;
+		    }
+		}
+	      if (match == 2)
+		break;
+	      else if (match == 0)
+		continue;
+	    }
+	  test_type = TYPE_MAIN_VARIANT (test_type);
+	  if (lang_hooks.types_compatible_p (test_type, sug_type))
+	    break;
+	  if (!(suggestions->flags & FMT_SUG_POINTER))
+	    {
+	      tree pro_type = lang_hooks.types.type_promotes_to (sug_type);
+	      if (lang_hooks.types_compatible_p (pro_type, test_type)
+		  && (TREE_CODE (arg) == NOP_EXPR
+		      || TREE_CODE (arg) == CONVERT_EXPR))
+		{
+		  tree orig_arg_type = TREE_TYPE (TREE_OPERAND (arg, 0));
+		  if (lang_hooks.types_compatible_p (sug_type, orig_arg_type))
+		    break;
+		}
+	    }
+	}
+    }
+
   if (wanted_type_name)
     {
       if (descr)
@@ -2347,6 +2489,17 @@
 		 "but argument %d has type %qT",
 		 format_length, format_start, wanted_type, p, arg_num, arg_type);
     }
+  if (suggestions && suggestions->type)
+    {
+      if (suggestions->name)
+	inform ("acceptable formats for type %<%s%s%> are: %s", suggestions->name,
+		suggestions->flags & FMT_SUG_POINTER ? " *" : "",
+		suggestions->desc);
+      else
+	inform ("acceptable formats for type %<%T%s%> are: %s", *suggestions->type,
+		suggestions->flags & FMT_SUG_POINTER ? " *" : "",
+		suggestions->desc);
+    }
 }
 
 
Index: gcc/c-format.h
===================================================================
--- gcc/c-format.h	(revision 138884)
+++ gcc/c-format.h	(working copy)
@@ -201,6 +201,33 @@
 } format_flag_pair;
 
 
+/* Flags used to specify types in format string suggestions.  */
+enum
+{
+  /* Set if the required type is a pointer to the type specified in the
+     format_suggestions structure.  */
+  FMT_SUG_POINTER = 1,
+  /* Set if the required type must be writable.  The FMT_SUG_POINTER flag
+     must also be set.  */
+  FMT_SUG_WRITE = 2
+};
+
+/* Structure mapping a type to format conversion suggestions.  */
+typedef struct
+{
+  /* The type wanted by the conversions.  */
+  tree *type;
+  /* A name for this type if it's typedef'd, otherwise NULL.  */
+  const char *name;
+  /* Flags specified by the FMT_SUG_ constants.  */
+  unsigned flags;
+  /* The standard to which the conversions belong.  */
+  enum format_std_version std;
+  /* The conversion suggestions for use with this type.  */
+  const char *desc;
+} format_suggestions;
+
+
 /* Structure describing a particular kind of format processed by GCC.  */
 typedef struct
 {
@@ -219,6 +246,8 @@
   const format_flag_spec *flag_specs;
   /* Details of bad combinations of flags.  */
   const format_flag_pair *bad_flag_pairs;
+  /* Suggestions for conversions based on type.  */
+  format_suggestions *suggestions;
   /* Flags applicable to this kind of format.  */
   int flags;
   /* Flag character to treat a width as, or 0 if width not used.  */
Index: gcc/c-typeck.c
===================================================================
--- gcc/c-typeck.c	(revision 138884)
+++ gcc/c-typeck.c	(working copy)
@@ -3620,6 +3620,12 @@
 	  || TREE_CODE (type) == UNION_TYPE)
 	pedwarn (OPT_pedantic, 
 		 "ISO C forbids casting nonscalar to the same type");
+      /* Add a conversion even if the main variants are the same since the
+	 types seen by the user may not be.  For example, if size_t has the
+	 same type as unsigned int, then "(unsigned) sizeof x" might be
+	 redundant, but for warning messages, the user wants the type to be
+	 thought of as	unsigned int.  */
+      value = convert (type, value);
     }
   else if (TREE_CODE (type) == UNION_TYPE)
     {
Index: gcc/c-common.c
===================================================================
--- gcc/c-common.c	(revision 138884)
+++ gcc/c-common.c	(working copy)
@@ -4016,6 +4016,19 @@
     mudflap_init ();
 }
 
+
+/* Look up the type with identifier NAME and return a copy of it.  We want
+   some of the standard, but non-primitive, types, such as size_t, to be
+   copies of the types they represent, so we can still tell them apart.  */
+
+static tree
+copy_type_with_name (const char *name)
+{
+  tree id = get_identifier (name);
+  return build_variant_type_copy (TREE_TYPE (identifier_global_value (id)));
+}
+
+
 /* Build tree nodes and builtin functions common to both C and C++ language
    frontends.  */
 
@@ -4107,8 +4120,7 @@
   /* `unsigned long' is the standard type for sizeof.
      Note that stddef.h uses `unsigned long',
      and this must agree, even if long and int are the same size.  */
-  size_type_node =
-    TREE_TYPE (identifier_global_value (get_identifier (SIZE_TYPE)));
+  size_type_node = copy_type_with_name (SIZE_TYPE);
   signed_size_type_node = c_common_signed_type (size_type_node);
   set_sizetype (size_type_node);
 
@@ -4252,8 +4264,7 @@
 			  (char_type_node, TYPE_QUAL_CONST));
 
   /* This is special for C++ so functions can be overloaded.  */
-  wchar_type_node = get_identifier (MODIFIED_WCHAR_TYPE);
-  wchar_type_node = TREE_TYPE (identifier_global_value (wchar_type_node));
+  wchar_type_node = copy_type_with_name (MODIFIED_WCHAR_TYPE);
   wchar_type_size = TYPE_PRECISION (wchar_type_node);
   if (c_dialect_cxx ())
     {
@@ -4314,8 +4325,7 @@
     TREE_TYPE (identifier_global_value (get_identifier (UINTMAX_TYPE)));
 
   default_function_type = build_function_type (integer_type_node, NULL_TREE);
-  ptrdiff_type_node
-    = TREE_TYPE (identifier_global_value (get_identifier (PTRDIFF_TYPE)));
+  ptrdiff_type_node = copy_type_with_name (PTRDIFF_TYPE);
   unsigned_ptrdiff_type_node = c_common_unsigned_type (ptrdiff_type_node);
 
   lang_hooks.decls.pushdecl

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2008-08-14 19:10 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-08-14 23:51 PATCH: Add format string suggestions to -Wformat warnings Dan Hipschman
2007-08-15  0:00 ` Andrew Pinski
2007-08-15  0:39 ` Joseph S. Myers
2007-08-21 22:58   ` Dan Hipschman
2007-09-13 23:20     ` Joseph S. Myers
2007-09-14  1:30       ` Ollie Wild
2007-09-14  3:51         ` Joseph S. Myers
2008-08-14 19:43       ` Dan Hipschman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).