From 1225025e3dea2045897e77af3020796431e768ab Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 6 Jun 2017 17:34:20 -0400
Subject: [PATCH 03/24] FIXME: C++: add maybe_add_type_conversion_fixits (uses
 vec<loc>; before blt; maybe generalizable to C?)

---
 gcc/cp/call.c                           | 24 ++++++++++---
 gcc/cp/cp-tree.h                        |  4 +++
 gcc/cp/typeck.c                         | 39 +++++++++++++++++++--
 gcc/testsuite/g++.dg/conversion/hints.C | 60 +++++++++++++++++++++++++++++++++
 4 files changed, 120 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/conversion/hints.C

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index b730a85..b3b1feb 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "c-family/c-objc.h"
 #include "internal-fn.h"
+#include "gcc-rich-location.h"
 
 /* The various kinds of conversion.  */
 
@@ -10111,8 +10112,14 @@ perform_implicit_conversion_flags (tree type, cp_expr expr,
 	  else if (invalid_nonstatic_memfn_p (loc, expr, complain))
 	    /* We gave an error.  */;
 	  else
-	    error_at (loc, "could not convert %qE from %qT to %qT", expr,
-		      TREE_TYPE (expr), type);
+	    {
+	      gcc_rich_location richloc (loc);
+	      maybe_add_type_conversion_fixits (&richloc, TREE_TYPE (expr),
+						type);
+	      error_at_rich_loc (&richloc,
+				 "could not convert %qE from %qT to %qT",
+				 expr.get_value (), TREE_TYPE (expr), type);
+	    }
 	}
       expr = error_mark_node;
     }
@@ -10432,9 +10439,16 @@ initialize_reference (tree type, cp_expr expr,
 		      "type %qT from an rvalue of type %qT",
 		      type, TREE_TYPE (expr));
 	  else
-	    error_at (loc, "invalid initialization of reference of type "
-		      "%qT from expression of type %qT", type,
-		      TREE_TYPE (expr));
+	    {
+	      gcc_rich_location richloc (loc);
+	      maybe_add_type_conversion_fixits (&richloc, TREE_TYPE (expr),
+						type);
+	      error_at_rich_loc
+	        (&richloc,
+		 "invalid initialization of reference of type "
+		 "%qT from expression of type %qT", type,
+		 TREE_TYPE (expr));
+	    }
 	}
       return error_mark_node;
     }
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 11f000d..4bfed04 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6859,6 +6859,10 @@ extern tree finish_left_unary_fold_expr      (tree, int);
 extern tree finish_right_unary_fold_expr     (tree, int);
 extern tree finish_binary_fold_expr          (tree, tree, int);
 
+extern void maybe_add_type_conversion_fixits (rich_location *richloc,
+					      tree actual_type,
+					      tree expected_type);
+
 /* in typeck2.c */
 extern void require_complete_eh_spec_types	(tree, tree);
 extern void cxx_incomplete_type_diagnostic	(location_t, const_tree,
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index fe7aac7..915030a 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -8391,6 +8391,35 @@ delta_from_ptrmemfunc (tree t)
   return build_ptrmemfunc_access_expr (t, delta_identifier);
 }
 
+/* Given RICHLOC, a rich_location at an expression that can't be
+   converted from ACTUAL_TYPE to EXPECTED_TYPE, potentially
+   add fix-it hints that will make the conversion succeed.
+
+   For example, if we got T and wanted T*, suggest adding a
+   leading "&".  */
+
+void
+maybe_add_type_conversion_fixits (rich_location *richloc,
+				  tree actual_type, tree expected_type)
+{
+  /* Got T; wanted T*.  */
+  if (TREE_CODE (expected_type) == POINTER_TYPE
+      && TREE_TYPE (expected_type) == actual_type)
+    richloc->add_fixit_insert_before ("&");
+  // FIXME: strip off const etc
+
+  /* Got T*; wanted T&.  */
+  if (TREE_CODE (actual_type) == POINTER_TYPE
+      && TREE_CODE (expected_type) == REFERENCE_TYPE
+      && TREE_TYPE (actual_type) == TREE_TYPE (expected_type))
+    richloc->add_fixit_insert_before ("*");
+
+  /* Got T*; wanted T.  */
+  if (TREE_CODE (actual_type) == POINTER_TYPE
+      && TREE_TYPE (actual_type) == expected_type)
+    richloc->add_fixit_insert_before ("*");
+}
+
 /* Convert value RHS to type TYPE as preparation for an assignment to
    an lvalue of type TYPE.  ERRTYPE indicates what kind of error the
    implicit conversion is.  If FNDECL is non-NULL, we are doing the
@@ -8507,8 +8536,14 @@ convert_for_assignment (tree type, cp_expr rhs,
 		    return r;
 		}
 	      else if (fndecl)
-		error ("cannot convert %qT to %qT for argument %qP to %qD",
-		       rhstype, type, parmnum, fndecl);
+		{
+		  gcc_rich_location richloc (rhs.get_location ());
+		  maybe_add_type_conversion_fixits (&richloc, rhstype, type);
+		  error_at_rich_loc
+		    (&richloc,
+		     "cannot convert %qT to %qT for argument %qP to %qD",
+		     rhstype, type, parmnum, fndecl);
+		}
 	      else
 		switch (errtype)
 		  {
diff --git a/gcc/testsuite/g++.dg/conversion/hints.C b/gcc/testsuite/g++.dg/conversion/hints.C
new file mode 100644
index 0000000..7de7938
--- /dev/null
+++ b/gcc/testsuite/g++.dg/conversion/hints.C
@@ -0,0 +1,60 @@
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+struct foo {};
+
+extern void takes_ptr (int one, foo *two, int three);
+
+void test_1 ()
+{
+  foo inst;
+  takes_ptr (100, inst, 200); // { dg-error "cannot convert" }
+  /* { dg-begin-multiline-output "" }
+   takes_ptr (100, inst, 200);
+                   ^~~~
+                   &
+     { dg-end-multiline-output "" } */
+}
+
+extern void takes_const_ptr (int one, const foo *two, int three);
+
+void test_2 ()
+{
+  foo inst;
+  takes_const_ptr (100, inst, 200); // { dg-error "cannot convert" }
+  // FIXME: fix-it!
+  /* { dg-begin-multiline-output "" }
+   takes_const_ptr (100, inst, 200);
+                         ^~~~
+     { dg-end-multiline-output "" } */
+}
+
+extern void takes_ref (int one, foo &two, int three); // { dg-line takes_ref_decl }
+
+void test_3 (foo *ptr)
+{
+  takes_ref (100, ptr, 200); // { dg-error "invalid initialization of reference of type 'foo&' from expression of type 'foo\\*'" }
+  /* { dg-begin-multiline-output "" }
+   takes_ref (100, ptr, 200);
+                   ^~~
+                   *
+     { dg-end-multiline-output "" } */
+  // { dg-message "in passing argument 2 of" "" { target *-*-* } takes_ref_decl }
+  // TODO: underline pertinent param here:
+  /* { dg-begin-multiline-output "" }
+ extern void takes_ref (int one, foo &two, int three);
+             ^~~~~~~~~
+     { dg-end-multiline-output "" } */
+
+}
+
+extern void takes_inst (int one, foo two, int three);
+
+void test_4 (foo *ptr)
+{
+  takes_inst (100, ptr, 200); // { dg-error "could not convert 'ptr' from 'foo\\*' to 'foo'" }
+  /* { dg-begin-multiline-output "" }
+   takes_inst (100, ptr, 200);
+                    ^~~
+                    *
+     { dg-end-multiline-output "" } */
+}
-- 
1.8.5.3

