diff mbox series

C++: highlight bad argument in some users of print_z_candidates (more PR c++/85110)

Message ID 1535047700-27258-1-git-send-email-dmalcolm@redhat.com
State New
Headers show
Series C++: highlight bad argument in some users of print_z_candidates (more PR c++/85110) | expand

Commit Message

David Malcolm Aug. 23, 2018, 6:08 p.m. UTC
This is a followup to:

  "[PATCH] C++: underline param in print_conversion_rejection (more PR c++/85110)"
     https://gcc.gnu.org/ml/gcc-patches/2018-08/msg01480.html

to highlight the pertinent argument in a unmatched function call
for which there is one candidate.

It updates the output from:

demo.cc: In function 'int test_4(int, const char*, float)':
demo.cc:5:44: error: no matching function for call to 's4::member_1(int&, const char*&, float&)'
5 |   return s4::member_1 (first, second, third);
  |                                            ^
demo.cc:1:24: note: candidate: 'static int s4::member_1(int, const char**, float)'
1 | struct s4 { static int member_1 (int one, const char **two, float three); };
  |                        ^~~~~~~~
demo.cc:1:56: note:   no known conversion for argument 2 from 'const char*' to 'const char**'
1 | struct s4 { static int member_1 (int one, const char **two, float three); };
  |                                           ~~~~~~~~~~~~~^~~

to:

demo.cc: In function 'int test_4(int, const char*, float)':
demo.cc:5:31: error: no matching function for call to 's4::member_1(int&, const char*&, float&)'
5 |   return s4::member_1 (first, second, third);
  |                               ^~~~~~
demo.cc:1:24: note: candidate: 'static int s4::member_1(int, const char**, float)'
1 | struct s4 { static int member_1 (int one, const char **two, float three); };
  |                        ^~~~~~~~
demo.cc:1:56: note:   no known conversion for argument 2 from 'const char*' to 'const char**'
1 | struct s4 { static int member_1 (int one, const char **two, float three); };
  |                                           ~~~~~~~~~~~~~^~~

updating the location of the "error" to use that of the argument with
the mismatching type.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu; adds a
further 33 PASS results to g++.sum.

OK for trunk?

gcc/cp/ChangeLog:
	PR c++/85110
	* call.c (struct conversion_info): Add "loc" field.
	(arg_conversion_rejection): Add "loc" param, using it to
	initialize the new field.
	(bad_arg_conversion_rejection): Likewise.
	(explicit_conversion_rejection): Initialize the new field to
	UNKNOWN_LOCATION.
	(template_conversion_rejection): Likewise.
	(add_function_candidate): Pass on the argument location to the new
	param of arg_conversion_rejection.
	(add_conv_candidate): Likewise.
	(build_builtin_candidate): Likewise.
	(build_user_type_conversion_1): Likewise.
	(get_location_for_arg_conversion): New function.
	(get_location_for_unmatched_call): New function.
	(build_new_method_call_1): Call get_location_for_unmatched_call
	when reporting on unmatched functions, and use it for the error's
	location.

gcc/testsuite/ChangeLog:
	PR c++/85110
	* g++.dg/diagnostic/param-type-mismatch-2.C: Update expected
	results to underline the pertinent argument in the initial
	error for unmatched calls in which there is a single candidate.
	Add test coverage for an unmatched overloaded operator.
---
 gcc/cp/call.c                                      | 107 ++++++++++++++++++---
 .../g++.dg/diagnostic/param-type-mismatch-2.C      |  37 ++++++-
 2 files changed, 123 insertions(+), 21 deletions(-)

Comments

Jason Merrill Aug. 30, 2018, 10:18 p.m. UTC | #1
On Thu, Aug 23, 2018 at 2:08 PM, David Malcolm <dmalcolm@redhat.com> wrote:
> This is a followup to:
>
>   "[PATCH] C++: underline param in print_conversion_rejection (more PR c++/85110)"
>      https://gcc.gnu.org/ml/gcc-patches/2018-08/msg01480.html
>
> to highlight the pertinent argument in a unmatched function call
> for which there is one candidate.
>
> It updates the output from:
>
> demo.cc: In function 'int test_4(int, const char*, float)':
> demo.cc:5:44: error: no matching function for call to 's4::member_1(int&, const char*&, float&)'
> 5 |   return s4::member_1 (first, second, third);
>   |                                            ^
> demo.cc:1:24: note: candidate: 'static int s4::member_1(int, const char**, float)'
> 1 | struct s4 { static int member_1 (int one, const char **two, float three); };
>   |                        ^~~~~~~~
> demo.cc:1:56: note:   no known conversion for argument 2 from 'const char*' to 'const char**'
> 1 | struct s4 { static int member_1 (int one, const char **two, float three); };
>   |                                           ~~~~~~~~~~~~~^~~
>
> to:
>
> demo.cc: In function 'int test_4(int, const char*, float)':
> demo.cc:5:31: error: no matching function for call to 's4::member_1(int&, const char*&, float&)'
> 5 |   return s4::member_1 (first, second, third);
>   |                               ^~~~~~

