From 4b394694db1a7943c0c3285b336793801368bb4f Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 27 Aug 2015 16:36:59 -0400
Subject: [PATCH 24/48] FIXME: C/C++ frontend: use tree ranges in various
 diagnostics

Screenshot:
 https://dmalcolm.fedorapeople.org/gcc/2015-09-09/tree-ranges.html

This mostly affects the C frontend, but it touches c-common.h so the
C++ frontend needs a slight adjustment.

FIXME:
  This needs some rewrites, and doesn't quite compile.

gcc/c-family/ChangeLog:
	* c-common.c: Include gcc-rich-location.h.
	(binary_op_error): Add params orig_op0 and orig_op1; use them
	to add ranges and captions to the error.
	* c-common.h (binary_op_error): Add params orig_op0 and orig_op1.

gcc/c/ChangeLog:
	* c-parser.c (c_parser_static_assert_declaration_no_semi):
	Convert locals "assert_loc" and "value_loc" from location_t to
	source_range, thus using ranges in diagnostics.
	* c-typeck.c (inform_declaration): Use DECL_LOCATION_RANGE
	of the decl, rather than DECL_SOURCE_LOCATION.
	(build_function_call_vec): Use the EXPR_LOCATION_RANGE of
	the function when issuing diagnostics, rather than "loc".
	(convert_for_assignment): Emit range-based diagnostics, rather
	than locations when CAN_HAVE_RANGE_P (rhs).
	(c_finish_return): Likewise when CAN_HAVE_RANGE_P (retval).
	When issuing warnings about erroneous presence/absence of return
	values, show the location of current_function_decl using inform.
	(build_binary_op): Pass orig_op0 and orig_op1 to binary_op_error.

gcc/cp/ChangeLog:
	* typeck.c (cp_build_binary_op): Pass orig_op0 and orig_op1 to
	binary_op_error.

gcc/testsuite/ChangeLog:
	* gcc.dg/diagnostic-tree-expr-ranges.c: New file.
---
 gcc/c-family/c-common.c                            |   7 +-
 gcc/c-family/c-common.h                            |   3 +-
 gcc/c/c-parser.c                                   |   9 +-
 gcc/c/c-typeck.c                                   |  56 +++++---
 gcc/cp/typeck.c                                    |   3 +-
 gcc/testsuite/gcc.dg/diagnostic-tree-expr-ranges.c | 147 ++++++++++++++++++++-
 6 files changed, 198 insertions(+), 27 deletions(-)

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index c102bbd..be36609 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -53,6 +53,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimplify.h"
 #include "wide-int-print.h"
 #include "gimple-expr.h"
+#include "gcc-rich-location.h"
 
 cpp_reader *parse_in;		/* Declared in c-pragma.h.  */
 
@@ -4350,6 +4351,7 @@ c_register_builtin_type (tree type, const char* name)
 
 void
 binary_op_error (location_t location, enum tree_code code,
+		 tree orig_op0, tree orig_op1,
 		 tree type0, tree type1)
 {
   const char *opname;
@@ -4401,7 +4403,10 @@ binary_op_error (location_t location, enum tree_code code,
     default:
       gcc_unreachable ();
     }
-  error_at (location,
+  gcc_rich_location richloc (location);
+  richloc.maybe_add_expr (orig_op0);
+  richloc.maybe_add_expr (orig_op1);
+  error_at_rich_loc (&richloc,
 	    "invalid operands to binary %s (have %qT and %qT)", opname,
 	    type0, type1);
 }
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index b0a7661..d2939d4 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -818,7 +818,8 @@ extern tree c_sizeof_or_alignof_type (location_t, tree, bool, bool, int);
 extern tree c_alignof_expr (location_t, tree);
 /* Print an error message for invalid operands to arith operation CODE.
    NOP_EXPR is used as a special case (see truthvalue_conversion).  */
-extern void binary_op_error (location_t, enum tree_code, tree, tree);
+extern void binary_op_error (location_t, enum tree_code, tree, tree,
+			     tree, tree);
 extern tree fix_string_type (tree);
 extern void constant_expression_warning (tree);
 extern void constant_expression_error (tree);
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index d39d7a2..1101e18 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -5086,7 +5086,8 @@ c_parser_statement_after_labels (c_parser *parser, vec<tree> *chain)
 	      location_t xloc = c_parser_peek_token (parser)->location;
 	      struct c_expr expr = c_parser_expression_conv (parser);
 	      mark_exp_read (expr.value);
-	      stmt = c_finish_return (xloc, expr.value, expr.original_type);
+	      stmt = c_finish_return (EXPR_LOC_OR_LOC (expr.value, xloc),
+				      expr.value, expr.original_type);
 	      goto expect_semicolon;
 	    }
 	  break;
