This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] C++: use an optional vec<location_t> for callsites
- From: David Malcolm <dmalcolm at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Cc: David Malcolm <dmalcolm at redhat dot com>
- Date: Wed, 23 Aug 2017 16:15:54 -0400
- Subject: [PATCH] C++: use an optional vec<location_t> for callsites
- Authentication-results: sourceware.org; auth=none
- Authentication-results: ext-mx08.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com
- Authentication-results: ext-mx08.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=dmalcolm at redhat dot com
- Dmarc-filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 8F0A6C0587E5
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