Hmm, it seems pretty subtle to just change the highlighting when the
message talks about the call as a whole.  I think if we're going to
focus in this way we might change the diagnostic to something like

error: no known conversion for argument 2 from 'const char*' to 'const char**'
note: in call to 'static int s4::member_1(int, const char**, float)'

or whatever the messages are from
convert_arguments/convert_for_initialization which already deal with
the single-candidate case for non-member functions.

Jason
diff mbox series

Patch

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index ef445e0..3bc42f5 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -436,6 +436,8 @@  struct conversion_info {
   tree from;
   /* The type of the parameter.  */
   tree to_type;
+  /* The location of the argument.  */
+  location_t loc;
 };
   
 struct rejection_reason {
@@ -627,24 +629,28 @@  arity_rejection (tree first_arg, int expected, int actual)
 }
 
 static struct rejection_reason *
-arg_conversion_rejection (tree first_arg, int n_arg, tree from, tree to)
+arg_conversion_rejection (tree first_arg, int n_arg, tree from, tree to,
+			  location_t loc)
 {
   struct rejection_reason *r = alloc_rejection (rr_arg_conversion);
   int adjust = first_arg != NULL_TREE;
   r->u.conversion.n_arg = n_arg - adjust;
   r->u.conversion.from = from;
   r->u.conversion.to_type = to;
+  r->u.conversion.loc = loc;
   return r;
 }
 
 static struct rejection_reason *
-bad_arg_conversion_rejection (tree first_arg, int n_arg, tree from, tree to)
+bad_arg_conversion_rejection (tree first_arg, int n_arg, tree from, tree to,
+			      location_t loc)
 {
   struct rejection_reason *r = alloc_rejection (rr_bad_arg_conversion);
   int adjust = first_arg != NULL_TREE;
   r->u.bad_conversion.n_arg = n_arg - adjust;
   r->u.bad_conversion.from = from;
   r->u.bad_conversion.to_type = to;
+  r->u.bad_conversion.loc = loc;
   return r;
 }
 
@@ -655,6 +661,7 @@  explicit_conversion_rejection (tree from, tree to)
   r->u.conversion.n_arg = 0;
   r->u.conversion.from = from;
   r->u.conversion.to_type = to;
+  r->u.conversion.loc = UNKNOWN_LOCATION;
   return r;
 }
 
@@ -665,6 +672,7 @@  template_conversion_rejection (tree from, tree to)
   r->u.conversion.n_arg = 0;
   r->u.conversion.from = from;
   r->u.conversion.to_type = to;
+  r->u.conversion.loc = UNKNOWN_LOCATION;
   return r;
 }
 
@@ -2254,14 +2262,17 @@  add_function_candidate (struct z_candidate **candidates,
       if (! t)
 	{
 	  viable = 0;
-	  reason = arg_conversion_rejection (first_arg, i, argtype, to_type);
+	  reason = arg_conversion_rejection (first_arg, i, argtype, to_type,
+					     EXPR_LOCATION (arg));
 	  break;
 	}
 
       if (t->bad_p)
 	{
 	  viable = -1;
-	  reason = bad_arg_conversion_rejection (first_arg, i, arg, to_type);
+	  reason = bad_arg_conversion_rejection (first_arg, i, arg, to_type,
+						 EXPR_LOCATION (arg));
+
 	}
     }
 
@@ -2350,7 +2361,8 @@  add_conv_candidate (struct z_candidate **candidates, tree fn, tree obj,
       if (t->bad_p)
 	{
 	  viable = -1;
-	  reason = bad_arg_conversion_rejection (NULL_TREE, i, arg, convert_type);
+	  reason = bad_arg_conversion_rejection (NULL_TREE, i, arg, convert_type,
+						 EXPR_LOCATION (arg));
 	}
 
       if (i == 0)
@@ -2411,13 +2423,14 @@  build_builtin_candidate (struct z_candidate **candidates, tree fnname,
 	  /* We need something for printing the candidate.  */
 	  t = build_identity_conv (types[i], NULL_TREE);
 	  reason = arg_conversion_rejection (NULL_TREE, i, argtypes[i],
-					     types[i]);
+					     types[i], EXPR_LOCATION (args[i]));
 	}
       else if (t->bad_p)
 	{
 	  viable = 0;
 	  reason = bad_arg_conversion_rejection (NULL_TREE, i, args[i],
-						 types[i]);
+						 types[i],
+						 EXPR_LOCATION (args[i]));
 	}
       convs[i] = t;
     }