@@ -7874,7 +7875,10 @@ c_parser_postfix_expression (c_parser *parser)
       expr.value = error_mark_node;
       break;
     }
-  return c_parser_postfix_expression_after_primary (parser, loc, expr);
+  return c_parser_postfix_expression_after_primary (parser,
+						    EXPR_LOC_OR_LOC (expr.value,
+								     loc),
+						    expr);
 }
 
 /* Parse a postfix expression after a parenthesized type name: the
@@ -8334,6 +8338,7 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p,
       if (literal_zero_mask)
 	c_parser_check_literal_zero (parser, literal_zero_mask, idx + 1);
       expr = c_parser_expr_no_commas (parser, NULL);
+      loc = EXPR_LOC_OR_LOC (expr.value, loc);
       if (convert_p)
 	expr = convert_lvalue_to_rvalue (loc, expr, true, true);
       if (fold_p)
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 6fff1ea..106bf9d 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -5768,9 +5768,9 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
     switch (errtype)                                                     \
       {                                                                  \
       case ic_argpass:                                                   \
-        if (pedwarn (PLOC, OPT, AR, parmnum, rname))			 \
-          inform ((fundecl && !DECL_IS_BUILTIN (fundecl))	         \
-		  ? DECL_SOURCE_LOCATION (fundecl) : PLOC,		 \
+        if (pedwarn (PLOC, OPT, AR, parmnum, rname))                    \
+          inform ((fundecl && !DECL_IS_BUILTIN (fundecl))                \
+                 ? DECL_SOURCE_LOCATION (fundecl) : PLOC,       \
                   "expected %qT but argument is of type %qT",            \
                   type, rhstype);                                        \
         break;                                                           \
@@ -5798,8 +5798,8 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
       {                                                                  \
       case ic_argpass:                                                   \
         if (pedwarn (PLOC, OPT, AR, parmnum, rname, QUALS))		 \
-          inform ((fundecl && !DECL_IS_BUILTIN (fundecl))	         \
-		  ? DECL_SOURCE_LOCATION (fundecl) : PLOC,		 \
+          inform ((fundecl && !DECL_IS_BUILTIN (fundecl))                \
+                 ? DECL_SOURCE_LOCATION (fundecl) : PLOC,       \
                   "expected %qT but argument is of type %qT",            \
                   type, rhstype);                                        \
         break;                                                           \
@@ -5826,9 +5826,9 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
     switch (errtype)                                                     \
       {                                                                  \
       case ic_argpass:                                                   \
-        if (warning_at (PLOC, OPT, AR, parmnum, rname, QUALS))           \
+        if (warning_at (PLOC, OPT, AR, parmnum, rname, QUALS))          \
           inform ((fundecl && !DECL_IS_BUILTIN (fundecl))                \
-                  ? DECL_SOURCE_LOCATION (fundecl) : PLOC,               \
+                  ? DECL_SOURCE_LOCATION (fundecl) : PLOC,      \
                   "expected %qT but argument is of type %qT",            \
                   type, rhstype);                                        \
         break;                                                           \
@@ -6403,7 +6403,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
   switch (errtype)
     {
     case ic_argpass:
-      error_at (expr_loc, "incompatible type for argument %d of %qE", parmnum,
+      error_at (location, "incompatible type for argument %d of %qE", parmnum,
 		rname);
       inform ((fundecl && !DECL_IS_BUILTIN (fundecl))
 	      ? DECL_SOURCE_LOCATION (fundecl) : expr_loc,
@@ -9407,7 +9407,7 @@ c_finish_return (location_t loc, tree retval, tree origtype)
   source_location xloc = expansion_point_location_if_in_system_header (loc);
 
   if (TREE_THIS_VOLATILE (current_function_decl))
-    warning_at (xloc, 0,
+    warning_at (loc, 0,
 		"function declared %<noreturn%> has a %<return%> statement");
 
   if (flag_cilkplus && contains_array_notation_expr (retval))
@@ -9425,7 +9425,8 @@ c_finish_return (location_t loc, tree retval, tree origtype)
     }
   if (flag_cilkplus && retval && contains_cilk_spawn_stmt (retval))
     {
-      error_at (loc, "use of %<_Cilk_spawn%> in a return statement is not "
+      error_at (loc,
+		"use of %<_Cilk_spawn%> in a return statement is not "
 		"allowed");
       return error_mark_node;
     }
@@ -9449,24 +9450,36 @@ c_finish_return (location_t loc, tree retval, tree origtype)
       if ((warn_return_type || flag_isoc99)
 	  && valtype != 0 && TREE_CODE (valtype) != VOID_TYPE)
 	{
+	  bool warned_here;
 	  if (flag_isoc99)
-	    pedwarn (loc, 0, "%<return%> with no value, in "
-		     "function returning non-void");
+	    warned_here = pedwarn
+	      (loc, 0,
+	       "%<return%> with no value, in function returning non-void");
 	  else
-	    warning_at (loc, OPT_Wreturn_type, "%<return%> with no value, "
-			"in function returning non-void");
-	  no_warning = true;
+	    warned_here =
+	      warning_at (loc, OPT_Wreturn_type,
+			  "%<return%> with no value, "
+			  "in function returning non-void");
+	  if (warned_here)
+	    inform (DECL_SOURCE_LOCATION (current_function_decl),
+		    "declared here");
 	}
     }
   else if (valtype == 0 || TREE_CODE (valtype) == VOID_TYPE)
     {
       current_function_returns_null = 1;
+      bool warned_here;
       if (TREE_CODE (TREE_TYPE (retval)) != VOID_TYPE)
-	pedwarn (xloc, 0,
-		 "%<return%> with a value, in function returning void");
+	warned_here = pedwarn
+	  (loc, 0,
+	   "%<return%> with a value, in function returning void");
       else
-	pedwarn (xloc, OPT_Wpedantic, "ISO C forbids "
-		 "%<return%> with expression, in function returning void");
+	warned_here = pedwarn
+	  (loc, OPT_Wpedantic, "ISO C forbids "
+	   "%<return%> with expression, in function returning void");
+      if (warned_here)
+	inform (DECL_SOURCE_LOCATION (current_function_decl),
+		"declared here");
     }
   else
     {
@@ -11068,7 +11081,7 @@ build_binary_op (location_t location, enum tree_code code,
       && (!tree_int_cst_equal (TYPE_SIZE (type0), TYPE_SIZE (type1))
 	  || !vector_types_compatible_elements_p (type0, type1)))
     {
-      binary_op_error (location, code, type0, type1);
+      binary_op_error (location, code, orig_op0, orig_op1, type0, type1);
       return error_mark_node;
     }
 
@@ -11307,7 +11320,8 @@ build_binary_op (location_t location, enum tree_code code,
 
   if (!result_type)
     {
-      binary_op_error (location, code, TREE_TYPE (op0), TREE_TYPE (op1));
+      binary_op_error (location, code, orig_op0, orig_op1,
+		       TREE_TYPE (op0), TREE_TYPE (op1));
       return error_mark_node;
     }
 
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 693fd08..c41e6e8 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -4872,7 +4872,8 @@ cp_build_binary_op (location_t location,
 	      || !vector_types_compatible_elements_p (type0, type1))
 	    {
 	      if (complain & tf_error)
-		binary_op_error (location, code, type0, type1);
+		binary_op_error (location, code, orig_op0, orig_op1,
+				 type0, type1);
 	      return error_mark_node;
 	    }
 	  arithmetic_types_p = 1;
diff --git a/gcc/testsuite/gcc.dg/diagnostic-tree-expr-ranges.c b/gcc/testsuite/gcc.dg/diagnostic-tree-expr-ranges.c
index 3318543..d1bcb74 100644
--- a/gcc/testsuite/gcc.dg/diagnostic-tree-expr-ranges.c
+++ b/gcc/testsuite/gcc.dg/diagnostic-tree-expr-ranges.c
@@ -1,4 +1,5 @@
-/* { dg-options "-fdiagnostics-show-caret" } */
+/* { dg-options "-fdiagnostics-show-caret -Wreturn-local-addr" } */
+
 
 void test_nonconst_static_assert (int i)
 {
@@ -10,3 +11,147 @@ void test_nonconst_static_assert (int i)
 { dg-end-multiline-output "" }
 */
 }
