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(-) 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 "" } */ +} -- 1.8.5.3