@@ -2436,7 +2449,8 @@  build_builtin_candidate (struct z_candidate **candidates, tree fnname,
 	{
 	  viable = 0;
 	  reason = arg_conversion_rejection (NULL_TREE, 0, argtypes[2],
-					     boolean_type_node);
+					     boolean_type_node,
+					     EXPR_LOCATION (args[2]));
 	}
     }
 
@@ -3927,7 +3941,8 @@  build_user_type_conversion_1 (tree totype, tree expr, int flags,
 	    {
 	      cand->viable = 0;
 	      cand->reason = arg_conversion_rejection (NULL_TREE, -2,
-						       rettype, totype);
+						       rettype, totype,
+						       EXPR_LOCATION (expr));
 	    }
 	  else if (DECL_NONCONVERTING_P (cand->fn)
 		   && ics->rank > cr_exact)
@@ -3947,7 +3962,8 @@  build_user_type_conversion_1 (tree totype, tree expr, int flags,
 	      cand->viable = -1;
 	      cand->reason
 		= bad_arg_conversion_rejection (NULL_TREE, -2,
-						rettype, totype);
+						rettype, totype,
+						EXPR_LOCATION (expr));
 	    }
 	  else if (primary_template_specialization_p (cand->fn)
 		   && ics->rank > cr_exact)
@@ -9157,6 +9173,61 @@  name_as_c_string (tree name, tree type, bool *free_p)
   return CONST_CAST (char *, pretty_name);
 }
 
+/* Subroutine of get_location_for_unmatched_call.
+   Return the location of the pertinent argument in INFO, if
+   available.
+   Otherwise, return DEFAULT_LOCATION.  */
+
+static location_t
+get_location_for_arg_conversion (conversion_info *info,
+				 location_t default_location)
+{
+  if (info->loc == UNKNOWN_LOCATION)
+    return default_location;
+  return info->loc;
+}
+
+/* Get the best location to use when reporting a function call
+   for which there are no valid candidates.
+
+   If there is just one candidate, and it is invalid due to the
+   argument type, use the location of the pertinent argument.
+
+   Otherwise, use DEFAULT_LOCATION.  */
+
+static location_t
+get_location_for_unmatched_call (z_candidate *candidates,
+				 location_t default_location)
+{
+  if (!candidates)
+    return default_location;
+
+  /* Must be exactly one candidate.  */
+  if (candidates->next)
+    return default_location;
+
+  /* Must be an rr_arg_conversion or rr_bad_arg_conversion.  */
+  z_candidate *candidate = candidates;
+  rejection_reason *r = candidate->reason;
+
+  if (r == NULL)
+    return default_location;
+
+  switch (r->code)
+    {
+    default:
+      return default_location;
+
+    case rr_arg_conversion:
+      return get_location_for_arg_conversion (&r->u.conversion,
+					      default_location);
+
+    case rr_bad_arg_conversion:
+      return get_location_for_arg_conversion (&r->u.bad_conversion,
+					      default_location);
+    }
+}
+
 /* Build a call to "INSTANCE.FN (ARGS)".  If FN_P is non-NULL, it will
    be set, upon return, to the function called.  ARGS may be NULL.
    This may change ARGS.  */