+
+extern void test_callee (int first, const char *second, int third);
+
+void test_bad_argument_types (int first, int second, int third)
+{
+  test_callee (first, first + second + third, third); /* { dg-warning "passing argument 2 of 'test_callee' makes pointer from integer without a cast" }  */
+
+/*
+{ dg-begin-multiline-output "" }
+   test_callee (first, first + second + third, third);
+                       ~~~~~~~~~~~~~~~^~~~~~~
+{ dg-end-multiline-output "" }
+{ dg-begin-multiline-output "" }
+ extern void test_callee (int first, const char *second, int third);
+             ^~~~~~~~~~~
+{ dg-end-multiline-output "" }
+FIXME: we ought to highlight the specific param in the decl;
+however FUNCTION_DECL doesn't seem to store the locations of the params,
+just the types.
+*/
+
+  test_callee (first, &first, third); /* { dg-warning "passing argument 2 of 'test_callee' from incompatible pointer type" } */
+
+/*
+{ dg-begin-multiline-output "" }
+   test_callee (first, &first, third);
+                       ^~~~~~
+{ dg-end-multiline-output "" }
+{ dg-begin-multiline-output "" }
+ extern void test_callee (int first, const char *second, int third);
+             ^~~~~~~~~~~
+{ dg-end-multiline-output "" }
+FIXME: again, we ought to highlight the specific param in the decl
+*/
+
+  test_callee ("hello world", "", third); /* { dg-warning "passing argument 1 of 'test_callee' makes integer from pointer without a cast" } */
+
+/*
+{ dg-begin-multiline-output "" }
+   test_callee ("hello world", "", third);
+                ^~~~~~~~~~~~~
+{ dg-end-multiline-output "" }
+{ dg-begin-multiline-output "" }
+ extern void test_callee (int first, const char *second, int third);
+             ^~~~~~~~~~~
+{ dg-end-multiline-output "" }
+FIXME: and again, we ought to highlight the specific param in the decl
+*/
+
+}
+
+/* Adapted from https://gcc.gnu.org/wiki/ClangDiagnosticsComparison */
+
+void call_of_non_function_ptr (char **argP, char **argQ)
+{
+  (argP - argQ)(); /* { dg-error "called object is not a function or function pointer" } */
+
+/* { dg-begin-multiline-output "" }
+   (argP - argQ)();
+   ~~~~~~^~~~~~~
+   { dg-end-multiline-output "" } */
+
+  argP();       /* { dg-error "called object 'argP' is not a function or function pointer" } */
+
+/* { dg-begin-multiline-output "" }
+   argP();
+   ^~~~
+   { dg-end-multiline-output "" }
+   { dg-begin-multiline-output "" }
+ void call_of_non_function_ptr (char **argP, char **argQ)
+                                       ^~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Adapted from https://gcc.gnu.org/wiki/ClangDiagnosticsComparison */
+
+typedef float __m128;
+void bad_binary_op ()
+{
+  __m128 myvec[2];
+  int const *ptr;
+  myvec[1]/ptr; /* { dg-error "invalid operands to binary /" } */
+
+/*
+{ dg-begin-multiline-output "" }
+   myvec[1]/ptr;
+   ~~~~~~~~^~~~
+{ dg-end-multiline-output "" } */
+
+}
+
+struct s {};
+struct t {};
+extern struct s some_function (void);
+extern struct t some_other_function (void);
+
+int another_bad_binary_op (void)
+{
+  return (some_function ()
+	  + some_other_function ()); /* { dg-error "invalid operands to binary +" } */
+
+/* { dg-begin-multiline-output "" }
+   return (some_function ()
+           ~~~~~~~~~~~~~~~~
+    + some_other_function ());
+    ^ ~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void surplus_return_when_void (void)
+{
+  return 500; /* { dg-warning "'return' with a value, in function returning void" } */
+/* { dg-begin-multiline-output "" }
+   return 500;
+          ^~~
+   { dg-end-multiline-output "" } */
+/* { dg-begin-multiline-output "" }
+ void surplus_return_when_void (void)
+      ^~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+int missing_return_value (void)
+{
+  return; /* { dg-warning "'return' with no value, in function returning non-void" } */
+/* { dg-begin-multiline-output "" }
+   return;
+   ^~~~~~
+   { dg-end-multiline-output "" } */
+/* { dg-begin-multiline-output "" }
+ int missing_return_value (void)
+     ^~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+int *address_of_local (void)
+{
+  int i;
+  return &i; /* { dg-warning "function returns address of local variable" } */
+/* { dg-begin-multiline-output "" }
+   return &i;
+          ^~
+   { dg-end-multiline-output "" } */
+}
-- 
1.8.5.3

