From 6702f67ffda6c8948746fb619057eb8f4b19a3de Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 1 Sep 2015 09:10:32 -0400
Subject: [PATCH 62/67] FIXME: WIP on c-family/c-format.c to use string literal
 location info

---
 gcc/c-family/c-format.c                            | 102 +++++++++++++--------
 .../format/pr52952-mismatching-argument-type-hex.c |  17 ++++
 .../pr52952-mismatching-argument-type-multiple.c   |  18 ++++
 .../format/pr52952-mismatching-argument-type-oct.c |  17 ++++
 .../format/pr52952-mismatching-argument-type-u8.c  |  15 +++
 .../format/pr52952-mismatching-argument-type.c     |  15 +++
 .../format/pr52952-multiline-format-string.c       |  18 ++++
 .../format/pr52952-spurious-trailing-percent.c     |  15 +++
 8 files changed, 181 insertions(+), 36 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type-hex.c
 create mode 100644 gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type-multiple.c
 create mode 100644 gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type-oct.c
 create mode 100644 gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type-u8.c
 create mode 100644 gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type.c
 create mode 100644 gcc/testsuite/gcc.dg/format/pr52952-multiline-format-string.c
 create mode 100644 gcc/testsuite/gcc.dg/format/pr52952-spurious-trailing-percent.c

diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
index 99ecff4..bf58ad1 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-format.h"
 #include "alloc-pool.h"
 #include "c-target.h"
+#include "gcc-rich-location.h"
 
 /* Handle attributes associated with format checking.  */
 
@@ -1023,6 +1024,7 @@ static void check_format_info (function_format_info *, tree);
 static void check_format_arg (void *, tree, unsigned HOST_WIDE_INT);
 static void check_format_info_main (format_check_results *,
 				    function_format_info *,
+				    tree,
 				    const char *, int, tree,
 				    unsigned HOST_WIDE_INT,
 				    object_allocator<format_wanted_type> &);
@@ -1036,8 +1038,9 @@ static void finish_dollar_format_checking (format_check_results *, int);
 static const format_flag_spec *get_flag_spec (const format_flag_spec *,
 					      int, const char *);
 
