diff mbox

[PR,c/52952] More precise locations within format strings

Message ID CAESRpQBeU=KbU_inFFzJxohXRhzebbZ0yR-D6D=jiuQkfycM=Q@mail.gmail.com
State New
Headers show

Commit Message

Manuel López-Ibáñez May 20, 2015, 8:15 a.m. UTC
This is a new version of the patch submitted here:

https://gcc.gnu.org/ml/gcc-patches/2014-11/msg00663.html

but handling (some) escape sequences.

I could not figure out a way to re-use the code from libcpp for this,
thus I implemented a simple function that given a string and offset in
bytes, it computes the visual column corresponding to that offset. The
function is very conservative: As soon as something unknown or
inconsistent is detected, it returns zero, thus preserving the current
behavior. This also preserves the current behavior for
non-concatenated tokens.

Bootstrapped and regression tested on x86_64-linux-gnu.

OK?


gcc/testsuite/ChangeLog:

2015-05-20  Manuel López-Ibáñez  <manu@gcc.gnu.org>

    PR c/52952
    * gcc.dg/redecl-4.c: Update column numbers.
    * gcc.dg/format/bitfld-1.c: Likewise.
    * gcc.dg/format/attr-2.c: Likewise.
    * gcc.dg/format/attr-6.c: Likewise.
    * gcc.dg/format/attr-7.c (baz): Likewise.
    * gcc.dg/format/asm_fprintf-1.c: Likewise.
    * gcc.dg/format/attr-4.c: Likewise.
    * gcc.dg/format/branch-1.c: Likewise.
    * gcc.dg/format/c90-printf-1.c: Likewise. Add tests for column
    locations within strings with embedded escape sequences.

gcc/c-family/ChangeLog:

2015-05-20  Manuel López-Ibáñez  <manu@gcc.gnu.org>

    PR c/52952
    * c-format.c (location_column_from_byte_offset): New.
    (location_from_offset): New.
    (struct format_wanted_type): Add offset_loc field.
    (check_format_info): Move handling of location for extra arguments
    closer to the point of warning.
    (check_format_arg): Set offset_is_invalid.
    (check_format_info_main): Pass the result of location_from_offset
    to warning_at.
    (format_type_warning): Pass the result of location_from_offset
    to warning_at.

Comments

Jeff Law May 20, 2015, 1:33 p.m. UTC | #1
On 05/20/2015 02:15 AM, Manuel López-Ibáñez wrote:
> This is a new version of the patch submitted here:
>
> https://gcc.gnu.org/ml/gcc-patches/2014-11/msg00663.html
>
> but handling (some) escape sequences.
>
> I could not figure out a way to re-use the code from libcpp for this,
> thus I implemented a simple function that given a string and offset in
> bytes, it computes the visual column corresponding to that offset. The
> function is very conservative: As soon as something unknown or
> inconsistent is detected, it returns zero, thus preserving the current
> behavior. This also preserves the current behavior for
> non-concatenated tokens.
>
> Bootstrapped and regression tested on x86_64-linux-gnu.
>
> OK?
>
>
> gcc/testsuite/ChangeLog:
>
> 2015-05-20  Manuel López-Ibáñez  <manu@gcc.gnu.org>
>
>      PR c/52952
>      * gcc.dg/redecl-4.c: Update column numbers.
>      * gcc.dg/format/bitfld-1.c: Likewise.
>      * gcc.dg/format/attr-2.c: Likewise.
>      * gcc.dg/format/attr-6.c: Likewise.
>      * gcc.dg/format/attr-7.c (baz): Likewise.
>      * gcc.dg/format/asm_fprintf-1.c: Likewise.
>      * gcc.dg/format/attr-4.c: Likewise.
>      * gcc.dg/format/branch-1.c: Likewise.
>      * gcc.dg/format/c90-printf-1.c: Likewise. Add tests for column
>      locations within strings with embedded escape sequences.
>
> gcc/c-family/ChangeLog:
>
> 2015-05-20  Manuel López-Ibáñez  <manu@gcc.gnu.org>
>
>      PR c/52952
>      * c-format.c (location_column_from_byte_offset): New.
>      (location_from_offset): New.
>      (struct format_wanted_type): Add offset_loc field.
>      (check_format_info): Move handling of location for extra arguments
>      closer to the point of warning.
>      (check_format_arg): Set offset_is_invalid.
>      (check_format_info_main): Pass the result of location_from_offset
>      to warning_at.
>      (format_type_warning): Pass the result of location_from_offset
>      to warning_at.
So if I'm understanding the situation correctly, with this new version 
behaviour for non-concatenated tokens is preserved which was the only 
behaviour regression in the prior patch, right?

Thus, this version of the patch is strictly an improvement (points to 
the issue within the format string rather than to the start of the 
string).  Right?

I don't particularly like file scoped "offset_is_invalid" variable.  It 
appears that it's only set within check_format_arg, but it's used from a 
variety of other locations via location_from_offset.  Given the current 
structure of the code, alternatives would be even uglier.

Ok for the trunk.

Thanks,
Jeff
Manuel López-Ibáñez May 20, 2015, 4:08 p.m. UTC | #2
On 20 May 2015 at 15:33, Jeff Law <law@redhat.com> wrote:
> So if I'm understanding the situation correctly, with this new version
> behaviour for non-concatenated tokens is preserved which was the only
> behaviour regression in the prior patch, right?

The new version will also handle most escape sequences correctly and
simply preserve the current location for those that are not handled.

> Thus, this version of the patch is strictly an improvement (points to the
> issue within the format string rather than to the start of the string).
> Right?

I hope so :)

> I don't particularly like file scoped "offset_is_invalid" variable.  It
> appears that it's only set within check_format_arg, but it's used from a
> variety of other locations via location_from_offset.  Given the current
> structure of the code, alternatives would be even uglier.

This comes from the previous version of the patch, but it is not
necessary anymore, since before using an offset, we try to read the
string at the location, and if there is no string, the offset is zero.

The variable is set here:

if (TREE_CODE (format_tree) == VAR_DECL
    && TREE_CODE (TREE_TYPE (format_tree)) == ARRAY_TYPE
    && (array_init = decl_constant_value (format_tree)) != format_tree
    && TREE_CODE (array_init) == STRING_CST)
      {
    /* Extract the string constant initializer.  Note that this may include
       a trailing NUL character that is not in the array (e.g.
       const char a[3] = "foo";).  */
    array_size = DECL_SIZE_UNIT (format_tree);
    format_tree = array_init;
        offset_is_invalid = true;
      }

to handle this case:

void foo()
{
  const char a[] = " %d ";
  __builtin_printf(a, 0.5);
}

in such a case, the location we get is the one of the use of 'a', thus
we cannot get at the actual string " %d " to find an offset. Thus, it
preserves the current (not ideal) behavior.

OK with offset_is_invalid removed after regtesting?

Cheers,

Manuel.
Jeff Law May 20, 2015, 4:12 p.m. UTC | #3
On 05/20/2015 10:08 AM, Manuel López-Ibáñez wrote:
>
>> I don't particularly like file scoped "offset_is_invalid" variable.  It
>> appears that it's only set within check_format_arg, but it's used from a
>> variety of other locations via location_from_offset.  Given the current
>> structure of the code, alternatives would be even uglier.
>
> This comes from the previous version of the patch, but it is not
> necessary anymore, since before using an offset, we try to read the
> string at the location, and if there is no string, the offset is zero.
Ah, well, if it isn't needed, then let's kill it :-)