@@ -9375,13 +9446,16 @@  build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args,
     {
       if (complain & tf_error)
 	{
+	  location_t loc = get_location_for_unmatched_call (candidates,
+							    input_location);
 	  auto_diagnostic_group d;
 	  if (!COMPLETE_OR_OPEN_TYPE_P (basetype))
 	    cxx_incomplete_type_error (instance, basetype);
 	  else if (optype)
-	    error ("no matching function for call to %<%T::operator %T(%A)%#V%>",
-		   basetype, optype, build_tree_list_vec (user_args),
-		   TREE_TYPE (instance));
+	    error_at (loc,
+		      "no matching function for call to %<%T::operator %T(%A)%#V%>",
+		      basetype, optype, build_tree_list_vec (user_args),
+		      TREE_TYPE (instance));
 	  else
 	    {
 	      tree arglist = build_tree_list_vec (user_args);
@@ -9396,9 +9470,10 @@  build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args,
 		errname = lookup_template_function (errname, explicit_targs);
 	      if (skip_first_for_error)
 		arglist = TREE_CHAIN (arglist);
-	      error ("no matching function for call to %<%T::%s%E(%A)%#V%>",
-		     basetype, &"~"[!twiddle], errname, arglist,
-		     TREE_TYPE (instance));
+	      error_at (loc,
+			"no matching function for call to %<%T::%s%E(%A)%#V%>",
+			basetype, &"~"[!twiddle], errname, arglist,
+			TREE_TYPE (instance));
 	    }
 	  print_z_candidates (location_of (name), candidates);
 	}
diff --git a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
index 4957f61..8fb167a 100644
--- a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
+++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
@@ -74,7 +74,7 @@  int test_4 (int first, const char *second, float third)
   return s4::member_1 (first, second, third); // { dg-error "no matching function for call to 's4::member_1\\(int&, const char\\*&, float&\\)'" }
   /* { dg-begin-multiline-output "" }
    return s4::member_1 (first, second, third);
-                                            ^
+                               ^~~~~~
      { dg-end-multiline-output "" } */
   // { dg-message "candidate: 'static int s4::member_1\\(int, const char\\*\\*, float\\)'" "" { target *-*-* } s4_member_1 }
   /* { dg-begin-multiline-output "" }
@@ -98,7 +98,7 @@  int test_5 (int first, const char *second, float third)
   return inst.member_1 (first, second, third); // { dg-error "no matching function for call to 's5::member_1\\(int&, const char\\*&, float&\\)'" }
   /* { dg-begin-multiline-output "" }
    return inst.member_1 (first, second, third);
-                                             ^
+                                ^~~~~~
      { dg-end-multiline-output "" } */
   // { dg-message "candidate: 'int s5::member_1\\(int, const char\\*\\*, float\\)'" "" { target *-*-* } s5_member_1 }
   /* { dg-begin-multiline-output "" }
@@ -121,7 +121,7 @@  int test_6 (int first, const char *second, float third, s6 *ptr)
   return ptr->member_1 (first, second, third); // { dg-error "no matching function for call to 's6::member_1\\(int&, const char\\*&, float&\\)'" }
   /* { dg-begin-multiline-output "" }
    return ptr->member_1 (first, second, third);
-                                             ^
+                                ^~~~~~
      { dg-end-multiline-output "" } */
   // { dg-message "candidate: 'int s6::member_1\\(int, const char\\*\\*, float\\)'" "" { target *-*-* } s6_member_1 }
   /* { dg-begin-multiline-output "" }
@@ -171,7 +171,7 @@  int test_8 (int first, const char *second, float third)
   return s8 <const char **>::member_1 (first, second, third); // { dg-error "no matching function for call to 's8<const char\\*\\*>::member_1\\(int&, const char\\*&, float&\\)'" }
   /* { dg-begin-multiline-output "" }
    return s8 <const char **>::member_1 (first, second, third);
-                                                            ^
+                                               ^~~~~~
      { dg-end-multiline-output "" } */
   // { dg-message "candidate: 'static int s8<T>::member_1\\(int, T, float\\)" "" { target *-*-* } s8_member_1 }
   /* { dg-begin-multiline-output "" }
@@ -196,7 +196,7 @@  int test_9 (int first, const char *second, float third)
   return inst.member_1 (first, second, third); // { dg-error "no matching function for call to 's9<const char\\*\\*>::member_1\\(int&, const char\\*&, float&\\)'" }
   /* { dg-begin-multiline-output "" }
    return inst.member_1 (first, second, third);
-                                             ^
+                                ^~~~~~
      { dg-end-multiline-output "" } */
   // { dg-message "candidate: 'int s9<T>::member_1\\(int, T, float\\)" "" { target *-*-* } s9_member_1 }
   /* { dg-begin-multiline-output "" }
@@ -209,3 +209,30 @@  int test_9 (int first, const char *second, float third)
                                     ~~^~~
      { dg-end-multiline-output "" } */
 }
+
+/* Overloaded operator (with one candidate).  */
+
+struct s10 {};
+
+extern int operator- (const s10&, int); // { dg-line s10_operator }
+
+int test_10 ()
+{
+  s10 v10_a, v10_b;
+
+  return v10_a - v10_b; // { dg-error "no match for" }
+  /* { dg-begin-multiline-output "" }
+   return v10_a - v10_b;
+          ~~~~~~^~~~~~~
+     { dg-end-multiline-output "" } */
+  // { dg-message "candidate" "" { target *-*-* } s10_operator }
+  /* { dg-begin-multiline-output "" }
+ extern int operator- (const s10&, int);
+            ^~~~~~~~
+     { dg-end-multiline-output "" } */
+  // { dg-message "no known conversion for argument 2 from" "" { target *-*-* } s10_operator }
+  /* { dg-begin-multiline-output "" }
+ extern int operator- (const s10&, int);
+                                   ^~~
+     { dg-end-multiline-output "" } */
+}