-static void check_format_types (location_t, format_wanted_type *);
-static void format_type_warning (location_t, format_wanted_type *, tree, tree);
+static void check_format_types (rich_location *, format_wanted_type *);
+static void format_type_warning (rich_location *, format_wanted_type *, tree,
+				 tree);
 
 /* 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
@@ -1689,12 +1692,14 @@ check_format_arg (void *ctx, tree format_tree,
   res->number_other++;
   object_allocator <format_wanted_type> fwt_pool ("format_wanted_type pool",
 						  10);
-  check_format_info_main (res, info, format_chars, format_length,
+  check_format_info_main (res, info, format_tree, format_chars, format_length,
 			  params, arg_num, fwt_pool);
 }
 
 
-/* Do the main part of checking a call to a format function.  FORMAT_CHARS
+/* Do the main part of checking a call to a format function.
+   FORMAT_STRING_CST is the STRING_CST format string.
+   FORMAT_CHARS
    is the NUL-terminated format string (which at this point may contain
    internal NUL characters); FORMAT_LENGTH is its length (excluding the
    terminating NUL character).  ARG_NUM is one less than the number of
@@ -1703,7 +1708,9 @@ check_format_arg (void *ctx, tree format_tree,
 
 static void
 check_format_info_main (format_check_results *res,
-			function_format_info *info, const char *format_chars,
+			function_format_info *info,
+			tree format_string_cst,
+			const char *format_chars,
 			int format_length, tree params,
 			unsigned HOST_WIDE_INT arg_num,
 			object_allocator <format_wanted_type> &fwt_pool)
@@ -1722,6 +1729,8 @@ check_format_info_main (format_check_results *res,
 
   init_dollar_format_checking (info->first_arg_num, first_fillin_param);
 
+  //const char *start_of_format_string = format_chars;
+
   while (*format_chars != 0)
     {
       int i;
@@ -1752,10 +1761,20 @@ check_format_info_main (format_check_results *res,
 	continue;
       if (*format_chars == 0)
 	{
+#if 1
           warning_at (location_from_offset (format_string_loc,
 					    format_chars - orig_format_chars),
 		      OPT_Wformat_,
 		      "spurious trailing %<%%%> in format");
+#else
+	  ptrdiff_t offset = (format_chars - 1) - start_of_format_string;
+	  gcc_rich_location richloc (format_string_cst,
+				     offset,
+				     offset,
+				     offset);
+	  warning_at_rich_loc (&richloc, OPT_Wformat_,
+			       "spurious trailing %<%%%> in format");
+#endif
 	  continue;
 	}
       if (*format_chars == '%')
@@ -1763,6 +1782,7 @@ check_format_info_main (format_check_results *res,
 	  ++format_chars;
 	  continue;
 	}
+      const char *start_of_this_format = format_chars;
       flag_chars[0] = 0;
 
       if ((fki->flags & (int) FMT_FLAG_USE_DOLLAR) && has_operand_number != 0)
@@ -2426,7 +2446,15 @@ check_format_info_main (format_check_results *res,
 	}
 
       if (first_wanted_type != 0)
-        check_format_types (format_string_loc, first_wanted_type);
+	{
+	  ptrdiff_t offset_to_format_start = (start_of_this_format - 1) - orig_format_chars;
+	  ptrdiff_t offset_to_format_end = (format_chars - 1) - orig_format_chars;
+	  gcc_rich_location richloc (format_string_cst,
+				     offset_to_format_start,
+				     offset_to_format_start,
+				     offset_to_format_end);
+	  check_format_types (&richloc, first_wanted_type);
+	}
     }
 
   if (format_chars - orig_format_chars != format_length)
@@ -2449,7 +2477,7 @@ check_format_info_main (format_check_results *res,
    including width and precision arguments).  LOC is the location of
    the format string.  */
 static void
-check_format_types (location_t loc, format_wanted_type *types)
+check_format_types (rich_location *richloc, format_wanted_type *types)
 {
   for (; types != 0; types = types->next)
     {
@@ -2476,7 +2504,7 @@ check_format_types (location_t loc, format_wanted_type *types)
       cur_param = types->param;
       if (!cur_param)
         {
-          format_type_warning (loc, types, wanted_type, NULL);
+          format_type_warning (richloc, types, wanted_type, NULL);
           continue;
         }
 
@@ -2550,7 +2578,7 @@ check_format_types (location_t loc, format_wanted_type *types)
 	    }
 	  else
 	    {
-              format_type_warning (loc, types, wanted_type, orig_cur_type);
+              format_type_warning (richloc, types, wanted_type, orig_cur_type);
 	      break;
 	    }
 	}
@@ -2618,7 +2646,7 @@ check_format_types (location_t loc, format_wanted_type *types)
 	  && TYPE_PRECISION (cur_type) == TYPE_PRECISION (wanted_type))
 	continue;
       /* Now we have a type mismatch.  */
-      format_type_warning (loc, types, wanted_type, orig_cur_type);
+      format_type_warning (richloc, types, wanted_type, orig_cur_type);
     }
 }
 
@@ -2631,7 +2659,7 @@ check_format_types (location_t loc, format_wanted_type *types)
    are taken from TYPE.  ARG_TYPE is the type of the actual argument,
    or NULL if it is missing.  */
 static void