>
> The variable is set here:
>
> if (TREE_CODE (format_tree) == VAR_DECL
>      && TREE_CODE (TREE_TYPE (format_tree)) == ARRAY_TYPE
>      && (array_init = decl_constant_value (format_tree)) != format_tree
>      && TREE_CODE (array_init) == STRING_CST)
>        {
>      /* Extract the string constant initializer.  Note that this may include
>         a trailing NUL character that is not in the array (e.g.
>         const char a[3] = "foo";).  */
>      array_size = DECL_SIZE_UNIT (format_tree);
>      format_tree = array_init;
>          offset_is_invalid = true;
>        }
>
> to handle this case:
>
> void foo()
> {
>    const char a[] = " %d ";
>    __builtin_printf(a, 0.5);
> }
>
> in such a case, the location we get is the one of the use of 'a', thus
> we cannot get at the actual string " %d " to find an offset. Thus, it
> preserves the current (not ideal) behavior.
>
> OK with offset_is_invalid removed after regtesting?
Yes.

jeff
diff mbox

Patch

Index: gcc/c-family/c-format.c
===================================================================
--- gcc/c-family/c-format.c	(revision 223371)
+++ gcc/c-family/c-format.c	(working copy)
@@ -76,10 +76,90 @@  static bool cmp_attribs (const char *tat
 
 static int first_target_format_type;
 static const char *format_name (int format_num);
 static int format_flags (int format_num);
 
+/* FIXME: This indicates that loc is not the location of the format
+   string, thus computing an offset is useless.  This happens, for
+   example, when the format string is a constant array.
+   Unfortunately, GCC does not keep track of the location of the
+   initializer of the array yet.  */
+static bool offset_is_invalid;
+
+/* Given a string S of length LINE_WIDTH, find the visual column
+   corresponding to OFFSET bytes.   */
+
+static unsigned int
+location_column_from_byte_offset (const char *s, int line_width,
+				  unsigned int offset)
+{
+  const char * c = s;
+  if (*c != '"')
+    return 0;
+
+  c++, offset--;
+  while (offset > 0)
+    {
+      if (c - s >= line_width)
+	return 0;
+
+      switch (*c)
+	{
+	case '\\':
+	  c++;
+	  if (c - s >= line_width)
+	    return 0;
+	  switch (*c)
+	    {
+	    case '\\': case '\'': case '"': case '?':
+	    case '(': case '{': case '[': case '%':
+	    case 'a': case 'b': case 'f': case 'n':
+	    case 'r': case 't': case 'v': 
+	    case 'e': case 'E':
+	      c++, offset--;
+	      break;
+
+	    default:
+	      return 0;
+	    }
+	  break;
+
+	case '"':
+	  /* We found the end of the string too early.  */
+	  return 0;
+	  
+	default:
+	  c++, offset--;
+	  break;
+	}
+    }
+  return c - s;
+}
+
+/* Return a location that encodes the same location as LOC but shifted
+   by OFFSET bytes.  */
+
+static location_t
+location_from_offset (location_t loc, int offset)
+{
+  gcc_checking_assert (offset >= 0);
+  if (offset_is_invalid
+      || linemap_location_from_macro_expansion_p (line_table, loc)
+      || offset < 0)
+    return loc;
+
+  expanded_location s = expand_location_to_spelling_point (loc);
+  int line_width;
+  const char *line = location_get_source_line (s, &line_width);
+  line += s.column - 1 ;
+  line_width -= s.column - 1;
+  unsigned int column = 
+    location_column_from_byte_offset (line, line_width, (unsigned) offset);
+
+  return linemap_position_for_loc_and_offset (line_table, loc, column);
+}
+
 /* Check that we have a pointer to a string suitable for use as a format.
    The default is to check for a char type.
    For objective-c dialects, this is extended to include references to string
    objects validated by objc_string_ref_type_p ().  
    Targets may also provide a string object type that can be used within c and 
@@ -388,10 +468,13 @@  typedef struct format_wanted_type
   int format_length;
   /* The actual parameter to check against the wanted type.  */
   tree param;
   /* The argument number of that parameter.  */
   int arg_num;
+  /* The offset location of this argument with respect to the format
+     string location.  */
+  unsigned int offset_loc;
   /* The next type to check for this format conversion, or NULL if none.  */
   struct format_wanted_type *next;
 } format_wanted_type;
 
 /* Convenience macro for format_length_info meaning unused.  */
