This patch extends various call-handling locations within the C++ frontend so that they can accept a possibly NULL vec<location_t> *, using the location information if available in various diagnostics.
It updates the C++ frontend's parser for this production: postfix-expression: postfix-expression ( expression-list [opt] ) within cp_parser_postfix_expression so that it captures an auto_vec<location_t> for the arguments (via cp_parser_parenthesized_expression_list), and passes that around within the parser, so that e.g. error: invalid conversion from 'int' to 'const char*' [-fpermissive] return callee_1 (first, second, third); ^ becomes: error: invalid conversion from 'int' to 'const char*' [-fpermissive] return callee_1 (first, second, third); ^~~~~~ The vec<location_t> * are also passed to the arg-checking code of r251238 ( https://gcc.gnu.org/ml/gcc-patches/2017-08/msg01164.html ) so that -Wformat can underline mismatching arguments for C++. Doing this for C++ requires handling of the case where the vec<location_t> from the expression-list needs to be offset by 1 relative to the args due to the "this" parameter not being part of the expression-list, e.g. in: logger->log ("%i %s %i", 100, 101, 102); [1] ^~ ~~~ hence the patch adds a new "argument_locs" class to be responsible for this in various places in c-family. Successfully bootstrapped®rtested on x86_64-pc-linux-gnu; OK for trunk? [1] We don't yet underline the "%s" in C++; I plan to fix that as a separate patch. gcc/c-family/ChangeLog: * c-common.c (argument_locs::get_param_location): New method. (check_function_arguments): Convert param "arglocs" from vec<location_t> * to argument_locs. * c-common.h (class argument_locs): New class. (check_function_arguments): Convert final param from vec<location_t> * to argument_locs. (check_function_format): Likewise. * c-format.c (struct format_check_results): Add ctor; convert field "arglocs" from vec<location_t> * to argument_locs. (check_function_format): Convert param "arglocs" from vec<location_t> * to argument_locs. (check_format_info): Likewise. Use a ctor when initializing format_ctx. (check_format_arg): Convert local "arglocs" from vec<location_t> * to argument_locs. (class argument_parser): Likewise for field "arglocs". (argument_parser::argument_parser): Likewise. (check_format_info_main): Likewise for param "arglocs". (check_format_types): Likewise. Replace logic for looking up locations within arglocs with a call to argument_locs::get_param_location. gcc/c/ChangeLog: * c-typeck.c (build_function_call_vec): Update for conversion of final parameter of check_function_arguments. gcc/cp/ChangeLog: * call.c (print_z_candidate): Add optional "arglocs" param and pass it to fn_type_unification. (print_z_candidates): Add optional "arglocs" param and pass it to print_z_candidate. (print_error_for_call_failure): Add optional "arglocs" param and pass it to print_z_candidates. (build_new_function_call): Add "arglocs" param and pass it to print_error_for_call_failure and build_over_call. (convert_like_real): Convert param "expr" from tree to cp_expr. (build_over_call): Add optional "arglocs" param. Use it if available when calling convert_like_with_context, by converting "arg" to a cp_expr. Use arglocs when calling check_function_arguments, determining the appropriate offset to use based on whether there was a "first_arg". (build_new_method_call_1): Add "arglocs" param and pass it to build_over_call. (build_new_method_call): Add "arglocs" param and pass it to build_new_method_call_1. * cp-tree.h (build_new_function_call): Add optional vec<location_t> * param. (build_new_method_call): Likewise. (fn_type_unification): Likewise. (finish_call_expr): Likewise. (cp_build_function_call_vec): Likewise. * parser.c (cp_parser_postfix_expression): Within handling of "postfix-expression ( expression-list [opt] )" production, add auto_vec<location_t> and pass it to be built to cp_parser_parenthesized_expression_list; pass it for use to calls to build_new_method_call and finish_call_expr. (cp_parser_parenthesized_expression_list): Add optional "arglocs" param. Convert local "expr" to cp_expr, using it to populate "arglocs" if non-NULL. * pt.c (unify_type_mismatch): Convert final param from tree to cp_expr to fix the location of the "inform". (unify_arg_conversion): Likewise. (fn_type_unification): Add "arglocs" param and pass it to type_unification_real. (check_non_deducible_conversion): Convert "arg" from tree to cp_expr. (type_unification_real): Add optional "arglocs" param. Convert local "arg" from tree to cp_expr, reading its location from arglocs, or, failing that, from input_location. * semantics.c (finish_call_expr): Add optional vec<location_t> * param and pass to calls to build_new_method_call and build_new_function_call. * typeck.c (cp_build_function_call_vec): Add optional vec<location_t> * param and pass to call to check_function_arguments. gcc/testsuite/ChangeLog: * g++.dg/Wformat-on-method.C: New test case. * g++.dg/diagnostic/param-type-mismatch.C: Update expected results to reflect underlining of pertinent arguments. --- gcc/c-family/c-common.c | 27 +++++++- gcc/c-family/c-common.h | 39 ++++++++++- gcc/c-family/c-format.c | 53 ++++++++------- gcc/c/c-typeck.c | 3 +- gcc/cp/call.c | 77 ++++++++++++++-------- gcc/cp/cp-tree.h | 15 +++-- gcc/cp/parser.c | 30 ++++++--- gcc/cp/pt.c | 54 ++++++++++----- gcc/cp/semantics.c | 9 +-- gcc/cp/typeck.c | 6 +- gcc/testsuite/g++.dg/Wformat-on-method.C | 31 +++++++++ .../g++.dg/diagnostic/param-type-mismatch.C | 18 ++--- 12 files changed, 261 insertions(+), 101 deletions(-) create mode 100644 gcc/testsuite/g++.dg/Wformat-on-method.C diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 156c89d..d238064 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -5534,12 +5534,35 @@ attribute_fallthrough_p (tree attr) } +/* Lookup the location of the argument with 0-based index PARAM_IDX, + gracefully failing if data is unavailable, and handling + offsetting the index for the case where the "this" + wasn't in the expression-list. */ + +location_t +argument_locs::get_param_location (unsigned int param_idx) const +{ + if (m_locs == NULL) + return UNKNOWN_LOCATION; + + if (param_idx < m_offset) + return UNKNOWN_LOCATION; + + param_idx -= m_offset; + + gcc_assert (param_idx < m_locs->length ()); + + return (*m_locs)[param_idx]; +} + /* Check for valid arguments being passed to a function with FNTYPE. There are NARGS arguments in the array ARGARRAY. LOC should be used for - diagnostics. Return true if -Wnonnull warning has been diagnosed. */ + diagnostics. Return true if -Wnonnull warning has been diagnosed. + ARGLOCS gives the locations of the arguments + (if its "locs" is non-NULL). */ bool check_function_arguments (location_t loc, const_tree fndecl, const_tree fntype, - int nargs, tree *argarray, vec<location_t> *arglocs) + int nargs, tree *argarray, argument_locs arglocs) { bool warned_p = false; diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 8e36768..7a0a5a0 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -807,8 +807,43 @@ extern const char *fname_as_string (int); extern tree fname_decl (location_t, unsigned, tree); extern int check_user_alignment (const_tree, bool); + +/* Information about the locations of arguments at a callsite, + based on parsing an expression-list. + + A bundle of a (possibly NULL) vec<location_t> *, together with + an offset for determining how this vec corresponds to the + arguments at a callsite. + + We either have an offset of 0: + | argarray[0] | argarray[1] | argarray[2] | ... + | m_locs [0] | m_locs [1] | m_locs [2] | ... + + or an offset of 1: + | argarray[0] | argarray[1] | argarray[2] | argarray[3] + | "this" | m_locs [0] | m_locs [1] | m_locs[2] + + for the C++ cases where the expression-list didn't contain "this", + such as in "ptr->call (expr0, expr1, expr2)". */ + +class argument_locs +{ + public: + argument_locs (const vec<location_t> *locs, unsigned int offset) + : m_locs (locs), m_offset (offset) + { + gcc_assert (offset == 0 || offset == 1); + } + + location_t get_param_location (unsigned int param_idx) const; + + private: + const vec<location_t> *m_locs; + unsigned int m_offset; +}; + extern bool check_function_arguments (location_t loc, const_tree, const_tree, - int, tree *, vec<location_t> *); + int, tree *, argument_locs); extern void check_function_arguments_recurse (void (*) (void *, tree, unsigned HOST_WIDE_INT), @@ -816,7 +851,7 @@ extern void check_function_arguments_recurse (void (*) unsigned HOST_WIDE_INT); extern bool check_builtin_function_arguments (location_t, vec<location_t>, tree, int, tree *); -extern void check_function_format (tree, int, tree *, vec<location_t> *); +extern void check_function_format (tree, int, tree *, argument_locs); extern bool attribute_fallthrough_p (tree); extern tree handle_format_attribute (tree *, tree, tree, int, bool *); extern tree handle_format_arg_attribute (tree *, tree, tree, int, bool *); diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c index 0dba979..e992a06 100644 --- a/gcc/c-family/c-format.c +++ b/gcc/c-family/c-format.c @@ -986,10 +986,16 @@ struct format_check_results struct format_check_context { + format_check_context (format_check_results *res_, + function_format_info *info_, + tree params_, + argument_locs arglocs_) + : res (res_), info (info_), params (params_), arglocs (arglocs_) {} + format_check_results *res; function_format_info *info; tree params; - vec<location_t> *arglocs; + argument_locs arglocs; }; /* Return the format name (as specified in the original table) for the format @@ -1012,8 +1018,7 @@ format_flags (int format_num) gcc_unreachable (); } -static void check_format_info (function_format_info *, tree, - vec<location_t> *); +static void check_format_info (function_format_info *, tree, argument_locs); static void check_format_arg (void *, tree, unsigned HOST_WIDE_INT); static void check_format_info_main (format_check_results *, function_format_info *, const char *, @@ -1021,7 +1026,7 @@ static void check_format_info_main (format_check_results *, int, tree, unsigned HOST_WIDE_INT, object_allocator<format_wanted_type> &, - vec<location_t> *); + argument_locs); static void init_dollar_format_checking (int, tree); static int maybe_read_dollar_number (const char **, int, @@ -1037,7 +1042,7 @@ static void check_format_types (const substring_loc &fmt_loc, const format_kind_info *fki, int offset_to_type_start, char conversion_char, - vec<location_t> *arglocs); + argument_locs arglocs); static void format_type_warning (const substring_loc &fmt_loc, source_range *param_range, format_wanted_type *, tree, @@ -1081,7 +1086,7 @@ decode_format_type (const char *s) void check_function_format (tree attrs, int nargs, tree *argarray, - vec<location_t> *arglocs) + argument_locs arglocs) { tree a; @@ -1406,9 +1411,8 @@ get_flag_spec (const format_flag_spec *spec, int flag, const char *predicates) static void check_format_info (function_format_info *info, tree params, - vec<location_t> *arglocs) + argument_locs arglocs) { - format_check_context format_ctx; unsigned HOST_WIDE_INT arg_num; tree format_tree; format_check_results res; @@ -1437,10 +1441,7 @@ check_format_info (function_format_info *info, tree params, res.number_other = 0; res.format_string_loc = input_location; - format_ctx.res = &res; - format_ctx.info = info; - format_ctx.params = params; - format_ctx.arglocs = arglocs; + format_check_context format_ctx (&res, info, params, arglocs); check_function_arguments_recurse (check_format_arg, &format_ctx, format_tree, arg_num); @@ -1525,7 +1526,7 @@ check_format_arg (void *ctx, tree format_tree, format_check_results *res = format_ctx->res; function_format_info *info = format_ctx->info; tree params = format_ctx->params; - vec<location_t> *arglocs = format_ctx->arglocs; + argument_locs arglocs = format_ctx->arglocs; int format_length; HOST_WIDE_INT offset; @@ -1777,7 +1778,7 @@ class argument_parser location_t format_string_loc, flag_chars_t &flag_chars, int &has_operand_number, tree first_fillin_param, object_allocator <format_wanted_type> &fwt_pool_, - vec<location_t> *arglocs); + argument_locs arglocs); bool read_any_dollar (); @@ -1856,7 +1857,7 @@ class argument_parser private: format_wanted_type *first_wanted_type; format_wanted_type *last_wanted_type; - vec<location_t> *arglocs; + argument_locs arglocs; }; /* flag_chars_t's constructor. */ @@ -2008,7 +2009,7 @@ argument_parser (function_format_info *info_, const char *&format_chars_, int &has_operand_number_, tree first_fillin_param_, object_allocator <format_wanted_type> &fwt_pool_, - vec<location_t> *arglocs_) + argument_locs arglocs_) : info (info_), fki (&format_types[info->format_type]), flag_specs (fki->flag_specs), @@ -2768,7 +2769,7 @@ check_format_info_main (format_check_results *res, int format_length, tree params, unsigned HOST_WIDE_INT arg_num, object_allocator <format_wanted_type> &fwt_pool, - vec<location_t> *arglocs) + argument_locs arglocs) { const char * const orig_format_chars = format_chars; const tree first_fillin_param = params; @@ -3046,7 +3047,7 @@ check_format_types (const substring_loc &fmt_loc, format_wanted_type *types, const format_kind_info *fki, int offset_to_type_start, char conversion_char, - vec<location_t> *arglocs) + argument_locs arglocs) { for (; types != 0; types = types->next) { @@ -3085,22 +3086,24 @@ check_format_types (const substring_loc &fmt_loc, char_type_flag = 0; source_range param_range; - source_range *param_range_ptr; + source_range *param_range_ptr = NULL; if (EXPR_HAS_LOCATION (cur_param)) { param_range = EXPR_LOCATION_RANGE (cur_param); param_range_ptr = ¶m_range; } - else if (arglocs) + else { /* arg_num is 1-based. */ gcc_assert (types->arg_num > 0); - location_t param_loc = (*arglocs)[types->arg_num - 1]; - param_range = get_range_from_loc (line_table, param_loc); - param_range_ptr = ¶m_range; + unsigned int param_idx = types->arg_num - 1; + location_t param_loc = arglocs.get_param_location (param_idx); + if (param_loc != UNKNOWN_LOCATION) + { + param_range = get_range_from_loc (line_table, param_loc); + param_range_ptr = ¶m_range; + } } - else - param_range_ptr = NULL; STRIP_NOPS (cur_param); diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c index d7ca148..5999a3b 100644 --- a/gcc/c/c-typeck.c +++ b/gcc/c/c-typeck.c @@ -3118,7 +3118,8 @@ build_function_call_vec (location_t loc, vec<location_t> arg_loc, /* Check that the arguments to the function are valid. */ bool warned_p = check_function_arguments (loc, fundecl, fntype, - nargs, argarray, &arg_loc); + nargs, argarray, + argument_locs (&arg_loc, 0)); if (name != NULL_TREE && !strncmp (IDENTIFIER_POINTER (name), "__builtin_", 10)) diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 067db59a..afcdd99 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -147,7 +147,8 @@ static int equal_functions (tree, tree); static int joust (struct z_candidate *, struct z_candidate *, bool, tsubst_flags_t); static int compare_ics (conversion *, conversion *); -static tree build_over_call (struct z_candidate *, int, tsubst_flags_t); +static tree build_over_call (struct z_candidate *, int, tsubst_flags_t, + vec<location_t> * = NULL); #define convert_like(CONV, EXPR, COMPLAIN) \ convert_like_real ((CONV), (EXPR), NULL_TREE, 0, \ /*issue_conversion_warnings=*/true, \ @@ -156,14 +157,16 @@ static tree build_over_call (struct z_candidate *, int, tsubst_flags_t); convert_like_real ((CONV), (EXPR), (FN), (ARGNO), \ /*issue_conversion_warnings=*/true, \ /*c_cast_p=*/false, (COMPLAIN)) -static tree convert_like_real (conversion *, tree, tree, int, bool, +static tree convert_like_real (conversion *, cp_expr, tree, int, bool, bool, tsubst_flags_t); static void op_error (location_t, enum tree_code, enum tree_code, tree, tree, tree, bool); static struct z_candidate *build_user_type_conversion_1 (tree, tree, int, tsubst_flags_t); -static void print_z_candidate (location_t, const char *, struct z_candidate *); -static void print_z_candidates (location_t, struct z_candidate *); +static void print_z_candidate (location_t, const char *, struct z_candidate *, + vec<location_t> * = NULL); +static void print_z_candidates (location_t, struct z_candidate *, + vec<location_t> * = NULL); static tree build_this (tree); static struct z_candidate *splice_viable (struct z_candidate *, bool, bool *); static bool any_strictly_viable (struct z_candidate *); @@ -3442,7 +3445,8 @@ print_arity_information (location_t loc, unsigned int have, unsigned int want) static void print_z_candidate (location_t loc, const char *msgstr, - struct z_candidate *candidate) + struct z_candidate *candidate, + vec<location_t> *arglocs) { const char *msg = (msgstr == NULL ? "" @@ -3531,7 +3535,7 @@ print_z_candidate (location_t loc, const char *msgstr, r->u.template_unification.return_type, r->u.template_unification.strict, r->u.template_unification.flags, - true, false); + true, false, arglocs); break; case rr_invalid_copy: inform (cloc, @@ -3560,7 +3564,8 @@ print_z_candidate (location_t loc, const char *msgstr, } static void -print_z_candidates (location_t loc, struct z_candidate *candidates) +print_z_candidates (location_t loc, struct z_candidate *candidates, + vec<location_t> *arglocs) { struct z_candidate *cand1; struct z_candidate **cand2; @@ -3606,7 +3611,7 @@ print_z_candidates (location_t loc, struct z_candidate *candidates) } for (; candidates; candidates = candidates->next) - print_z_candidate (loc, "candidate:", candidates); + print_z_candidate (loc, "candidate:", candidates, arglocs); } /* USER_SEQ is a user-defined conversion sequence, beginning with a @@ -4212,7 +4217,8 @@ perform_overload_resolution (tree fn, static void print_error_for_call_failure (tree fn, vec<tree, va_gc> *args, - struct z_candidate *candidates) + struct z_candidate *candidates, + vec<location_t> *arglocs = NULL) { tree targs = NULL_TREE; if (TREE_CODE (fn) == TEMPLATE_ID_EXPR) @@ -4232,7 +4238,7 @@ print_error_for_call_failure (tree fn, vec<tree, va_gc> *args, error_at (loc, "call of overloaded %<%D(%A)%> is ambiguous", name, build_tree_list_vec (args)); if (candidates) - print_z_candidates (loc, candidates); + print_z_candidates (loc, candidates, arglocs); } /* Return an expression for a call to FN (a namespace-scope function, @@ -4241,7 +4247,8 @@ print_error_for_call_failure (tree fn, vec<tree, va_gc> *args, tree build_new_function_call (tree fn, vec<tree, va_gc> **args, - tsubst_flags_t complain) + tsubst_flags_t complain, + vec<location_t> *arglocs) { struct z_candidate *candidates, *cand; bool any_viable_p; @@ -4275,7 +4282,7 @@ build_new_function_call (tree fn, vec<tree, va_gc> **args, return cp_build_function_call_vec (candidates->fn, args, complain); // Otherwise, emit notes for non-viable candidates. - print_error_for_call_failure (fn, *args, candidates); + print_error_for_call_failure (fn, *args, candidates, arglocs); } result = error_mark_node; } @@ -4307,7 +4314,7 @@ build_new_function_call (tree fn, vec<tree, va_gc> **args, flags |= LOOKUP_EXPLICIT_TMPL_ARGS; } - result = build_over_call (cand, flags, complain); + result = build_over_call (cand, flags, complain, arglocs); } /* Free all the conversions we allocated. */ @@ -6588,14 +6595,16 @@ maybe_print_user_conv_context (conversion *convs) conversions to inaccessible bases are permitted. */ static tree -convert_like_real (conversion *convs, tree expr, tree fn, int argnum, +convert_like_real (conversion *convs, cp_expr expr, tree fn, int argnum, bool issue_conversion_warnings, bool c_cast_p, tsubst_flags_t complain) { tree totype = convs->type; diagnostic_t diag_kind; int flags; - location_t loc = EXPR_LOC_OR_LOC (expr, input_location); + location_t loc = expr.get_location (); + if (loc == UNKNOWN_LOCATION) + loc = input_location; if (convs->bad_p && !(complain & tf_error)) return error_mark_node; @@ -6983,13 +6992,13 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum, cannot create a temporary. */ if (lvalue & clk_bitfield) error_at (loc, "cannot bind bitfield %qE to %qT", - expr, ref_type); + expr.get_value (), ref_type); else if (lvalue & clk_packed) error_at (loc, "cannot bind packed field %qE to %qT", - expr, ref_type); + expr.get_value (), ref_type); else error_at (loc, "cannot bind rvalue %qE to %qT", - expr, ref_type); + expr.get_value (), ref_type); return error_mark_node; } /* If the source is a packed field, and we must use a copy @@ -7003,7 +7012,7 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum, && type_has_nontrivial_copy_init (type)) { error_at (loc, "cannot bind packed field %qE to %qT", - expr, ref_type); + expr.get_value (), ref_type); return error_mark_node; } if (lvalue & clk_bitfield) @@ -7542,7 +7551,8 @@ unsafe_copy_elision_p (tree target, tree exp) bitmask of various LOOKUP_* flags which apply to the call itself. */ static tree -build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) +build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain, + vec<location_t> *arglocs) { tree fn = cand->fn; const vec<tree, va_gc> *args = cand->args; @@ -7687,6 +7697,15 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) nargs = parmlen; argarray = XALLOCAVEC (tree, nargs); + /* Determine offset to args covered by ARGLOCS within argarray. + We either have an offset of 0: + | argarray[0] | argarray[1] | argarray[2] | ... + | arglocs [0] | arglocs [1] | arglocs [2] | ... + or an offset of 1: + | argarray[0] | argarray[1] | argarray[2] | ... + | first_arg | arglocs [0] | arglocs [1] | arglocs[2] ...*/ + int arglocs_offset = first_arg != NULL_TREE ? 1 : 0; + /* The implicit parameters to a constructor are not considered by overload resolution, and must be of the proper type. */ if (DECL_CONSTRUCTOR_P (fn)) @@ -7805,7 +7824,9 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) parm = TREE_CHAIN (parm), ++arg_index, ++i) { tree type = TREE_VALUE (parm); - tree arg = (*args)[arg_index]; + cp_expr arg = (*args)[arg_index]; + if (arglocs) + arg.set_location ((*arglocs)[arg_index]); bool conversion_warning = true; conv = convs[i]; @@ -7949,7 +7970,9 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) fargs[j] = maybe_constant_value (argarray[j]); warned_p = check_function_arguments (input_location, fn, TREE_TYPE (fn), - nargs, fargs, NULL); + nargs, fargs, + argument_locs (arglocs, + arglocs_offset)); } if (DECL_INHERITED_CTOR (fn)) @@ -8917,7 +8940,8 @@ name_as_c_string (tree name, tree type, bool *free_p) static tree build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args, tree conversion_path, int flags, - tree *fn_p, tsubst_flags_t complain) + tree *fn_p, tsubst_flags_t complain, + vec<location_t> *arglocs) { struct z_candidate *candidates = 0, *cand; tree explicit_targs = NULL_TREE; @@ -9248,7 +9272,7 @@ build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args, if (fn_p) *fn_p = fn; /* Build the actual CALL_EXPR. */ - call = build_over_call (cand, flags, complain); + call = build_over_call (cand, flags, complain, arglocs); /* In an expression of the form `a->f()' where `f' turns out to be a static member function, `a' is none-the-less evaluated. */ @@ -9313,12 +9337,13 @@ build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args, tree build_new_method_call (tree instance, tree fns, vec<tree, va_gc> **args, tree conversion_path, int flags, - tree *fn_p, tsubst_flags_t complain) + tree *fn_p, tsubst_flags_t complain, + vec<location_t> *arglocs) { tree ret; bool subtime = timevar_cond_start (TV_OVERLOAD); ret = build_new_method_call_1 (instance, fns, args, conversion_path, flags, - fn_p, complain); + fn_p, complain, arglocs); timevar_cond_stop (TV_OVERLOAD, subtime); return ret; } diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 4dd9fc6..b1ec172 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5862,13 +5862,15 @@ extern tree extract_call_expr (tree); extern tree build_user_type_conversion (tree, tree, int, tsubst_flags_t); extern tree build_new_function_call (tree, vec<tree, va_gc> **, - tsubst_flags_t); + tsubst_flags_t, + vec<location_t> * = NULL); extern tree build_operator_new_call (tree, vec<tree, va_gc> **, tree *, tree *, tree, tree, tree *, tsubst_flags_t); extern tree build_new_method_call (tree, tree, vec<tree, va_gc> **, tree, - int, tree *, tsubst_flags_t); + int, tree *, tsubst_flags_t, + vec<location_t> * = NULL); extern tree build_special_member_call (tree, tree, vec<tree, va_gc> **, tree, int, tsubst_flags_t); @@ -6433,7 +6435,8 @@ extern tree instantiate_template (tree, tree, tsubst_flags_t); extern tree fn_type_unification (tree, tree, tree, const tree *, unsigned int, tree, unification_kind_t, int, - bool, bool); + bool, bool, + vec<location_t> * = NULL); extern void mark_decl_instantiated (tree, int); extern int more_specialized_fn (tree, tree, int); extern void do_decl_instantiation (tree, tree); @@ -6720,7 +6723,8 @@ bool empty_expr_stmt_p (tree); extern cp_expr perform_koenig_lookup (cp_expr, vec<tree, va_gc> *, tsubst_flags_t); extern tree finish_call_expr (tree, vec<tree, va_gc> **, bool, - bool, tsubst_flags_t); + bool, tsubst_flags_t, + vec<location_t> * = NULL); extern tree lookup_and_finish_template_variable (tree, tree, tsubst_flags_t = tf_warning_or_error); extern tree finish_template_variable (tree, tsubst_flags_t = tf_warning_or_error); extern cp_expr finish_increment_expr (cp_expr, enum tree_code); @@ -7026,7 +7030,8 @@ extern tree get_member_function_from_ptrfunc (tree *, tree, tsubst_flags_t); extern tree cp_build_function_call_nary (tree, tsubst_flags_t, ...) ATTRIBUTE_SENTINEL; extern tree cp_build_function_call_vec (tree, vec<tree, va_gc> **, - tsubst_flags_t); + tsubst_flags_t, + vec<location_t> * = NULL); extern tree build_x_binary_op (location_t, enum tree_code, tree, enum tree_code, tree, diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index b849824..29de054 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -2051,7 +2051,8 @@ static tree cp_parser_postfix_open_square_expression static tree cp_parser_postfix_dot_deref_expression (cp_parser *, enum cpp_ttype, cp_expr, bool, cp_id_kind *, location_t); static vec<tree, va_gc> *cp_parser_parenthesized_expression_list - (cp_parser *, int, bool, bool, bool *, location_t * = NULL); + (cp_parser *, int, bool, bool, bool *, location_t * = NULL, + vec<location_t> * = NULL); /* Values for the second parameter of cp_parser_parenthesized_expression_list. */ enum { non_attr = 0, normal_attr = 1, id_attr = 2 }; static void cp_parser_pseudo_destructor_name @@ -6961,6 +6962,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, bool saved_non_integral_constant_expression_p = false; tsubst_flags_t complain = complain_flags (decltype_p); vec<tree, va_gc> *args; + auto_vec<location_t> arglocs; location_t close_paren_loc = UNKNOWN_LOCATION; is_member_access = false; @@ -6981,7 +6983,8 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, (parser, non_attr, /*cast_p=*/false, /*allow_expansion_p=*/true, /*non_constant_p=*/NULL, - /*close_paren_loc=*/&close_paren_loc)); + /*close_paren_loc=*/&close_paren_loc, + &arglocs)); if (is_builtin_constant_p) { parser->integral_constant_expression_p @@ -7092,14 +7095,16 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, ? LOOKUP_NORMAL|LOOKUP_NONVIRTUAL : LOOKUP_NORMAL), /*fn_p=*/NULL, - complain)); + complain, + &arglocs)); } else postfix_expression = finish_call_expr (postfix_expression, &args, /*disallow_virtual=*/false, /*koenig_p=*/false, - complain); + complain, + &arglocs); } else if (TREE_CODE (postfix_expression) == OFFSET_REF || TREE_CODE (postfix_expression) == MEMBER_REF @@ -7114,14 +7119,16 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, = finish_call_expr (postfix_expression, &args, /*disallow_virtual=*/true, koenig_p, - complain); + complain, + &arglocs); else /* All other function calls. */ postfix_expression = finish_call_expr (postfix_expression, &args, /*disallow_virtual=*/false, koenig_p, - complain); + complain, + &arglocs); if (close_paren_loc != UNKNOWN_LOCATION) { @@ -7637,7 +7644,8 @@ cp_parser_parenthesized_expression_list (cp_parser* parser, bool cast_p, bool allow_expansion_p, bool *non_constant_p, - location_t *close_paren_loc) + location_t *close_paren_loc, + vec<location_t> *arglocs) { vec<tree, va_gc> *expression_list; bool fold_expr_p = is_attribute_list != non_attr; @@ -7664,8 +7672,6 @@ cp_parser_parenthesized_expression_list (cp_parser* parser, if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN)) while (true) { - tree expr; - /* At the beginning of attribute lists, check to see if the next token is an identifier. */ if (is_attribute_list == id_attr @@ -7680,6 +7686,7 @@ cp_parser_parenthesized_expression_list (cp_parser* parser, } else { + cp_expr expr; bool expr_non_constant_p; /* Parse the next assignment-expression. */ @@ -7723,7 +7730,10 @@ cp_parser_parenthesized_expression_list (cp_parser* parser, expressions to the list, so that we can still tell if the correct form for a parenthesized expression-list is found. That gives better errors. */ - vec_safe_push (expression_list, expr); + vec_safe_push (expression_list, expr.get_value ()); + + if (arglocs) + arglocs->safe_push (expr.get_location ()); if (expr == error_mark_node) goto skip_comma; diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 8d816c7..c642077 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -155,7 +155,8 @@ static int maybe_adjust_types_for_deduction (unification_kind_t, tree*, tree*, static int type_unification_real (tree, tree, tree, const tree *, unsigned int, int, unification_kind_t, int, vec<deferred_access_check, va_gc> **, - bool); + bool, + vec<location_t> * = NULL); static void note_template_header (int); static tree convert_nontype_argument_function (tree, tree, tsubst_flags_t); static tree convert_nontype_argument (tree, tree, tsubst_flags_t); @@ -6191,10 +6192,16 @@ unify_cv_qual_mismatch (bool explain_p, tree parm, tree arg) } static int -unify_type_mismatch (bool explain_p, tree parm, tree arg) +unify_type_mismatch (bool explain_p, tree parm, cp_expr arg) { if (explain_p) - inform (input_location, " mismatched types %qT and %qT", parm, arg); + { + location_t loc = arg.get_location (); + if (loc == UNKNOWN_LOCATION) + loc = input_location; + inform (loc, " mismatched types %qT and %qT", parm, + arg.get_value ()); + } return unify_invalid (explain_p); } @@ -6310,12 +6317,17 @@ unify_too_few_arguments (bool explain_p, int have, int wanted, static int unify_arg_conversion (bool explain_p, tree to_type, - tree from_type, tree arg) + tree from_type, cp_expr arg) { if (explain_p) - inform (EXPR_LOC_OR_LOC (arg, input_location), - " cannot convert %qE (type %qT) to type %qT", - arg, from_type, to_type); + { + location_t loc = arg.get_location (); + if (loc == UNKNOWN_LOCATION) + loc = input_location; + inform (loc, + " cannot convert %qE (type %qT) to type %qT", + arg.get_value (), from_type, to_type); + } return unify_invalid (explain_p); } @@ -18416,7 +18428,8 @@ fn_type_unification (tree fn, unification_kind_t strict, int flags, bool explain_p, - bool decltype_p) + bool decltype_p, + vec<location_t> *arglocs) { tree parms; tree fntype; @@ -18625,7 +18638,7 @@ fn_type_unification (tree fn, ok = !type_unification_real (DECL_INNERMOST_TEMPLATE_PARMS (fn), full_targs, parms, args, nargs, /*subr=*/0, - strict, flags, &checks, explain_p); + strict, flags, &checks, explain_p, arglocs); if (!explain_p) pop_tinst_level (); if (!ok) @@ -18860,7 +18873,7 @@ maybe_adjust_types_for_deduction (unification_kind_t strict, unify_one_argument. */ static int -check_non_deducible_conversion (tree parm, tree arg, int strict, +check_non_deducible_conversion (tree parm, cp_expr arg, int strict, int flags, bool explain_p) { tree type; @@ -18882,7 +18895,7 @@ check_non_deducible_conversion (tree parm, tree arg, int strict, else if (strict != DEDUCE_EXACT) { if (can_convert_arg (parm, type, - TYPE_P (arg) ? NULL_TREE : arg, + TYPE_P (arg) ? NULL_TREE : arg.get_value (), flags, explain_p ? tf_warning_or_error : tf_none)) return unify_success (explain_p); } @@ -19187,9 +19200,10 @@ type_unification_real (tree tparms, unification_kind_t strict, int flags, vec<deferred_access_check, va_gc> **checks, - bool explain_p) + bool explain_p, + vec<location_t> *arglocs) { - tree parm, arg; + tree parm; int i; int ntparms = TREE_VEC_LENGTH (tparms); int saw_undeduced = 0; @@ -19235,7 +19249,12 @@ type_unification_real (tree tparms, parameter pack is a non-deduced context. */ continue; - arg = args[ia]; + cp_expr arg = args[ia]; + if (arglocs) + arg.set_location ((*arglocs)[ia]); + else + if (!CAN_HAVE_LOCATION_P (arg.get_value ())) + arg.set_location (input_location); ++ia; if (unify_one_argument (tparms, full_targs, parm, arg, subr, strict, @@ -19398,7 +19417,12 @@ type_unification_real (tree tparms, parameter pack is a non-deduced context. */ continue; - arg = args[ia]; + cp_expr arg = args[ia]; + if (arglocs) + arg.set_location ((*arglocs)[ia]); + else + if (!CAN_HAVE_LOCATION_P (arg.get_value ())) + arg.set_location (input_location); ++ia; if (uses_template_parms (parm)) diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 5401e78..d58eef2 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -2304,7 +2304,8 @@ perform_koenig_lookup (cp_expr fn, vec<tree, va_gc> *args, tree finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual, - bool koenig_p, tsubst_flags_t complain) + bool koenig_p, tsubst_flags_t complain, + vec<location_t> *arglocs) { tree result; tree orig_fn; @@ -2388,7 +2389,7 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual, ? LOOKUP_NORMAL | LOOKUP_NONVIRTUAL : LOOKUP_NORMAL), /*fn_p=*/NULL, - complain); + complain, arglocs); } } @@ -2440,7 +2441,7 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual, ? LOOKUP_NORMAL|LOOKUP_NONVIRTUAL : LOOKUP_NORMAL), /*fn_p=*/NULL, - complain); + complain, arglocs); } else if (is_overloaded_fn (fn)) { @@ -2483,7 +2484,7 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual, } /* A call to a namespace-scope function. */ - result = build_new_function_call (fn, args, complain); + result = build_new_function_call (fn, args, complain, arglocs); } } else if (TREE_CODE (fn) == PSEUDO_DTOR_EXPR) diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 63667f3..3c8ef7a3 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -3577,7 +3577,8 @@ cp_build_function_call_nary (tree function, tsubst_flags_t complain, ...) tree cp_build_function_call_vec (tree function, vec<tree, va_gc> **params, - tsubst_flags_t complain) + tsubst_flags_t complain, + vec<location_t> *arglocs) { tree fntype, fndecl; int is_method; @@ -3699,7 +3700,8 @@ cp_build_function_call_vec (tree function, vec<tree, va_gc> **params, /* Check for errors in format strings and inappropriately null parameters. */ bool warned_p = check_function_arguments (input_location, fndecl, fntype, - nargs, argarray, NULL); + nargs, argarray, + argument_locs (arglocs, 0)); ret = build_cxx_call (function, nargs, argarray, complain); diff --git a/gcc/testsuite/g++.dg/Wformat-on-method.C b/gcc/testsuite/g++.dg/Wformat-on-method.C new file mode 100644 index 0000000..4ff22fb --- /dev/null +++ b/gcc/testsuite/g++.dg/Wformat-on-method.C @@ -0,0 +1,31 @@ +/* { dg-options "-Wformat -fdiagnostics-show-caret" } */ + +struct logger +{ + void log (const char *fmt, ...) + __attribute__ ((format (gnu_printf, (2), (3)))); + + void test (); +}; + +/* Test of location-lookup within method call. */ + +void test (logger *logger) +{ + logger->log ("%i %s %i", 100, 101, 102); /* { dg-warning "format '%s' expects argument of type 'char\\*', but argument 4 has type 'int'" } */ +/* { dg-begin-multiline-output "" } + logger->log ("%i %s %i", 100, 101, 102); + ~~~ ^ + { dg-end-multiline-output "" } */ +} + +/* Test of location-lookup with implicit "this". */ + +void logger::test () +{ + log ("%i %s %i", 100, 101, 102); /* { dg-warning "format '%s' expects argument of type 'char\\*', but argument 4 has type 'int'" } */ +/* { dg-begin-multiline-output "" } + log ("%i %s %i", 100, 101, 102); + ~~~ ^ + { dg-end-multiline-output "" } */ +} diff --git a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C index b8833ef..f9f5d74 100644 --- a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C +++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C @@ -17,7 +17,7 @@ int test_1 (int first, int second, float third) return callee_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" } /* { dg-begin-multiline-output "" } return callee_1 (first, second, third); - ^ + ^~~~~~ { dg-end-multiline-output "" } */ // { dg-message "initializing argument 2 of 'int callee_1\\(int, const char\\*, float\\)'" "" { target *-*-* } callee_1 } /* { dg-begin-multiline-output "" } @@ -35,7 +35,7 @@ int test_2 (int first, int second, float third) return callee_2 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" } /* { dg-begin-multiline-output "" } return callee_2 (first, second, third); - ^ + ^~~~~~ { dg-end-multiline-output "" } */ // { dg-message "initializing argument 2 of 'int callee_2\\(int, const char\\*, float\\)'" "" { target *-*-* } callee_2 } /* { dg-begin-multiline-output "" } @@ -56,7 +56,7 @@ int test_3 (int first, int second, float third) return callee_3 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" } /* { dg-begin-multiline-output "" } return callee_3 (first, second, third); - ^ + ^~~~~~ { dg-end-multiline-output "" } */ // { dg-message "initializing argument 2 of 'int callee_3\\(int, const char\\*, float\\)'" "" { target *-*-* } callee_3 } /* { dg-begin-multiline-output "" } @@ -74,7 +74,7 @@ int test_4 (int first, int second, float third) return s4::member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" } /* { dg-begin-multiline-output "" } return s4::member_1 (first, second, third); - ^ + ^~~~~~ { dg-end-multiline-output "" } */ /* { dg-begin-multiline-output "" } struct s4 { static int member_1 (int one, const char *two, float three); }; @@ -92,7 +92,7 @@ int test_5 (int first, int second, float third) return inst.member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" } /* { dg-begin-multiline-output "" } return inst.member_1 (first, second, third); - ^ + ^~~~~~ { dg-end-multiline-output "" } */ /* { dg-begin-multiline-output "" } struct s5 { int member_1 (int one, const char *two, float three); }; @@ -109,7 +109,7 @@ int test_6 (int first, int second, float third, s6 *ptr) return ptr->member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" } /* { dg-begin-multiline-output "" } return ptr->member_1 (first, second, third); - ^ + ^~~~~~ { dg-end-multiline-output "" } */ /* { dg-begin-multiline-output "" } struct s6 { int member_1 (int one, const char *two, float three); }; @@ -131,7 +131,7 @@ int test_7 (int first, int second, float third) { dg-end-multiline-output "" } */ /* { dg-begin-multiline-output "" } return test_7 <const char *> (first, second, third); - ^ + ^~~~~~ { dg-end-multiline-output "" } */ /* { dg-begin-multiline-output "" } int test_7 (int one, T two, float three); @@ -149,7 +149,7 @@ int test_8 (int first, int second, float third) return s8 <const char *>::member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" } /* { dg-begin-multiline-output "" } return s8 <const char *>::member_1 (first, second, third); - ^ + ^~~~~~ { dg-end-multiline-output "" } */ /* { dg-begin-multiline-output "" } struct s8 { static int member_1 (int one, T two, float three); }; @@ -168,7 +168,7 @@ int test_9 (int first, int second, float third) return inst.member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" } /* { dg-begin-multiline-output "" } return inst.member_1 (first, second, third); - ^ + ^~~~~~ { dg-end-multiline-output "" } */ /* { dg-begin-multiline-output "" } struct s9 { int member_1 (int one, T two, float three); }; -- 1.8.5.3