-format_type_warning (location_t loc, format_wanted_type *type,
+format_type_warning (rich_location *richloc, format_wanted_type *type,
 		     tree wanted_type, tree arg_type)
 {
   int kind = type->kind;
@@ -2640,7 +2668,7 @@ format_type_warning (location_t loc, format_wanted_type *type,
   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;
+  //unsigned int offset_loc = type->offset_loc;
 
   char *p;
   /* If ARG_TYPE is a typedef with a misleading name (for example,
@@ -2674,41 +2702,43 @@ format_type_warning (location_t loc, format_wanted_type *type,
       p[pointer_count + 1] = 0;
     }
 
+#if 0
   loc = location_from_offset (loc, offset_loc);
+#endif
 		      
   if (wanted_type_name)
     {
       if (arg_type)
-        warning_at (loc, OPT_Wformat_,
-		    "%s %<%s%.*s%> expects argument of type %<%s%s%>, "
-		    "but argument %d has type %qT",
-		    gettext (kind_descriptions[kind]),
-		    (kind == CF_KIND_FORMAT ? "%" : ""),
-		    format_length, format_start, 
-		    wanted_type_name, p, arg_num, arg_type);
+	warning_at_rich_loc (richloc, OPT_Wformat_,
+			     "%s %<%s%.*s%> expects argument of type %<%s%s%>, "
+			     "but argument %d has type %qT",
+			     gettext (kind_descriptions[kind]),
+			     (kind == CF_KIND_FORMAT ? "%" : ""),
+			     format_length, format_start,
+			     wanted_type_name, p, arg_num, arg_type);
       else
-        warning_at (loc, OPT_Wformat_,
-		    "%s %<%s%.*s%> expects a matching %<%s%s%> argument",
-		    gettext (kind_descriptions[kind]),
-		    (kind == CF_KIND_FORMAT ? "%" : ""),
-		    format_length, format_start, wanted_type_name, p);
+	warning_at_rich_loc (richloc, OPT_Wformat_,
+			     "%s %<%s%.*s%> expects a matching %<%s%s%> argument",
+			     gettext (kind_descriptions[kind]),
+			     (kind == CF_KIND_FORMAT ? "%" : ""),
+			     format_length, format_start, wanted_type_name, p);
     }
   else
     {
       if (arg_type)
-        warning_at (loc, OPT_Wformat_,
-		    "%s %<%s%.*s%> expects argument of type %<%T%s%>, "
-		    "but argument %d has type %qT",
-		    gettext (kind_descriptions[kind]),
-		    (kind == CF_KIND_FORMAT ? "%" : ""),
-		    format_length, format_start, 
-		    wanted_type, p, arg_num, arg_type);
+	warning_at_rich_loc (richloc, OPT_Wformat_,
+			     "%s %<%s%.*s%> expects argument of type %<%T%s%>, "
+			     "but argument %d has type %qT",
+			     gettext (kind_descriptions[kind]),
+			     (kind == CF_KIND_FORMAT ? "%" : ""),
+			     format_length, format_start,
+			     wanted_type, p, arg_num, arg_type);
       else
-        warning_at (loc, OPT_Wformat_,
-		    "%s %<%s%.*s%> expects a matching %<%T%s%> argument",
-		    gettext (kind_descriptions[kind]),
-		    (kind == CF_KIND_FORMAT ? "%" : ""),
-		    format_length, format_start, wanted_type, p);
+	warning_at_rich_loc (richloc, OPT_Wformat_,
+			     "%s %<%s%.*s%> expects a matching %<%T%s%> argument",
+			     gettext (kind_descriptions[kind]),
+			     (kind == CF_KIND_FORMAT ? "%" : ""),
+			     format_length, format_start, wanted_type, p);
     }
 }
 
diff --git a/gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type-hex.c b/gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type-hex.c
new file mode 100644
index 0000000..f653764
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type-hex.c
@@ -0,0 +1,17 @@
+/* { dg-options "-Wformat -fdiagnostics-show-caret" } */
+
+#include <stdio.h>
+
+void foo (const char *msg)
+{
+  /* "%" is \x25
+     "i" is \x69 */
+  printf("hello \x25\x69", msg);  /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
+}
+
+/*
+{ dg-begin-multiline-output "" }
+   printf("hello \x25\x69", msg);
+                 ^~~~~~~~
+{ dg-end-multiline-output "" }
+*/
diff --git a/gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type-multiple.c b/gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type-multiple.c
new file mode 100644
index 0000000..27893be
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type-multiple.c
@@ -0,0 +1,18 @@
+/* { dg-options "-Wformat -fdiagnostics-show-caret" } */
+
+#include <stdio.h>
+
+void foo (const char *msg)
+{
+  /* "%" is \x25 in hex
+     "i" is \151 in octal.  */
+  printf("prefix"  "\x25"  "\151"  "suffix",  /* { dg-warning "format '%i'" } */
+	 msg);
+}
+
+/*
+{ dg-begin-multiline-output "" }
+   printf("prefix"  "\x25"  "\151"  "suffix",
+                     ^~~~~~~~~~~~
+{ dg-end-multiline-output "" }
+*/
diff --git a/gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type-oct.c b/gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type-oct.c
new file mode 100644
index 0000000..7aceb4f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type-oct.c
@@ -0,0 +1,17 @@
+/* { dg-options "-Wformat -fdiagnostics-show-caret" } */
+
+#include <stdio.h>
+
+void foo (const char *msg)
+{
+  /* "%" is octal 045
+     "i" is octal 151.  */
+  printf("hello \045\151", msg);  /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
+}
+
+/*
+{ dg-begin-multiline-output "" }
+   printf("hello \045\151", msg);
+                 ^~~~~~~~
+{ dg-end-multiline-output "" }
+*/
diff --git a/gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type-u8.c b/gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type-u8.c
new file mode 100644
index 0000000..c376973
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type-u8.c
@@ -0,0 +1,15 @@
+/* { dg-options "-Wformat -fdiagnostics-show-caret" } */
+
+#include <stdio.h>
+
+void foo (const char *msg)
+{
+  printf(u8"hello %i", msg);/* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
+}
+
+/*
+{ dg-begin-multiline-output "" }
+   printf(u8"hello %i", msg);
+                   ^~
+{ dg-end-multiline-output "" }
+*/
diff --git a/gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type.c b/gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type.c
new file mode 100644
index 0000000..5534be5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/pr52952-mismatching-argument-type.c
@@ -0,0 +1,15 @@
+/* { dg-options "-Wformat -fdiagnostics-show-caret" } */
+
+#include <stdio.h>
+
+void foo (const char *msg)
+{
+  printf("hello %i", msg);  /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
+}
+
+/*
+{ dg-begin-multiline-output "" }
+   printf("hello %i", msg);
+                 ^~
+{ dg-end-multiline-output "" }
+*/
diff --git a/gcc/testsuite/gcc.dg/format/pr52952-multiline-format-string.c b/gcc/testsuite/gcc.dg/format/pr52952-multiline-format-string.c
new file mode 100644
index 0000000..db46764
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/pr52952-multiline-format-string.c
@@ -0,0 +1,18 @@
+/* { dg-options "-Wformat -fdiagnostics-show-caret" } */
+
+extern int printf (__const char *__restrict __format, ...);
+void f(void) {
+  printf ("before the fmt specifier"
+          "%"  /* { dg-warning "format '%d' expects a matching 'int' argument" } */
+          "d"
+          "after the fmt specifier");
+}
+
+/*
+{ dg-begin-multiline-output "" }
+           "%"
+            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+           "d"
+           ~~
+{ dg-end-multiline-output "" }
+*/
diff --git a/gcc/testsuite/gcc.dg/format/pr52952-spurious-trailing-percent.c b/gcc/testsuite/gcc.dg/format/pr52952-spurious-trailing-percent.c
new file mode 100644
index 0000000..157a509
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/pr52952-spurious-trailing-percent.c
@@ -0,0 +1,15 @@
+/* { dg-options "-Wformat -fdiagnostics-show-caret" } */
+
+#include <stdio.h>
+
+void foo(void)
+{
+  printf("hello world %"); /* { dg-warning "spurious trailing" } */
+}
+
+/*
+{ dg-begin-multiline-output "" }
+   printf("hello world %");
+                       ^
+{ dg-end-multiline-output "" }
+*/
-- 
1.8.5.3