@@ -1356,12 +1439,10 @@  check_format_info (function_format_info 
 
   check_function_arguments_recurse (check_format_arg, &format_ctx,
 				    format_tree, arg_num);
 
   location_t loc = format_ctx.res->format_string_loc;
-  if (res.extra_arg_loc == UNKNOWN_LOCATION)
-    res.extra_arg_loc = loc;
 
   if (res.number_non_literal > 0)
     {
       /* Functions taking a va_list normally pass a non-literal format
 	 string.  These functions typically are declared with
@@ -1403,12 +1484,16 @@  check_format_info (function_format_info 
      arguments, but was otherwise OK (either non-literal or checked OK).
      If the format is an empty string, this should be counted similarly to the
      case of extra format arguments.  */
   if (res.number_extra_args > 0 && res.number_non_literal == 0
       && res.number_other == 0)
-    warning_at (res.extra_arg_loc, OPT_Wformat_extra_args,
-		"too many arguments for format");
+    {
+      if (res.extra_arg_loc == UNKNOWN_LOCATION)
+	res.extra_arg_loc = loc;
+      warning_at (res.extra_arg_loc, OPT_Wformat_extra_args,
+		  "too many arguments for format");
+    }
   if (res.number_dollar_extra_args > 0 && res.number_non_literal == 0
       && res.number_other == 0)
     warning_at (loc, OPT_Wformat_extra_args, "unused arguments in $-style format");
   if (res.number_empty > 0 && res.number_non_literal == 0
       && res.number_other == 0)
@@ -1502,10 +1587,11 @@  check_format_arg (void *ctx, tree format
     {
       res->number_non_literal++;
       return;
     }
   res->format_string_loc = EXPR_LOC_OR_LOC (format_tree, input_location);
+  offset_is_invalid = false;
   format_tree = TREE_OPERAND (format_tree, 0);
   if (format_types[info->format_type].flags 
       & (int) FMT_FLAG_PARSE_ARG_CONVERT_EXTERNAL)
     {
       bool objc_str = (info->format_type == gcc_objc_string_format_type);
@@ -1555,10 +1641,11 @@  check_format_arg (void *ctx, tree format
       /* Extract the string constant initializer.  Note that this may include
 	 a trailing NUL character that is not in the array (e.g.
 	 const char a[3] = "foo";).  */
       array_size = DECL_SIZE_UNIT (format_tree);
       format_tree = array_init;
+      offset_is_invalid = true;
     }
   if (TREE_CODE (format_tree) != STRING_CST)
     {
       res->number_non_literal++;
       return;
@@ -1680,11 +1767,13 @@  check_format_info_main (format_check_res
 
       if (*format_chars++ != '%')
 	continue;
       if (*format_chars == 0)
 	{
-          warning_at (format_string_loc, OPT_Wformat_,
+          warning_at (location_from_offset (format_string_loc,
+					    format_chars - orig_format_chars),
+		      OPT_Wformat_,
 		      "spurious trailing %<%%%> in format");
 	  continue;
 	}
       if (*format_chars == '%')
 	{
@@ -1725,11 +1814,14 @@  check_format_info_main (format_check_res
 	{
 	  const format_flag_spec *s = get_flag_spec (flag_specs,
 						     *format_chars, NULL);
 	  if (strchr (flag_chars, *format_chars) != 0)
 	    {
-	      warning_at (format_string_loc, OPT_Wformat_,
+	      warning_at (location_from_offset (format_string_loc,
+						format_chars + 1
+						- orig_format_chars),
+			  OPT_Wformat_,
 			  "repeated %s in format", _(s->name));
 	    }
 	  else
 	    {
 	      i = strlen (flag_chars);
@@ -1805,10 +1897,12 @@  check_format_info_main (format_check_res
                   width_wanted_type.kind = CF_KIND_FIELD_WIDTH;
 		  width_wanted_type.format_start = format_chars - 1;
 		  width_wanted_type.format_length = 1;
 		  width_wanted_type.param = cur_param;
 		  width_wanted_type.arg_num = arg_num;
+		  width_wanted_type.offset_loc =
+		    format_chars - orig_format_chars;
 		  width_wanted_type.next = NULL;
 		  if (last_wanted_type != 0)
 		    last_wanted_type->next = &width_wanted_type;
 		  if (first_wanted_type == 0)
 		    first_wanted_type = &width_wanted_type;
@@ -1847,11 +1941,13 @@  check_format_info_main (format_check_res
 	  ++format_chars;
 	  i = strlen (flag_chars);
 	  flag_chars[i++] = fki->left_precision_char;
 	  flag_chars[i] = 0;
 	  if (!ISDIGIT (*format_chars))
-	    warning_at (format_string_loc, OPT_Wformat_,
+	    warning_at (location_from_offset (format_string_loc,
+					      format_chars - orig_format_chars),
+			OPT_Wformat_,
 			"empty left precision in %s format", fki->name);
 	  while (ISDIGIT (*format_chars))
 	    ++format_chars;
 	}
 
@@ -1912,10 +2008,12 @@  check_format_info_main (format_check_res
                   precision_wanted_type.kind = CF_KIND_FIELD_PRECISION;
 		  precision_wanted_type.param = cur_param;
 		  precision_wanted_type.format_start = format_chars - 2;
 		  precision_wanted_type.format_length = 2;
 		  precision_wanted_type.arg_num = arg_num;
+		  precision_wanted_type.offset_loc =
+		    format_chars - orig_format_chars;
 		  precision_wanted_type.next = NULL;
 		  if (last_wanted_type != 0)
 		    last_wanted_type->next = &precision_wanted_type;
 		  if (first_wanted_type == 0)
 		    first_wanted_type = &precision_wanted_type;
@@ -1924,11 +2022,13 @@  check_format_info_main (format_check_res
 	    }
 	  else
 	    {
 	      if (!(fki->flags & (int) FMT_FLAG_EMPTY_PREC_OK)
 		  && !ISDIGIT (*format_chars))
-		warning_at (format_string_loc, OPT_Wformat_,
+		warning_at (location_from_offset (format_string_loc,
+						  format_chars - orig_format_chars),
+			    OPT_Wformat_,
 			    "empty precision in %s format", fki->name);
 	      while (ISDIGIT (*format_chars))
 		++format_chars;
 	    }
 	}
@@ -2010,11 +2110,14 @@  check_format_info_main (format_check_res
 	    {
 	      if (strchr (flag_chars, *format_chars) != 0)
 		{
 		  const format_flag_spec *s = get_flag_spec (flag_specs,
 							     *format_chars, NULL);
-		  warning_at (format_string_loc, OPT_Wformat_,
+		  warning_at (location_from_offset (format_string_loc,
+						    format_chars 
+						    - orig_format_chars),
+			      OPT_Wformat_,
 			      "repeated %s in format", _(s->name));
 		}
 	      else
 		{
 		  i = strlen (flag_chars);
@@ -2028,11 +2131,13 @@  check_format_info_main (format_check_res
       format_char = *format_chars;
       if (format_char == 0
 	  || (!(fki->flags & (int) FMT_FLAG_FANCY_PERCENT_OK)
 	      && format_char == '%'))
 	{
-	  warning_at (format_string_loc, OPT_Wformat_,
+	  warning_at (location_from_offset (format_string_loc,
+					    format_chars - orig_format_chars),
+		      OPT_Wformat_,
 		      "conversion lacks type at end of format");
 	  continue;
 	}
       format_chars++;
       fci = fki->conversion_specs;
@@ -2040,23 +2145,29 @@  check_format_info_main (format_check_res
 	     && strchr (fci->format_chars, format_char) == 0)
 	  ++fci;
       if (fci->format_chars == 0)
 	{
 	  if (ISGRAPH (format_char))
-	    warning_at (format_string_loc, OPT_Wformat_,
+	    warning_at (location_from_offset (format_string_loc,
+					      format_chars - orig_format_chars),
+			OPT_Wformat_,
 			"unknown conversion type character %qc in format",
 			format_char);
 	  else
-	    warning_at (format_string_loc, OPT_Wformat_,
+	    warning_at (location_from_offset (format_string_loc,
+					      format_chars - orig_format_chars),
+			OPT_Wformat_,
 			"unknown conversion type character 0x%x in format",
 			format_char);
 	  continue;
 	}
       if (pedantic)
 	{
 	  if (ADJ_STD (fci->std) > C_STD_VER)
-	    warning_at (format_string_loc, OPT_Wformat_,
+	    warning_at (location_from_offset (format_string_loc,
+					      format_chars - orig_format_chars),
+			OPT_Wformat_,
 			"%s does not support the %<%%%c%> %s format",
 			C_STD_NAME (fci->std), format_char, fki->name);
 	}
 
       /* Validate the individual flags used, removing any that are invalid.  */
@@ -2069,12 +2180,14 @@  check_format_info_main (format_check_res
 	    flag_chars[i - d] = flag_chars[i];
 	    if (flag_chars[i] == fki->length_code_char)
 	      continue;
 	    if (strchr (fci->flag_chars, flag_chars[i]) == 0)
 	      {
-		warning_at (format_string_loc, 
-                            OPT_Wformat_, "%s used with %<%%%c%> %s format",
+		warning_at (location_from_offset (format_string_loc,
+						  format_chars 
+						  - orig_format_chars),
+			    OPT_Wformat_, "%s used with %<%%%c%> %s format",
 			    _(s->name), format_char, fki->name);
 		d++;
 		continue;
 	      }
 	    if (pedantic)
@@ -2184,11 +2297,13 @@  check_format_info_main (format_check_res
 	    ++format_chars;
 	  while (*format_chars && *format_chars != ']')
 	    ++format_chars;
 	  if (*format_chars != ']')
 	    /* The end of the format string was reached.  */
-	    warning_at (format_string_loc, OPT_Wformat_,
+	    warning_at (location_from_offset (format_string_loc,
+					      format_chars - orig_format_chars),
+			OPT_Wformat_,
 			"no closing %<]%> for %<%%[%> format");
 	}
 
       wanted_type = 0;
       wanted_type_name = 0;
@@ -2198,12 +2313,15 @@  check_format_info_main (format_check_res
 			 ? *fci->types[length_chars_val].type : 0);
 	  wanted_type_name = fci->types[length_chars_val].name;
 	  wanted_type_std = fci->types[length_chars_val].std;
 	  if (wanted_type == 0)
 	    {
-	      warning_at (format_string_loc, OPT_Wformat_,
-			  "use of %qs length modifier with %qc type character",
+	      warning_at (location_from_offset (format_string_loc,
+						format_chars - orig_format_chars),
+			  OPT_Wformat_,
+			  "use of %qs length modifier with %qc type character"
+			  " has either no effect or undefined behavior",
 			  length_chars, format_char);
 	      /* Heuristic: skip one argument when an invalid length/type
 		 combination is encountered.  */
 	      arg_num++;
 	      if (params != 0)
@@ -2216,11 +2334,13 @@  check_format_info_main (format_check_res
 		      have been warned for.  */
 		   && ADJ_STD (wanted_type_std) > ADJ_STD (length_chars_std)
 		   && ADJ_STD (wanted_type_std) > ADJ_STD (fci->std))
 	    {
 	      if (ADJ_STD (wanted_type_std) > C_STD_VER)
-		warning_at (format_string_loc, OPT_Wformat_,
+		warning_at (location_from_offset (format_string_loc,
+						  format_chars - orig_format_chars),
+			    OPT_Wformat_,
 			    "%s does not support the %<%%%s%c%> %s format",
 			    C_STD_NAME (wanted_type_std), length_chars,
 			    format_char, fki->name);
 	    }
 	}
@@ -2301,10 +2421,11 @@  check_format_info_main (format_check_res
               wanted_type_ptr->kind = CF_KIND_FORMAT;
 	      wanted_type_ptr->param = cur_param;
 	      wanted_type_ptr->arg_num = arg_num;
 	      wanted_type_ptr->format_start = format_start;
 	      wanted_type_ptr->format_length = format_chars - format_start;
+	      wanted_type_ptr->offset_loc = format_chars - orig_format_chars;
 	      wanted_type_ptr->next = NULL;
 	      if (last_wanted_type != 0)
 		last_wanted_type->next = wanted_type_ptr;
 	      if (first_wanted_type == 0)
 		first_wanted_type = wanted_type_ptr;
@@ -2325,11 +2446,13 @@  check_format_info_main (format_check_res
       if (first_wanted_type != 0)
         check_format_types (format_string_loc, first_wanted_type);
     }
 
   if (format_chars - orig_format_chars != format_length)
-    warning_at (format_string_loc, OPT_Wformat_contains_nul,
+    warning_at (location_from_offset (format_string_loc,
+				      format_chars + 1 - orig_format_chars),
+		OPT_Wformat_contains_nul,
 		"embedded %<\\0%> in format");
   if (info->first_arg_num != 0 && params != 0
       && has_operand_number <= 0)
     {
       res->number_other--;
@@ -2533,10 +2656,11 @@  format_type_warning (location_t loc, for
   const char *wanted_type_name = type->wanted_type_name;
   const char *format_start = type->format_start;
   int format_length = type->format_length;
   int pointer_count = type->pointer_count;
   int arg_num = type->arg_num;
+  unsigned int offset_loc = type->offset_loc;
 
   char *p;
   /* 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.  */
@@ -2566,10 +2690,12 @@  format_type_warning (location_t loc, for
       p[0] = ' ';
       memset (p + 1, '*', pointer_count);
       p[pointer_count + 1] = 0;
     }
 
+  loc = location_from_offset (loc, offset_loc);
+		      
   if (wanted_type_name)
     {
       if (arg_type)
         warning_at (loc, OPT_Wformat_,
 		    "%s %<%s%.*s%> expects argument of type %<%s%s%>, "
Index: gcc/testsuite/gcc.dg/redecl-4.c
===================================================================
--- gcc/testsuite/gcc.dg/redecl-4.c	(revision 223371)
+++ gcc/testsuite/gcc.dg/redecl-4.c	(working copy)
@@ -13,11 +13,11 @@  f (void)
     int printf (const char *, ...);
     int strcmp ();
     /* Should get format warnings even though the built-in declaration
        isn't "visible".  */
     printf (
-	    "%s", 1); /* { dg-warning "6:format" } */
+	    "%s", 1); /* { dg-warning "8:format" } */
     /* The type of strcmp here should have no prototype.  */
     if (0)
       strcmp (1);
     /* Likewise, implicitly declared memcmp.  */
     if (0)
Index: gcc/testsuite/gcc.dg/format/bitfld-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/bitfld-1.c	(revision 223371)
+++ gcc/testsuite/gcc.dg/format/bitfld-1.c	(working copy)
@@ -44,8 +44,8 @@  foo (void)
   printf ("%d%u", x.s32, x.s32);
 #else
   printf ("%ld%lu", x.u32, x.u32);
   printf ("%ld%lu", x.s32, x.s32);
 #endif
-  printf ("%llu", x.u48); /* { dg-warning "11:has type '.*unsigned int:48'" } */
+  printf ("%llu", x.u48); /* { dg-warning "15:has type '.*unsigned int:48'" } */
   printf ("%llu", (unsigned long long)x.u48);
 }
Index: gcc/testsuite/gcc.dg/format/attr-2.c
===================================================================
--- gcc/testsuite/gcc.dg/format/attr-2.c	(revision 223371)
+++ gcc/testsuite/gcc.dg/format/attr-2.c	(working copy)
@@ -28,17 +28,17 @@  extern char *t__format_arg__ (const char
 
 void
 foo (int i, int *ip, double d)
 {
   tformatprintf ("%d", i);
-  tformatprintf ("%"); /* { dg-warning "18:format" "attribute format printf" } */
+  tformatprintf ("%"); /* { dg-warning "19:format" "attribute format printf" } */
   tformat__printf__ ("%d", i);
   tformat__printf__ ("%"); /* { dg-warning "format" "attribute format __printf__" } */
   tformatscanf ("%d", ip);
   tformatscanf ("%"); /* { dg-warning "format" "attribute format scanf" } */
   tformat__scanf__ ("%d", ip);
-  tformat__scanf__ ("%"); /* { dg-warning "format" "attribute format __scanf__" } */
+  tformat__scanf__ ("%"); /* { dg-warning "22:format" "attribute format __scanf__" } */
   tformatstrftime ("%a");
   tformatstrftime ("%"); /* { dg-warning "format" "attribute format strftime" } */
   tformat__strftime__ ("%a");
   tformat__strftime__ ("%"); /* { dg-warning "format" "attribute format __strftime__" } */
   tformatstrfmon ("%n", d);
Index: gcc/testsuite/gcc.dg/format/attr-6.c
===================================================================
--- gcc/testsuite/gcc.dg/format/attr-6.c	(revision 223371)
+++ gcc/testsuite/gcc.dg/format/attr-6.c	(working copy)
@@ -15,8 +15,8 @@ 
    of it as a built-in function.  */
 
 void
 foo (const char *s, int *p)
 {
-  scanf("%ld", p); /* { dg-warning "9:format" "implicit scanf" } */
+  scanf("%ld", p); /* { dg-warning "12:format" "implicit scanf" } */
   /* { dg-warning "implicit" "implicit decl warning" { target *-*-* } 20 } */
 }
Index: gcc/testsuite/gcc.dg/format/attr-7.c
===================================================================
--- gcc/testsuite/gcc.dg/format/attr-7.c	(revision 223371)
+++ gcc/testsuite/gcc.dg/format/attr-7.c	(working copy)
@@ -16,11 +16,11 @@  char * (__attribute__((format_arg(1))) *
 void
 baz (int i)
 {
   (*tformatprintf0) ("%d", i);
   (*tformatprintf0) ((*tformat_arg) ("%d"), i);
-  (*tformatprintf0) ("%"); /* { dg-warning "22:format" "prefix" } */
+  (*tformatprintf0) ("%"); /* { dg-warning "23:format" "prefix" } */
   (*tformatprintf0) ((*tformat_arg) ("%")); /* { dg-warning "format" "prefix" } */
   (*tformatprintf1) ("%d", i);
   (*tformatprintf1) ((*tformat_arg) ("%d"), i);
   (*tformatprintf1) ("%"); /* { dg-warning "format" "postfix" } */
   (*tformatprintf1) ((*tformat_arg) ("%")); /* { dg-warning "format" "postfix" } */
Index: gcc/testsuite/gcc.dg/format/asm_fprintf-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/asm_fprintf-1.c	(revision 223371)
+++ gcc/testsuite/gcc.dg/format/asm_fprintf-1.c	(working copy)
@@ -40,41 +40,42 @@  foo (int i, int i1, int i2, unsigned int
   asm_fprintf ("%O%R%I%L%U%@");
   asm_fprintf ("%r", i);
   asm_fprintf ("%wd%wi%wo%wu%wx%wX", ll, ll, ull, ull, ull, ull);
 
   /* Standard specifiers not accepted in asm_fprintf.  */
-  asm_fprintf ("%f\n", d); /* { dg-warning "16:format" "float" } */
-  asm_fprintf ("%e\n", d); /* { dg-warning "16:format" "float" } */
-  asm_fprintf ("%E\n", d); /* { dg-warning "16:format" "float" } */
-  asm_fprintf ("%g\n", d); /* { dg-warning "16:format" "float" } */
-  asm_fprintf ("%G\n", d); /* { dg-warning "16:format" "float" } */
-  asm_fprintf ("%p\n", p); /* { dg-warning "16:format" "pointer" } */
-  asm_fprintf ("%n\n", n); /* { dg-warning "16:format" "counter" } */
-  asm_fprintf ("%hd\n", i); /* { dg-warning "16:format" "conversion" } */
+  asm_fprintf ("%f\n", d); /* { dg-warning "18:format" "float" } */
+  asm_fprintf ("%e\n", d); /* { dg-warning "18:format" "float" } */
+  asm_fprintf ("%E\n", d); /* { dg-warning "18:format" "float" } */
+  asm_fprintf ("%g\n", d); /* { dg-warning "18:format" "float" } */
+  asm_fprintf ("%G\n", d); /* { dg-warning "18:format" "float" } */
+  asm_fprintf ("%p\n", p); /* { dg-warning "18:format" "pointer" } */
+  asm_fprintf ("%n\n", n); /* { dg-warning "18:format" "counter" } */
+  asm_fprintf ("%hd\n", i); /* { dg-warning "18:format" "conversion" } */
 
   /* Various tests of bad argument types.  */
-  asm_fprintf ("%d", l); /* { dg-warning "16:format" "bad argument types" } */
-  asm_fprintf ("%wd", l); /* { dg-warning "16:format" "bad argument types" } */
-  asm_fprintf ("%d", ll); /* { dg-warning "16:format" "bad argument types" } */
-  asm_fprintf ("%*d\n", i1, i); /* { dg-warning "16:format" "bad * argument types" } */
-  asm_fprintf ("%.*d\n", i2, i); /* { dg-warning "16:format" "bad * argument types" } */
-  asm_fprintf ("%*.*ld\n", i1, i2, l); /* { dg-warning "16:format" "bad * argument types" } */
-  asm_fprintf ("%ld", i); /* { dg-warning "16:format" "bad argument types" } */
-  asm_fprintf ("%s", n); /* { dg-warning "16:format" "bad argument types" } */
+  asm_fprintf ("%d", l); /* { dg-warning "18:format" "bad argument types" } */
+  asm_fprintf ("%wd", l); /* { dg-warning "19:format" "bad argument types" } */
+  asm_fprintf ("%d", ll); /* { dg-warning "18:format" "bad argument types" } */
+  asm_fprintf ("%*d\n", i1, i); /* { dg-warning "18:format" "bad * argument types" } */
+  asm_fprintf ("%.*d\n", i2, i); /* { dg-warning "19:format" "bad * argument types" } */
+  asm_fprintf ("%*.*ld\n", i1, i2, l); /* { dg-warning "18:format" "bad * argument types" } */
+  asm_fprintf ("%ld", i); /* { dg-warning "19:format" "bad argument types" } */
+  asm_fprintf ("%s", n); /* { dg-warning "18:format" "bad argument types" } */
 
   /* Wrong number of arguments.  */
-  asm_fprintf ("%d%d", i); /* { dg-warning "16:matching" "wrong number of args" } */
+  asm_fprintf ("%d%d", i); /* { dg-warning "20:matching" "wrong number of args" } */
   asm_fprintf ("%d", i, i); /* { dg-warning "16:arguments" "wrong number of args" } */
   /* Miscellaneous bogus constructions.  */
   asm_fprintf (""); /* { dg-warning "16:zero-length" "warning for empty format" } */
-  asm_fprintf ("\0"); /* { dg-warning "16:embedded" "warning for embedded NUL" } */
-  asm_fprintf ("%d\0", i); /* { dg-warning "16:embedded" "warning for embedded NUL" } */
-  asm_fprintf ("%d\0%d", i, i); /* { dg-warning "16:embedded|too many" "warning for embedded NUL" } */
+  asm_fprintf ("\0"); /* { dg-warning "17:embedded" "warning for embedded NUL" } */
+  asm_fprintf ("%d\0", i); /* { dg-warning "19:embedded" "warning for embedded NUL" } */
+  asm_fprintf ("%d\0%d", i, i); /* { dg-warning "19:embedded|too many" "warning for embedded NUL" } */
   asm_fprintf (NULL); /* { dg-warning "null" "null format string warning" } */
-  asm_fprintf ("%"); /* { dg-warning "16:trailing" "trailing % warning" } */
-  asm_fprintf ("%++d", i); /* { dg-warning "16:repeated" "repeated flag warning" } */
+  asm_fprintf ("%"); /* { dg-warning "17:trailing" "trailing % warning" } */
+  asm_fprintf ("%++d", i); /* { dg-warning "19:repeated" "repeated flag warning" } */
   asm_fprintf ((const char *)L"foo"); /* { dg-warning "30:wide" "wide string" } */
   asm_fprintf ("%s", (char *)0); /* { dg-warning "null" "%s with NULL" } */
 
   /* Make sure we still get warnings for regular printf.  */
-  printf ("%d\n", ll); /* { dg-warning "11:format" "bad argument types" } */
+  printf ("%d\n", ll); /* { dg-warning "13:format" "bad argument types" } */
 }
+/* { dg-warning "16:too many arguments for format" "too many arguments" { target *-*-* } 0 } */
Index: gcc/testsuite/gcc.dg/format/attr-4.c
===================================================================
--- gcc/testsuite/gcc.dg/format/attr-4.c	(revision 223371)
+++ gcc/testsuite/gcc.dg/format/attr-4.c	(working copy)
@@ -14,11 +14,11 @@  extern __attribute__((noreturn)) void ba
 
 void
 baz (int i, int *ip, double d)
 {
   tformatprintf0 ("%d", i);
-  tformatprintf0 ("%"); /* { dg-warning "19:format" "attribute format printf case 0" } */
+  tformatprintf0 ("%"); /* { dg-warning "20:format" "attribute format printf case 0" } */
   tformatprintf1 ("%d", i);
   tformatprintf1 ("%"); /* { dg-warning "format" "attribute format printf case 1" } */
   tformatprintf2 ("%d", i);
   tformatprintf2 ("%"); /* { dg-warning "format" "attribute format printf case 2" } */
   tformatprintf3 ("%d", i);
Index: gcc/testsuite/gcc.dg/format/branch-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/branch-1.c	(revision 223371)
+++ gcc/testsuite/gcc.dg/format/branch-1.c	(working copy)
@@ -7,22 +7,22 @@ 
 
 void
 foo (long l, int nfoo)
 {
   printf ((nfoo > 1) ? "%d foos" : "%d foo", nfoo);
-  printf ((l > 1) ? "%d foos" /* { dg-warning "21:int" "wrong type in conditional expr" } */
-	          : "%d foo", l); /* { dg-warning "14:int" "wrong type in conditional expr" } */
-  printf ((l > 1) ? "%ld foos" : "%d foo", l); /* { dg-warning "34:int" "wrong type in conditional expr" } */
-  printf ((l > 1) ? "%d foos" : "%ld foo", l); /* { dg-warning "21:int" "wrong type in conditional expr" } */
+  printf ((l > 1) ? "%d foos" /* { dg-warning "23:int" "wrong type in conditional expr" } */
+	          : "%d foo", l); /* { dg-warning "16:int" "wrong type in conditional expr" } */
+  printf ((l > 1) ? "%ld foos" : "%d foo", l); /* { dg-warning "36:int" "wrong type in conditional expr" } */
+  printf ((l > 1) ? "%d foos" : "%ld foo", l); /* { dg-warning "23:int" "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 "38: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 "61:long int" "wrong type" } */
-  printf ((nfoo > 1) ? "%ld foos" : ((nfoo > 0) ? "%d foo" : "%d foos"), nfoo); /* { dg-warning "24:long int" "wrong type" } */
-  printf ((nfoo > 1) ? "%d foos" : ((nfoo > 0) ? "%ld foo" : "%d foos"), nfoo); /* { dg-warning "50:long int" "wrong type" } */
+  printf ((nfoo > 1) ? "%d foos" : ((nfoo > 0) ? "%d foo" : "%ld foos"), nfoo); /* { dg-warning "64:long int" "wrong type" } */
+  printf ((nfoo > 1) ? "%ld foos" : ((nfoo > 0) ? "%d foo" : "%d foos"), nfoo); /* { dg-warning "27:long int" "wrong type" } */
+  printf ((nfoo > 1) ? "%d foos" : ((nfoo > 0) ? "%ld foo" : "%d foos"), nfoo); /* { dg-warning "53:long int" "wrong type" } */
   /* Extra arguments to NULL should be complained about.  */
   printf (0, "foo"); /* { dg-warning "14:too many" "NULL extra args" } */
   /* { dg-warning "null" "null format arg" { target *-*-* } 26 } */
 }
Index: gcc/testsuite/gcc.dg/format/c90-printf-1.c
===================================================================
--- gcc/testsuite/gcc.dg/format/c90-printf-1.c	(revision 223371)
+++ gcc/testsuite/gcc.dg/format/c90-printf-1.c	(working copy)
@@ -32,32 +32,39 @@  foo (int i, int i1, int i2, unsigned int
   /* GCC has objected to the next one in the past, but it is a valid way
      of specifying zero precision.
   */
   printf ("%.e\n", d); /* { dg-bogus "precision" "bogus precision warning" } */
   /* Bogus use of width.  */
-  printf ("%5n\n", n); /* { dg-warning "11:width" "width with %n" } */
+  printf ("%5n\n", n); /* { dg-warning "14:width" "width with %n" } */
   /* Erroneous, ignored or pointless constructs with precision.  */
   /* Whether negative values for precision may be included in the format
      string is not entirely clear; presume not, following Clive Feather's
      proposed resolution to DR#220 against C99.  In any case, such a
      construct should be warned about.
   */
-  printf ("%.-5d\n", i); /* { dg-warning "11:format|precision" "negative precision warning" } */
-  printf ("%.-*d\n", i); /* { dg-warning "11:format" "broken %.-*d format" } */
-  printf ("%.3c\n", i); /* { dg-warning "11:precision" "precision with %c" } */
-  printf ("%.3p\n", p); /* { dg-warning "11:precision" "precision with %p" } */
-  printf ("%.3n\n", n); /* { dg-warning "11:precision" "precision with %n" } */
+  printf ("%.-5d\n", i); /* { dg-warning "14:format|precision" "negative precision warning" } */
+  /* { dg-warning "too many arguments for format" "too many arguments" { target *-*-* } 44 } */
+  printf ("%.-*d\n", i); /* { dg-warning "14:format" "broken %.-*d format" } */
+  /* { dg-warning "too many arguments for format" "too many arguments" { target *-*-* } 46 } */
+  printf ("%.3c\n", i); /* { dg-warning "15:precision" "precision with %c" } */
+  printf ("%.3p\n", p); /* { dg-warning "15:precision" "precision with %p" } */
+  printf ("%.3n\n", n); /* { dg-warning "15:precision" "precision with %n" } */
   /* Valid and invalid %% constructions.  Some of the warning messages
      are non-optimal, but they do detect the errorneous nature of the
      format string.
   */
   printf ("%%");
-  printf ("%.3%"); /* { dg-warning "11:format" "bogus %%" } */
-  printf ("%-%"); /* { dg-warning "11:format" "bogus %%" } */
-  printf ("%-%\n"); /* { dg-warning "11:format" "bogus %%" } */
-  printf ("%5%\n"); /* { dg-warning "11:format" "bogus %%" } */
-  printf ("%h%\n"); /* { dg-warning "11:format" "bogus %%" } */
+  printf ("%.3%"); /* { dg-warning "14:type" "missing type" } */
+  /* { dg-warning "15:trailing" "bogus %%" { target *-*-* } 56 } */
+  printf ("%-%"); /* { dg-warning "13:type" "missing type" } */
+  /* { dg-warning "14:trailing" "bogus %%" { target *-*-* } 58 } */
+  printf ("%-%\n"); /* { dg-warning "13:format" "bogus %%" } */
+  /* { dg-warning "15:format" "bogus %%" { target *-*-* } 60 } */
+  printf ("%5%\n"); /* { dg-warning "13:format" "bogus %%" } */
+  /* { dg-warning "15:format" "bogus %%" { target *-*-* } 62 } */
+  printf ("%h%\n"); /* { dg-warning "13:format" "bogus %%" } */
+  /* { dg-warning "15:format" "bogus %%" { target *-*-* } 64 } */
   /* Valid and invalid %h, %l, %L constructions.  */
   printf ("%hd", i);
   printf ("%hi", i);
   /* Strictly, these parameters should be int or unsigned int according to
      what unsigned short promotes to.  However, GCC ignores sign
@@ -65,98 +72,98 @@  foo (int i, int i1, int i2, unsigned int
      correct checking without print_char_table needing to know whether
      int and short are the same size.
   */
   printf ("%ho%hu%hx%hX", u, u, u, u);
   printf ("%hn", hn);
-  printf ("%hf", d); /* { dg-warning "11:length" "bad use of %h" } */
-  printf ("%he", d); /* { dg-warning "11:length" "bad use of %h" } */
-  printf ("%hE", d); /* { dg-warning "11:length" "bad use of %h" } */
-  printf ("%hg", d); /* { dg-warning "11:length" "bad use of %h" } */
-  printf ("%hG", d); /* { dg-warning "11:length" "bad use of %h" } */
-  printf ("%hc", i); /* { dg-warning "11:length" "bad use of %h" } */
-  printf ("%hs", s); /* { dg-warning "11:length" "bad use of %h" } */
-  printf ("%hp", p); /* { dg-warning "11:length" "bad use of %h" } */
-  printf ("%h"); /* { dg-warning "11:conversion lacks type" "bare %h" } */
-  printf ("%h."); /* { dg-warning "11:conversion" "bogus %h." } */
-  printf ("%ld%li%lo%lu%lx%lX", l, l, ul, ul, ul, ul);
-  printf ("%ln", ln);
-  printf ("%lf", d); /* { dg-warning "11:length|C" "bad use of %l" } */
-  printf ("%le", d); /* { dg-warning "11:length|C" "bad use of %l" } */
-  printf ("%lE", d); /* { dg-warning "11:length|C" "bad use of %l" } */
-  printf ("%lg", d); /* { dg-warning "11:length|C" "bad use of %l" } */
-  printf ("%lG", d); /* { dg-warning "11:length|C" "bad use of %l" } */
-  printf ("%lp", p); /* { dg-warning "11:length|C" "bad use of %l" } */
+  printf (" %hf", d); /* { dg-warning "15:length" "bad use of %h" } */
+  printf (" %he", d); /* { dg-warning "15:length" "bad use of %h" } */
+  printf (" %hE", d); /* { dg-warning "15:length" "bad use of %h" } */
+  printf (" %hg", d); /* { dg-warning "15:length" "bad use of %h" } */
+  printf (" %hG", d); /* { dg-warning "15:length" "bad use of %h" } */
+  printf (" %hc", i); /* { dg-warning "15:length" "bad use of %h" } */
+  printf (" %hs", s); /* { dg-warning "15:length" "bad use of %h" } */
+  printf (" %hp", p); /* { dg-warning "15:length" "bad use of %h" } */
+  printf (" %h"); /* { dg-warning "14:conversion lacks type" "bare %h" } */
+  printf (" %h."); /* { dg-warning "15:conversion" "bogus %h." } */
+  printf (" %ld%li%lo%lu%lx%lX", l, l, ul, ul, ul, ul);
+  printf (" %ln", ln);
+  printf (" %lf", d); /* { dg-warning "15:length|C" "bad use of %l" } */
+  printf (" %le", d); /* { dg-warning "15:length|C" "bad use of %l" } */
+  printf (" %lE", d); /* { dg-warning "15:length|C" "bad use of %l" } */
+  printf (" %lg", d); /* { dg-warning "15:length|C" "bad use of %l" } */
+  printf (" %lG", d); /* { dg-warning "15:length|C" "bad use of %l" } */
+  printf (" %lp", p); /* { dg-warning "15:length|C" "bad use of %l" } */
   /* These next two were added in C94, but should be objected to in C90.
      For the first one, GCC has wanted wchar_t instead of the correct C94
      and C99 wint_t.
   */
-  printf ("%lc", lc); /* { dg-warning "11:length|C" "C90 bad use of %l" } */
-  printf ("%ls", ls); /* { dg-warning "11:length|C" "C90 bad use of %l" } */
+  printf ("%lc", lc); /* { dg-warning "14:length|C" "C90 bad use of %l" } */
+  printf ("%ls", ls); /* { dg-warning "14:length|C" "C90 bad use of %l" } */
   /* These uses of %L are legitimate, though GCC has wrongly warned for
      them in the past.
   */
   printf ("%Le%LE%Lf%Lg%LG", ld, ld, ld, ld, ld);
   /* These next six are accepted by GCC as referring to long long,
      but -pedantic correctly warns.
   */
-  printf ("%Ld", ll); /* { dg-warning "11:does not support" "bad use of %L" } */
-  printf ("%Li", ll); /* { dg-warning "11:does not support" "bad use of %L" } */
-  printf ("%Lo", ull); /* { dg-warning "11:does not support" "bad use of %L" } */
-  printf ("%Lu", ull); /* { dg-warning "11:does not support" "bad use of %L" } */
-  printf ("%Lx", ull); /* { dg-warning "11:does not support" "bad use of %L" } */
-  printf ("%LX", ull); /* { dg-warning "11:does not support" "bad use of %L" } */
-  printf ("%Lc", i); /* { dg-warning "11:length" "bad use of %L" } */
-  printf ("%Ls", s); /* { dg-warning "11:length" "bad use of %L" } */
-  printf ("%Lp", p); /* { dg-warning "11:length" "bad use of %L" } */
-  printf ("%Ln", n); /* { dg-warning "11:length" "bad use of %L" } */
+  printf ("%Ld", ll); /* { dg-warning "14:does not support" "bad use of %L" } */
+  printf ("%Li", ll); /* { dg-warning "14:does not support" "bad use of %L" } */
+  printf ("%Lo", ull); /* { dg-warning "14:does not support" "bad use of %L" } */
+  printf ("%Lu", ull); /* { dg-warning "14:does not support" "bad use of %L" } */
+  printf ("%Lx", ull); /* { dg-warning "14:does not support" "bad use of %L" } */
+  printf ("%LX", ull); /* { dg-warning "14:does not support" "bad use of %L" } */
+  printf ("%Lc", i); /* { dg-warning "14:length" "bad use of %L" } */
+  printf ("%Ls", s); /* { dg-warning "14:length" "bad use of %L" } */
+  printf ("%Lp", p); /* { dg-warning "14:length" "bad use of %L" } */
+  printf ("%Ln", n); /* { dg-warning "14:length" "bad use of %L" } */
   /* Valid uses of each bare conversion.  */
   printf ("%d%i%o%u%x%X%f%e%E%g%G%c%s%p%n%%", i, i, u, u, u, u, d, d, d, d, d,
 	  i, s, p, n);
   /* Uses of the - flag (valid on all non-%, non-n conversions).  */
   printf ("%-d%-i%-o%-u%-x%-X%-f%-e%-E%-g%-G%-c%-s%-p", i, i, u, u, u, u,
 	  d, d, d, d, d, i, s, p);
-  printf ("%-n", n); /* { dg-warning "11:flag" "bad use of %-n" } */
+  printf ("%-n", n); /* { dg-warning "14:flag" "bad use of %-n" } */
   /* Uses of the + flag (valid on signed conversions only).  */
   printf ("%+d%+i%+f%+e%+E%+g%+G\n", i, i, d, d, d, d, d);
-  printf ("%+o", u); /* { dg-warning "11:flag" "bad use of + flag" } */
-  printf ("%+u", u); /* { dg-warning "11:flag" "bad use of + flag" } */
-  printf ("%+x", u); /* { dg-warning "11:flag" "bad use of + flag" } */
-  printf ("%+X", u); /* { dg-warning "11:flag" "bad use of + flag" } */
-  printf ("%+c", i); /* { dg-warning "11:flag" "bad use of + flag" } */
-  printf ("%+s", s); /* { dg-warning "11:flag" "bad use of + flag" } */
-  printf ("%+p", p); /* { dg-warning "11:flag" "bad use of + flag" } */
-  printf ("%+n", n); /* { dg-warning "11:flag" "bad use of + flag" } */
+  printf ("%+o", u); /* { dg-warning "14:flag" "bad use of + flag" } */
+  printf ("%+u", u); /* { dg-warning "14:flag" "bad use of + flag" } */
+  printf ("%+x", u); /* { dg-warning "14:flag" "bad use of + flag" } */
+  printf ("%+X", u); /* { dg-warning "14:flag" "bad use of + flag" } */
+  printf ("%+c", i); /* { dg-warning "14:flag" "bad use of + flag" } */
+  printf ("%+s", s); /* { dg-warning "14:flag" "bad use of + flag" } */
+  printf ("%+p", p); /* { dg-warning "14:flag" "bad use of + flag" } */
+  printf ("%+n", n); /* { dg-warning "14:flag" "bad use of + flag" } */
   /* Uses of the space flag (valid on signed conversions only, and ignored
      with +).
   */
   printf ("% +d", i); /* { dg-warning "11:use of both|ignored" "use of space and + flags" } */
   printf ("%+ d", i); /* { dg-warning "11:use of both|ignored" "use of space and + flags" } */
   printf ("% d% i% f% e% E% g% G\n", i, i, d, d, d, d, d);
-  printf ("% o", u); /* { dg-warning "11:flag" "bad use of space flag" } */
-  printf ("% u", u); /* { dg-warning "11:flag" "bad use of space flag" } */
-  printf ("% x", u); /* { dg-warning "11:flag" "bad use of space flag" } */
-  printf ("% X", u); /* { dg-warning "11:flag" "bad use of space flag" } */
-  printf ("% c", i); /* { dg-warning "11:flag" "bad use of space flag" } */
-  printf ("% s", s); /* { dg-warning "11:flag" "bad use of space flag" } */
-  printf ("% p", p); /* { dg-warning "11:flag" "bad use of space flag" } */
-  printf ("% n", n); /* { dg-warning "11:flag" "bad use of space flag" } */
+  printf ("% o", u); /* { dg-warning "14:flag" "bad use of space flag" } */
+  printf ("% u", u); /* { dg-warning "14:flag" "bad use of space flag" } */
+  printf ("% x", u); /* { dg-warning "14:flag" "bad use of space flag" } */
+  printf ("% X", u); /* { dg-warning "14:flag" "bad use of space flag" } */
+  printf ("% c", i); /* { dg-warning "14:flag" "bad use of space flag" } */
+  printf ("% s", s); /* { dg-warning "14:flag" "bad use of space flag" } */
+  printf ("% p", p); /* { dg-warning "14:flag" "bad use of space flag" } */
+  printf ("% n", n); /* { dg-warning "14:flag" "bad use of space flag" } */
   /* Uses of the # flag.  */
   printf ("%#o%#x%#X%#e%#E%#f%#g%#G", u, u, u, d, d, d, d, d);
-  printf ("%#d", i); /* { dg-warning "11:flag" "bad use of # flag" } */
-  printf ("%#i", i); /* { dg-warning "11:flag" "bad use of # flag" } */
-  printf ("%#u", u); /* { dg-warning "11:flag" "bad use of # flag" } */
-  printf ("%#c", i); /* { dg-warning "11:flag" "bad use of # flag" } */
-  printf ("%#s", s); /* { dg-warning "11:flag" "bad use of # flag" } */
-  printf ("%#p", p); /* { dg-warning "11:flag" "bad use of # flag" } */
-  printf ("%#n", n); /* { dg-warning "11:flag" "bad use of # flag" } */
+  printf ("%#d", i); /* { dg-warning "14:flag" "bad use of # flag" } */
+  printf ("%#i", i); /* { dg-warning "14:flag" "bad use of # flag" } */
+  printf ("%#u", u); /* { dg-warning "14:flag" "bad use of # flag" } */
+  printf ("%#c", i); /* { dg-warning "14:flag" "bad use of # flag" } */
+  printf ("%#s", s); /* { dg-warning "14:flag" "bad use of # flag" } */
+  printf ("%#p", p); /* { dg-warning "14:flag" "bad use of # flag" } */
+  printf ("%#n", n); /* { dg-warning "14:flag" "bad use of # flag" } */
   /* Uses of the 0 flag.  */
   printf ("%08d%08i%08o%08u%08x%08X%08e%08E%08f%08g%08G", i, i, u, u, u, u,
 	  d, d, d, d, d);
-  printf ("%0c", i); /* { dg-warning "11:flag" "bad use of 0 flag" } */
-  printf ("%0s", s); /* { dg-warning "11:flag" "bad use of 0 flag" } */
-  printf ("%0p", p); /* { dg-warning "11:flag" "bad use of 0 flag" } */
-  printf ("%0n", n); /* { dg-warning "11:flag" "bad use of 0 flag" } */
+  printf ("%0c", i); /* { dg-warning "14:flag" "bad use of 0 flag" } */
+  printf ("%0s", s); /* { dg-warning "14:flag" "bad use of 0 flag" } */
+  printf ("%0p", p); /* { dg-warning "14:flag" "bad use of 0 flag" } */
+  printf ("%0n", n); /* { dg-warning "14:flag" "bad use of 0 flag" } */
   /* 0 flag ignored with precision for certain types, not others.  */
   printf ("%08.5d", i); /* { dg-warning "11:ignored" "0 flag ignored with precision" } */
   printf ("%08.5i", i); /* { dg-warning "11:ignored" "0 flag ignored with precision" } */
   printf ("%08.5o", u); /* { dg-warning "11:ignored" "0 flag ignored with precision" } */
   printf ("%08.5u", u); /* { dg-warning "11:ignored" "0 flag ignored with precision" } */
@@ -174,17 +181,17 @@  foo (int i, int i1, int i2, unsigned int
   printf ("%-08E", d); /* { dg-warning "11:flags|ignored" "0 flag ignored with - flag" } */
   printf ("%-08f", d); /* { dg-warning "11:flags|ignored" "0 flag ignored with - flag" } */
   printf ("%-08g", d); /* { dg-warning "11:flags|ignored" "0 flag ignored with - flag" } */
   printf ("%-08G", d); /* { dg-warning "11:flags|ignored" "0 flag ignored with - flag" } */
   /* Various tests of bad argument types.  */
-  printf ("%d", l); /* { dg-warning "11:format" "bad argument types" } */
-  printf ("%*.*d", l, i2, i); /* { dg-warning "11:field" "bad * argument types" } */
-  printf ("%*.*d", i1, l, i); /* { dg-warning "11:field" "bad * argument types" } */
-  printf ("%ld", i); /* { dg-warning "11:format" "bad argument types" } */
-  printf ("%s", n); /* { dg-warning "11:format" "bad argument types" } */
-  printf ("%p", i); /* { dg-warning "11:format" "bad argument types" } */
-  printf ("%n", p); /* { dg-warning "11:format" "bad argument types" } */
+  printf ("%d", l); /* { dg-warning "13:format" "bad argument types" } */
+  printf ("%*.*d", l, i2, i); /* { dg-warning "13:field" "bad * argument types" } */
+  printf ("%*.*d", i1, l, i); /* { dg-warning "15:field" "bad * argument types" } */
+  printf ("%ld", i); /* { dg-warning "14:format" "bad argument types" } */
+  printf ("%s", n); /* { dg-warning "13:format" "bad argument types" } */
+  printf ("%p", i); /* { dg-warning "13:format" "bad argument types" } */
+  printf ("%n", p); /* { dg-warning "13:format" "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
      or pointers to other types.  (Whether, in fact, character types are
      allowed here is unclear; see thread on comp.std.c, July 2000 for
@@ -194,22 +201,22 @@  foo (int i, int i1, int i2, unsigned int
      pointer targets differ in signedness, except in some circumstances
      for character pointers.  (In C99 we should consider warning for
      char * or unsigned char * being passed to %hhn, even if strictly
      legitimate by the standard.)
   */
-  printf ("%p", foo); /* { dg-warning "11:format" "bad argument types" } */
-  printf ("%n", un); /* { dg-warning "11:format" "bad argument types" } */
-  printf ("%p", n); /* { dg-warning "11:format" "bad argument types" } */
+  printf ("%p", foo); /* { dg-warning "13:format" "bad argument types" } */
+  printf ("%n", un); /* { dg-warning "13:format" "bad argument types" } */
+  printf ("%p", n); /* { dg-warning "13:format" "bad argument types" } */
   /* Allow character pointers with %p.  */
   printf ("%p%p%p%p", s, ss, us, css);
   /* %s allows any character type.  */
   printf ("%s%s%s%s", s, ss, us, css);
   /* Warning for void * arguments for %s is GCC's historical behavior,
      and seems useful to keep, even if some standard versions might be
      read to permit it.
   */
-  printf ("%s", p); /* { dg-warning "11:format" "bad argument types" } */
+  printf ("%s", p); /* { dg-warning "13:format" "bad argument types" } */
   /* The historical behavior is to allow signed / unsigned types
      interchangeably as arguments.  For values representable in both types,
      such usage may be correct.  For now preserve the behavior of GCC
      in such cases.
   */
@@ -218,20 +225,24 @@  foo (int i, int i1, int i2, unsigned int
      GCC has been inconsistent and allowed unsigned for width but not
      precision.
   */
   printf ("%*.*d", u1, u2, i);
   /* Wrong number of arguments.  */
-  printf ("%d%d", i); /* { dg-warning "11:matching" "wrong number of args" } */
+  printf ("%d%d", i); /* { dg-warning "15:matching" "wrong number of args" } */
   printf ("%d", i, i); /* { dg-warning "11:arguments" "wrong number of args" } */
   /* Miscellaneous bogus constructions.  */
   printf (""); /* { dg-warning "11:zero-length" "warning for empty format" } */
-  printf ("\0"); /* { dg-warning "11:embedded" "warning for embedded NUL" } */
-  printf ("%d\0", i); /* { dg-warning "11:embedded" "warning for embedded NUL" } */
-  printf ("%d\0%d", i, i); /* { dg-warning "11:embedded|too many" "warning for embedded NUL" } */
+  printf ("\0"); /* { dg-warning "12:embedded" "warning for embedded NUL" } */
+  printf ("%d\0", i); /* { dg-warning "14:embedded" "warning for embedded NUL" } */
+  printf ("%d\0%d", i, i); /* { dg-warning "embedded|too many" "warning for embedded NUL" } */
   printf (NULL); /* { dg-warning "3:null" "null format string warning" } */
-  printf ("%"); /* { dg-warning "11:trailing" "trailing % warning" } */
-  printf ("%++d", i); /* { dg-warning "11:repeated" "repeated flag warning" } */
+  printf ("%"); /* { dg-warning "12:trailing" "trailing % warning" } */
+  printf ("%++d", i); /* { dg-warning "14:repeated" "repeated flag warning" } */
   printf ("%n", cn); /* { dg-warning "3:constant" "%n with const" } */
   printf ((const char *)L"foo"); /* { dg-warning "25:wide" "wide string" } */
   printf ("%n", (int *)0); /* { dg-warning "3:null" "%n with NULL" } */
   printf ("%s", (char *)0); /* { dg-warning "3:null" "%s with NULL" } */
+  /* Test for correct column locations within strings with embedded
+     escape sequences. */
+  printf ("\\\a\n \"\t%5n\n", n); /* { dg-warning "25:width" "width with %n" } */
+  printf ("\\a\\n%5n\n", n); /* { dg-warning "20:width" "width with %n" } */
 }