C++: improvements to binary operator diagnostics (PR c++/87504)
The C frontend is able (where expression locations are available) to print problems with binary operators in 3-location form, labelling the types of the expressions: arg_0 op arg_1 ~~~~~ ^~ ~~~~~ | | | arg1 type arg0 type The C++ frontend currently just shows the combined location: arg_0 op arg_1 ~~~~~~^~~~~~~~ and fails to highlight where the subexpressions are, or their types. This patch introduces a op_location_t struct for handling the above operator-location vs combined-location split, and a new class binary_op_rich_location for displaying the above, so that the C++ frontend is able to use the more detailed 3-location form for type mismatches in binary operators, and for -Wtautological-compare (where types are not displayed). Both forms can be seen in this example: bad-binary-ops.C:69:20: error: no match for 'operator&&' (operand types are 's' and 't') 69 | return ns_4::foo && ns_4::inner::bar; | ~~~~~~~~~ ^~ ~~~~~~~~~~~~~~~~ | | | | s t bad-binary-ops.C:69:20: note: candidate: 'operator&&(bool, bool)' <built-in> 69 | return ns_4::foo && ns_4::inner::bar; | ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~ The patch also allows for some uses of macros in -Wtautological-compare, where both sides of the comparison have been spelled the same way, e.g.: Wtautological-compare-ranges.c:23:11: warning: self-comparison always evaluates to true [-Wtautological-compare] 23 | if (FOO == FOO); | ^~ gcc/c-family/ChangeLog: PR c++/87504 * c-common.h (warn_tautological_cmp): Convert 1st param from location_t to const op_location_t &. * c-warn.c (find_array_ref_with_const_idx_r): Call fold_for_warn when testing for INTEGER_CST. (warn_tautological_bitwise_comparison): Convert 1st param from location_t to const op_location_t &; use it to build a binary_op_rich_location, and use this. (spelled_the_same_p): New function. (warn_tautological_cmp): Convert 1st param from location_t to const op_location_t &. Warn for macro expansions if spelled_the_same_p. Use binary_op_rich_location. gcc/c/ChangeLog: PR c++/87504 * c-typeck.c (class maybe_range_label_for_tree_type_mismatch): Move from here to gcc-rich-location.h and gcc-rich-location.c. (build_binary_op): Use struct op_location_t and class binary_op_rich_location. gcc/cp/ChangeLog: PR c++/87504 * call.c (op_error): Convert 1st param from location_t to const op_location_t &. Use binary_op_rich_location for binary ops. (build_conditional_expr_1): Convert 1st param from location_t to const op_location_t &. (build_conditional_expr): Likewise. (build_new_op_1): Likewise. (build_new_op): Likewise. * cp-tree.h (build_conditional_expr): Likewise. (build_new_op): Likewise. (build_x_binary_op): Likewise. (cp_build_binary_op): Likewise. * parser.c (cp_parser_primary_expression): Build a location for id-expression nodes. (cp_parser_binary_expression): Use an op_location_t when calling build_x_binary_op. (cp_parser_operator): Build a location for user-defined literals. * typeck.c (build_x_binary_op): Convert 1st param from location_t to const op_location_t &. (cp_build_binary_op): Likewise. Use binary_op_rich_location. gcc/ChangeLog: PR c++/87504 * gcc-rich-location.c (maybe_range_label_for_tree_type_mismatch::get_text): Move here from c/c-typeck.c. (binary_op_rich_location::binary_op_rich_location): New ctor. (binary_op_rich_location::use_operator_loc_p): New function. * gcc-rich-location.h (class maybe_range_label_for_tree_type_mismatch)): Move here from c/c-typeck.c. (struct op_location_t): New forward decl. (class binary_op_rich_location): New class. * tree.h (struct op_location_t): New struct. gcc/testsuite/ChangeLog: * c-c++-common/Wtautological-compare-ranges.c: New test. * g++.dg/cpp0x/pr51420.C: Add -fdiagnostics-show-caret and update expected output. * g++.dg/diagnostic/bad-binary-ops.C: Update expected output from 1-location form to 3-location form, with labelling of ranges with types. Add examples of id-expression nodes with namespaces. * g++.dg/diagnostic/param-type-mismatch-2.C: Likewise. From-SVN: r267273
This commit is contained in:
parent
dfd7fdca2a
commit
a14feb3c78
@ -1,3 +1,18 @@
|
||||
2018-12-19 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c++/87504
|
||||
* gcc-rich-location.c
|
||||
(maybe_range_label_for_tree_type_mismatch::get_text): Move here from
|
||||
c/c-typeck.c.
|
||||
(binary_op_rich_location::binary_op_rich_location): New ctor.
|
||||
(binary_op_rich_location::use_operator_loc_p): New function.
|
||||
* gcc-rich-location.h
|
||||
(class maybe_range_label_for_tree_type_mismatch)): Move here from
|
||||
c/c-typeck.c.
|
||||
(struct op_location_t): New forward decl.
|
||||
(class binary_op_rich_location): New class.
|
||||
* tree.h (struct op_location_t): New struct.
|
||||
|
||||
2018-12-19 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c++/43064
|
||||
|
@ -1,3 +1,18 @@
|
||||
2018-12-19 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c++/87504
|
||||
* c-common.h (warn_tautological_cmp): Convert 1st param from
|
||||
location_t to const op_location_t &.
|
||||
* c-warn.c (find_array_ref_with_const_idx_r): Call fold_for_warn
|
||||
when testing for INTEGER_CST.
|
||||
(warn_tautological_bitwise_comparison): Convert 1st param from
|
||||
location_t to const op_location_t &; use it to build a
|
||||
binary_op_rich_location, and use this.
|
||||
(spelled_the_same_p): New function.
|
||||
(warn_tautological_cmp): Convert 1st param from location_t to
|
||||
const op_location_t &. Warn for macro expansions if
|
||||
spelled_the_same_p. Use binary_op_rich_location.
|
||||
|
||||
2018-12-19 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c++/43064
|
||||
|
@ -1268,7 +1268,8 @@ extern void constant_expression_error (tree);
|
||||
extern void overflow_warning (location_t, tree, tree = NULL_TREE);
|
||||
extern void warn_logical_operator (location_t, enum tree_code, tree,
|
||||
enum tree_code, tree, enum tree_code, tree);
|
||||
extern void warn_tautological_cmp (location_t, enum tree_code, tree, tree);
|
||||
extern void warn_tautological_cmp (const op_location_t &, enum tree_code,
|
||||
tree, tree);
|
||||
extern void warn_logical_not_parentheses (location_t, enum tree_code, tree,
|
||||
tree);
|
||||
extern bool warn_if_unused_value (const_tree, location_t);
|
||||
|
@ -322,7 +322,8 @@ find_array_ref_with_const_idx_r (tree *expr_p, int *, void *)
|
||||
|
||||
if ((TREE_CODE (expr) == ARRAY_REF
|
||||
|| TREE_CODE (expr) == ARRAY_RANGE_REF)
|
||||
&& TREE_CODE (TREE_OPERAND (expr, 1)) == INTEGER_CST)
|
||||
&& (TREE_CODE (fold_for_warn (TREE_OPERAND (expr, 1)))
|
||||
== INTEGER_CST))
|
||||
return integer_type_node;
|
||||
|
||||
return NULL_TREE;
|
||||
@ -334,7 +335,7 @@ find_array_ref_with_const_idx_r (tree *expr_p, int *, void *)
|
||||
of this comparison. */
|
||||
|
||||
static void
|
||||
warn_tautological_bitwise_comparison (location_t loc, tree_code code,
|
||||
warn_tautological_bitwise_comparison (const op_location_t &loc, tree_code code,
|
||||
tree lhs, tree rhs)
|
||||
{
|
||||
if (code != EQ_EXPR && code != NE_EXPR)
|
||||
@ -389,29 +390,64 @@ warn_tautological_bitwise_comparison (location_t loc, tree_code code,
|
||||
if (res == cstw)
|
||||
return;
|
||||
|
||||
binary_op_rich_location richloc (loc, lhs, rhs, false);
|
||||
if (code == EQ_EXPR)
|
||||
warning_at (loc, OPT_Wtautological_compare,
|
||||
warning_at (&richloc, OPT_Wtautological_compare,
|
||||
"bitwise comparison always evaluates to false");
|
||||
else
|
||||
warning_at (loc, OPT_Wtautological_compare,
|
||||
warning_at (&richloc, OPT_Wtautological_compare,
|
||||
"bitwise comparison always evaluates to true");
|
||||
}
|
||||
|
||||
/* Given LOC_A and LOC_B from macro expansions, return true if
|
||||
they are "spelled the same" i.e. if they are both directly from
|
||||
expansion of the same non-function-like macro. */
|
||||
|
||||
static bool
|
||||
spelled_the_same_p (location_t loc_a, location_t loc_b)
|
||||
{
|
||||
gcc_assert (from_macro_expansion_at (loc_a));
|
||||
gcc_assert (from_macro_expansion_at (loc_b));
|
||||
|
||||
const line_map_macro *map_a
|
||||
= linemap_check_macro (linemap_lookup (line_table, loc_a));
|
||||
|
||||
const line_map_macro *map_b
|
||||
= linemap_check_macro (linemap_lookup (line_table, loc_b));
|
||||
|
||||
if (map_a->macro == map_b->macro)
|
||||
if (!cpp_fun_like_macro_p (map_a->macro))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Warn if a self-comparison always evaluates to true or false. LOC
|
||||
is the location of the comparison with code CODE, LHS and RHS are
|
||||
operands of the comparison. */
|
||||
|
||||
void
|
||||
warn_tautological_cmp (location_t loc, enum tree_code code, tree lhs, tree rhs)
|
||||
warn_tautological_cmp (const op_location_t &loc, enum tree_code code,
|
||||
tree lhs, tree rhs)
|
||||
{
|
||||
if (TREE_CODE_CLASS (code) != tcc_comparison)
|
||||
return;
|
||||
|
||||
/* Don't warn for various macro expansions. */
|
||||
if (from_macro_expansion_at (loc)
|
||||
|| from_macro_expansion_at (EXPR_LOCATION (lhs))
|
||||
|| from_macro_expansion_at (EXPR_LOCATION (rhs)))
|
||||
if (from_macro_expansion_at (loc))
|
||||
return;
|
||||
bool lhs_in_macro = from_macro_expansion_at (EXPR_LOCATION (lhs));
|
||||
bool rhs_in_macro = from_macro_expansion_at (EXPR_LOCATION (rhs));
|
||||
if (lhs_in_macro || rhs_in_macro)
|
||||
{
|
||||
/* Don't warn if exactly one is from a macro. */
|
||||
if (!(lhs_in_macro && rhs_in_macro))
|
||||
return;
|
||||
|
||||
/* If both are in a macro, only warn if they're spelled the same. */
|
||||
if (!spelled_the_same_p (EXPR_LOCATION (lhs), EXPR_LOCATION (rhs)))
|
||||
return;
|
||||
}
|
||||
|
||||
warn_tautological_bitwise_comparison (loc, code, lhs, rhs);
|
||||
|
||||
@ -446,11 +482,12 @@ warn_tautological_cmp (location_t loc, enum tree_code code, tree lhs, tree rhs)
|
||||
const bool always_true = (code == EQ_EXPR || code == LE_EXPR
|
||||
|| code == GE_EXPR || code == UNLE_EXPR
|
||||
|| code == UNGE_EXPR || code == UNEQ_EXPR);
|
||||
binary_op_rich_location richloc (loc, lhs, rhs, false);
|
||||
if (always_true)
|
||||
warning_at (loc, OPT_Wtautological_compare,
|
||||
warning_at (&richloc, OPT_Wtautological_compare,
|
||||
"self-comparison always evaluates to true");
|
||||
else
|
||||
warning_at (loc, OPT_Wtautological_compare,
|
||||
warning_at (&richloc, OPT_Wtautological_compare,
|
||||
"self-comparison always evaluates to false");
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,11 @@
|
||||
2018-12-19 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c++/87504
|
||||
* c-typeck.c (class maybe_range_label_for_tree_type_mismatch):
|
||||
Move from here to gcc-rich-location.h and gcc-rich-location.c.
|
||||
(build_binary_op): Use struct op_location_t and
|
||||
class binary_op_rich_location.
|
||||
|
||||
2018-12-11 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
PR sanitizer/88426
|
||||
|
@ -11313,38 +11313,6 @@ build_vec_cmp (tree_code code, tree type,
|
||||
return build3 (VEC_COND_EXPR, type, cmp, minus_one_vec, zero_vec);
|
||||
}
|
||||
|
||||
/* Subclass of range_label for labelling the type of EXPR when reporting
|
||||
a type mismatch between EXPR and OTHER_EXPR.
|
||||
Either or both of EXPR and OTHER_EXPR could be NULL. */
|
||||
|
||||
class maybe_range_label_for_tree_type_mismatch : public range_label
|
||||
{
|
||||
public:
|
||||
maybe_range_label_for_tree_type_mismatch (tree expr, tree other_expr)
|
||||
: m_expr (expr), m_other_expr (other_expr)
|
||||
{
|
||||
}
|
||||
|
||||
label_text get_text (unsigned range_idx) const FINAL OVERRIDE
|
||||
{
|
||||
if (m_expr == NULL_TREE
|
||||
|| !EXPR_P (m_expr))
|
||||
return label_text (NULL, false);
|
||||
tree expr_type = TREE_TYPE (m_expr);
|
||||
|
||||
tree other_type = NULL_TREE;
|
||||
if (m_other_expr && EXPR_P (m_other_expr))
|
||||
other_type = TREE_TYPE (m_other_expr);
|
||||
|
||||
range_label_for_type_mismatch inner (expr_type, other_type);
|
||||
return inner.get_text (range_idx);
|
||||
}
|
||||
|
||||
private:
|
||||
tree m_expr;
|
||||
tree m_other_expr;
|
||||
};
|
||||
|
||||
/* Build a binary-operation expression without default conversions.
|
||||
CODE is the kind of expression to build.
|
||||
LOCATION is the operator's location.
|
||||
@ -12475,12 +12443,9 @@ build_binary_op (location_t location, enum tree_code code,
|
||||
|
||||
if (!result_type)
|
||||
{
|
||||
gcc_rich_location richloc (location);
|
||||
maybe_range_label_for_tree_type_mismatch
|
||||
label_for_op0 (orig_op0, orig_op1),
|
||||
label_for_op1 (orig_op1, orig_op0);
|
||||
richloc.maybe_add_expr (orig_op0, &label_for_op0);
|
||||
richloc.maybe_add_expr (orig_op1, &label_for_op1);
|
||||
/* Favor showing any expression locations that are available. */
|
||||
op_location_t oploc (location, UNKNOWN_LOCATION);
|
||||
binary_op_rich_location richloc (oploc, orig_op0, orig_op1, true);
|
||||
binary_op_error (&richloc, code, TREE_TYPE (op0), TREE_TYPE (op1));
|
||||
return error_mark_node;
|
||||
}
|
||||
|
@ -1,3 +1,27 @@
|
||||
2018-12-19 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c++/87504
|
||||
* call.c (op_error): Convert 1st param from location_t to
|
||||
const op_location_t &. Use binary_op_rich_location for binary
|
||||
ops.
|
||||
(build_conditional_expr_1): Convert 1st param from location_t to
|
||||
const op_location_t &.
|
||||
(build_conditional_expr): Likewise.
|
||||
(build_new_op_1): Likewise.
|
||||
(build_new_op): Likewise.
|
||||
* cp-tree.h (build_conditional_expr): Likewise.
|
||||
(build_new_op): Likewise.
|
||||
(build_x_binary_op): Likewise.
|
||||
(cp_build_binary_op): Likewise.
|
||||
* parser.c (cp_parser_primary_expression): Build a location
|
||||
for id-expression nodes.
|
||||
(cp_parser_binary_expression): Use an op_location_t when
|
||||
calling build_x_binary_op.
|
||||
(cp_parser_operator): Build a location for user-defined literals.
|
||||
* typeck.c (build_x_binary_op): Convert 1st param from location_t
|
||||
to const op_location_t &.
|
||||
(cp_build_binary_op): Likewise. Use binary_op_rich_location.
|
||||
|
||||
2018-12-19 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c++/43064
|
||||
|
@ -166,8 +166,8 @@ static tree build_over_call (struct z_candidate *, int, tsubst_flags_t);
|
||||
/*c_cast_p=*/false, (COMPLAIN))
|
||||
static tree convert_like_real (conversion *, tree, tree, int, bool,
|
||||
bool, tsubst_flags_t);
|
||||
static void op_error (location_t, enum tree_code, enum tree_code, tree,
|
||||
tree, tree, bool);
|
||||
static void op_error (const op_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 *);
|
||||
@ -4713,7 +4713,8 @@ op_error_string (const char *errmsg, int ntypes, bool match)
|
||||
}
|
||||
|
||||
static void
|
||||
op_error (location_t loc, enum tree_code code, enum tree_code code2,
|
||||
op_error (const op_location_t &loc,
|
||||
enum tree_code code, enum tree_code code2,
|
||||
tree arg1, tree arg2, tree arg3, bool match)
|
||||
{
|
||||
bool assop = code == MODIFY_EXPR;
|
||||
@ -4767,8 +4768,12 @@ op_error (location_t loc, enum tree_code code, enum tree_code code2,
|
||||
default:
|
||||
if (arg2)
|
||||
if (flag_diagnostics_show_caret)
|
||||
error_at (loc, op_error_string (G_("%<operator%s%>"), 2, match),
|
||||
opname, TREE_TYPE (arg1), TREE_TYPE (arg2));
|
||||
{
|
||||
binary_op_rich_location richloc (loc, arg1, arg2, true);
|
||||
error_at (&richloc,
|
||||
op_error_string (G_("%<operator%s%>"), 2, match),
|
||||
opname, TREE_TYPE (arg1), TREE_TYPE (arg2));
|
||||
}
|
||||
else
|
||||
error_at (loc, op_error_string (G_("%<operator%s%> in %<%E %s %E%>"),
|
||||
2, match),
|
||||
@ -4867,7 +4872,8 @@ conditional_conversion (tree e1, tree e2, tsubst_flags_t complain)
|
||||
arguments to the conditional expression. */
|
||||
|
||||
static tree
|
||||
build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
|
||||
build_conditional_expr_1 (const op_location_t &loc,
|
||||
tree arg1, tree arg2, tree arg3,
|
||||
tsubst_flags_t complain)
|
||||
{
|
||||
tree arg2_type;
|
||||
@ -5461,7 +5467,8 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
|
||||
/* Wrapper for above. */
|
||||
|
||||
tree
|
||||
build_conditional_expr (location_t loc, tree arg1, tree arg2, tree arg3,
|
||||
build_conditional_expr (const op_location_t &loc,
|
||||
tree arg1, tree arg2, tree arg3,
|
||||
tsubst_flags_t complain)
|
||||
{
|
||||
tree ret;
|
||||
@ -5650,8 +5657,9 @@ op_is_ordered (tree_code code)
|
||||
}
|
||||
|
||||
static tree
|
||||
build_new_op_1 (location_t loc, enum tree_code code, int flags, tree arg1,
|
||||
tree arg2, tree arg3, tree *overload, tsubst_flags_t complain)
|
||||
build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
|
||||
tree arg1, tree arg2, tree arg3, tree *overload,
|
||||
tsubst_flags_t complain)
|
||||
{
|
||||
struct z_candidate *candidates = 0, *cand;
|
||||
vec<tree, va_gc> *arglist;
|
||||
@ -6130,7 +6138,7 @@ build_new_op_1 (location_t loc, enum tree_code code, int flags, tree arg1,
|
||||
/* Wrapper for above. */
|
||||
|
||||
tree
|
||||
build_new_op (location_t loc, enum tree_code code, int flags,
|
||||
build_new_op (const op_location_t &loc, enum tree_code code, int flags,
|
||||
tree arg1, tree arg2, tree arg3,
|
||||
tree *overload, tsubst_flags_t complain)
|
||||
{
|
||||
|
@ -6097,7 +6097,8 @@ extern int raw_dump_id;
|
||||
extern bool check_dtor_name (tree, tree);
|
||||
int magic_varargs_p (tree);
|
||||
|
||||
extern tree build_conditional_expr (location_t, tree, tree, tree,
|
||||
extern tree build_conditional_expr (const op_location_t &,
|
||||
tree, tree, tree,
|
||||
tsubst_flags_t);
|
||||
extern tree build_addr_func (tree, tsubst_flags_t);
|
||||
extern void set_flags_from_callee (tree);
|
||||
@ -6122,7 +6123,8 @@ extern tree build_new_method_call (tree, tree,
|
||||
extern tree build_special_member_call (tree, tree,
|
||||
vec<tree, va_gc> **,
|
||||
tree, int, tsubst_flags_t);
|
||||
extern tree build_new_op (location_t, enum tree_code,
|
||||
extern tree build_new_op (const op_location_t &,
|
||||
enum tree_code,
|
||||
int, tree, tree, tree, tree *,
|
||||
tsubst_flags_t);
|
||||
extern tree build_op_call (tree, vec<tree, va_gc> **,
|
||||
@ -7339,7 +7341,7 @@ 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);
|
||||
extern tree build_x_binary_op (location_t,
|
||||
extern tree build_x_binary_op (const op_location_t &,
|
||||
enum tree_code, tree,
|
||||
enum tree_code, tree,
|
||||
enum tree_code, tree *,
|
||||
@ -7406,7 +7408,7 @@ extern tree composite_pointer_type (tree, tree, tree, tree,
|
||||
extern tree merge_types (tree, tree);
|
||||
extern tree strip_array_domain (tree);
|
||||
extern tree check_return_expr (tree, bool *);
|
||||
extern tree cp_build_binary_op (location_t,
|
||||
extern tree cp_build_binary_op (const op_location_t &,
|
||||
enum tree_code, tree, tree,
|
||||
tsubst_flags_t);
|
||||
extern tree build_x_vec_perm_expr (location_t,
|
||||
|
@ -5710,8 +5710,21 @@ cp_parser_primary_expression (cp_parser *parser,
|
||||
id_expression.get_location ()));
|
||||
if (error_msg)
|
||||
cp_parser_error (parser, error_msg);
|
||||
decl.set_location (id_expression.get_location ());
|
||||
decl.set_range (id_expr_token->location, id_expression.get_finish ());
|
||||
/* Build a location for an id-expression of the form:
|
||||
::ns::id
|
||||
~~~~~~^~
|
||||
or:
|
||||
id
|
||||
^~
|
||||
i.e. from the start of the first token to the end of the final
|
||||
token, with the caret at the start of the unqualified-id. */
|
||||
location_t caret_loc = get_pure_location (id_expression.get_location ());
|
||||
location_t start_loc = get_start (id_expr_token->location);
|
||||
location_t finish_loc = get_finish (id_expression.get_location ());
|
||||
location_t combined_loc
|
||||
= make_location (caret_loc, start_loc, finish_loc);
|
||||
|
||||
decl.set_location (combined_loc);
|
||||
return decl;
|
||||
}
|
||||
|
||||
@ -9556,7 +9569,8 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
|
||||
}
|
||||
else
|
||||
{
|
||||
current.lhs = build_x_binary_op (combined_loc, current.tree_type,
|
||||
op_location_t op_loc (current.loc, combined_loc);
|
||||
current.lhs = build_x_binary_op (op_loc, current.tree_type,
|
||||
current.lhs, current.lhs_type,
|
||||
rhs, rhs_type, &overload,
|
||||
complain_flags (decltype_p));
|
||||
@ -15391,8 +15405,16 @@ cp_parser_operator (cp_parser* parser, location_t start_loc)
|
||||
const char *name = IDENTIFIER_POINTER (id);
|
||||
id = cp_literal_operator_id (name);
|
||||
}
|
||||
start_loc = make_location (start_loc, start_loc, get_finish (end_loc));
|
||||
return cp_expr (id, start_loc);
|
||||
/* Generate a location of the form:
|
||||
"" _suffix_identifier
|
||||
^~~~~~~~~~~~~~~~~~~~~
|
||||
with caret == start at the start token, finish at the end of the
|
||||
suffix identifier. */
|
||||
location_t finish_loc
|
||||
= get_finish (cp_lexer_previous_token (parser->lexer)->location);
|
||||
location_t combined_loc
|
||||
= make_location (start_loc, start_loc, finish_loc);
|
||||
return cp_expr (id, combined_loc);
|
||||
}
|
||||
|
||||
default:
|
||||
|
@ -4128,7 +4128,7 @@ convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl,
|
||||
ARG2_CODE as ERROR_MARK. */
|
||||
|
||||
tree
|
||||
build_x_binary_op (location_t loc, enum tree_code code, tree arg1,
|
||||
build_x_binary_op (const op_location_t &loc, enum tree_code code, tree arg1,
|
||||
enum tree_code arg1_code, tree arg2,
|
||||
enum tree_code arg2_code, tree *overload_p,
|
||||
tsubst_flags_t complain)
|
||||
@ -4317,7 +4317,7 @@ warn_for_null_address (location_t location, tree op, tsubst_flags_t complain)
|
||||
multiple inheritance, and deal with pointer to member functions. */
|
||||
|
||||
tree
|
||||
cp_build_binary_op (location_t location,
|
||||
cp_build_binary_op (const op_location_t &location,
|
||||
enum tree_code code, tree orig_op0, tree orig_op1,
|
||||
tsubst_flags_t complain)
|
||||
{
|
||||
@ -5314,9 +5314,13 @@ cp_build_binary_op (location_t location,
|
||||
if (!result_type)
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error_at (location,
|
||||
"invalid operands of types %qT and %qT to binary %qO",
|
||||
TREE_TYPE (orig_op0), TREE_TYPE (orig_op1), code);
|
||||
{
|
||||
binary_op_rich_location richloc (location,
|
||||
orig_op0, orig_op1, true);
|
||||
error_at (&richloc,
|
||||
"invalid operands of types %qT and %qT to binary %qO",
|
||||
TREE_TYPE (orig_op0), TREE_TYPE (orig_op1), code);
|
||||
}
|
||||
return error_mark_node;
|
||||
}
|
||||
|
||||
|
@ -182,3 +182,92 @@ gcc_rich_location::add_fixit_insert_formatted (const char *content,
|
||||
else
|
||||
add_fixit_insert_before (insertion_point, content);
|
||||
}
|
||||
|
||||
/* Implementation of range_label::get_text for
|
||||
maybe_range_label_for_tree_type_mismatch.
|
||||
|
||||
If both expressions are non-NULL, then generate text describing
|
||||
the first expression's type (using the other expression's type
|
||||
for comparison, analogous to %H and %I in the C++ frontend, but
|
||||
on expressions rather than types). */
|
||||
|
||||
label_text
|
||||
maybe_range_label_for_tree_type_mismatch::get_text (unsigned range_idx) const
|
||||
{
|
||||
if (m_expr == NULL_TREE
|
||||
|| !EXPR_P (m_expr))
|
||||
return label_text (NULL, false);
|
||||
tree expr_type = TREE_TYPE (m_expr);
|
||||
|
||||
tree other_type = NULL_TREE;
|
||||
if (m_other_expr && EXPR_P (m_other_expr))
|
||||
other_type = TREE_TYPE (m_other_expr);
|
||||
|
||||
range_label_for_type_mismatch inner (expr_type, other_type);
|
||||
return inner.get_text (range_idx);
|
||||
}
|
||||
|
||||
/* binary_op_rich_location's ctor.
|
||||
|
||||
If use_operator_loc_p (LOC, ARG0, ARG1), then attempt to make a 3-location
|
||||
rich_location of the form:
|
||||
|
||||
arg_0 op arg_1
|
||||
~~~~~ ^~ ~~~~~
|
||||
| |
|
||||
| arg1 type
|
||||
arg0 type
|
||||
|
||||
labelling the types of the arguments if SHOW_TYPES is true.
|
||||
|
||||
Otherwise, make a 1-location rich_location using the compound
|
||||
location within LOC:
|
||||
|
||||
arg_0 op arg_1
|
||||
~~~~~~^~~~~~~~
|
||||
|
||||
for which we can't label the types. */
|
||||
|
||||
binary_op_rich_location::binary_op_rich_location (const op_location_t &loc,
|
||||
tree arg0, tree arg1,
|
||||
bool show_types)
|
||||
: gcc_rich_location (loc.m_combined_loc),
|
||||
m_label_for_arg0 (arg0, arg1),
|
||||
m_label_for_arg1 (arg1, arg0)
|
||||
{
|
||||
/* Default (above) to using the combined loc.
|
||||
Potentially override it here: if we have location information for the
|
||||
operator and for both arguments, then split them all out.
|
||||
Alternatively, override it if we don't have the combined location. */
|
||||
if (use_operator_loc_p (loc, arg0, arg1))
|
||||
{
|
||||
set_range (0, loc.m_operator_loc, SHOW_RANGE_WITH_CARET);
|
||||
maybe_add_expr (arg0, show_types ? &m_label_for_arg0 : NULL);
|
||||
maybe_add_expr (arg1, show_types ? &m_label_for_arg1 : NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if binary_op_rich_location's ctor should attempt to make
|
||||
a 3-location rich_location (the location of the operator and of
|
||||
the 2 arguments), or fall back to a 1-location rich_location showing
|
||||
just the combined location of the operation as a whole. */
|
||||
|
||||
bool
|
||||
binary_op_rich_location::use_operator_loc_p (const op_location_t &loc,
|
||||
tree arg0, tree arg1)
|
||||
{
|
||||
/* If we don't have a combined location, then use the operator location,
|
||||
and try to add ranges for the operators. */
|
||||
if (loc.m_combined_loc == UNKNOWN_LOCATION)
|
||||
return true;
|
||||
|
||||
/* If we don't have the operator location, then use the
|
||||
combined location. */
|
||||
if (loc.m_operator_loc == UNKNOWN_LOCATION)
|
||||
return false;
|
||||
|
||||
/* We have both operator location and combined location: only use the
|
||||
operator location if we have locations for both arguments. */
|
||||
return (EXPR_HAS_LOCATION (arg0)
|
||||
&& EXPR_HAS_LOCATION (arg1));
|
||||
}
|
||||
|
@ -162,4 +162,61 @@ class range_label_for_type_mismatch : public range_label
|
||||
tree m_other_type;
|
||||
};
|
||||
|
||||
/* Subclass of range_label for labelling the type of EXPR when reporting
|
||||
a type mismatch between EXPR and OTHER_EXPR.
|
||||
Either or both of EXPR and OTHER_EXPR could be NULL. */
|
||||
|
||||
class maybe_range_label_for_tree_type_mismatch : public range_label
|
||||
{
|
||||
public:
|
||||
maybe_range_label_for_tree_type_mismatch (tree expr, tree other_expr)
|
||||
: m_expr (expr), m_other_expr (other_expr)
|
||||
{
|
||||
}
|
||||
|
||||
label_text get_text (unsigned range_idx) const FINAL OVERRIDE;
|
||||
|
||||
private:
|
||||
tree m_expr;
|
||||
tree m_other_expr;
|
||||
};
|
||||
|
||||
struct op_location_t;
|
||||
|
||||
/* A subclass of rich_location for showing problems with binary operations.
|
||||
|
||||
If enough location information is available, the ctor will make a
|
||||
3-location rich_location of the form:
|
||||
|
||||
arg_0 op arg_1
|
||||
~~~~~ ^~ ~~~~~
|
||||
| |
|
||||
| arg1 type
|
||||
arg0 type
|
||||
|
||||
labelling the types of the arguments if SHOW_TYPES is true.
|
||||
|
||||
Otherwise, it will fall back to a 1-location rich_location using the
|
||||
compound location within LOC:
|
||||
|
||||
arg_0 op arg_1
|
||||
~~~~~~^~~~~~~~
|
||||
|
||||
for which we can't label the types. */
|
||||
|
||||
class binary_op_rich_location : public gcc_rich_location
|
||||
{
|
||||
public:
|
||||
binary_op_rich_location (const op_location_t &loc,
|
||||
tree arg0, tree arg1,
|
||||
bool show_types);
|
||||
|
||||
private:
|
||||
static bool use_operator_loc_p (const op_location_t &loc,
|
||||
tree arg0, tree arg1);
|
||||
|
||||
maybe_range_label_for_tree_type_mismatch m_label_for_arg0;
|
||||
maybe_range_label_for_tree_type_mismatch m_label_for_arg1;
|
||||
};
|
||||
|
||||
#endif /* GCC_RICH_LOCATION_H */
|
||||
|
@ -1,3 +1,13 @@
|
||||
2018-12-19 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* c-c++-common/Wtautological-compare-ranges.c: New test.
|
||||
* g++.dg/cpp0x/pr51420.C: Add -fdiagnostics-show-caret and update
|
||||
expected output.
|
||||
* g++.dg/diagnostic/bad-binary-ops.C: Update expected output from
|
||||
1-location form to 3-location form, with labelling of ranges with
|
||||
types. Add examples of id-expression nodes with namespaces.
|
||||
* g++.dg/diagnostic/param-type-mismatch-2.C: Likewise.
|
||||
|
||||
2018-12-19 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c++/43064
|
||||
|
42
gcc/testsuite/c-c++-common/Wtautological-compare-ranges.c
Normal file
42
gcc/testsuite/c-c++-common/Wtautological-compare-ranges.c
Normal file
@ -0,0 +1,42 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-Wtautological-compare -fdiagnostics-show-caret" } */
|
||||
|
||||
#define FOO foo
|
||||
|
||||
void
|
||||
fn1 (int foo)
|
||||
{
|
||||
if (foo == foo); /* { dg-warning "self-comparison always evaluates to true" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
if (foo == foo);
|
||||
^~
|
||||
{ dg-end-multiline-output "" { target c } } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
if (foo == foo);
|
||||
~~~ ^~ ~~~
|
||||
{ dg-end-multiline-output "" { target c++ } } */
|
||||
}
|
||||
|
||||
void
|
||||
fn2 (int foo)
|
||||
{
|
||||
if (FOO == FOO); /* { dg-warning "self-comparison always evaluates to true" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
if (FOO == FOO);
|
||||
^~
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
void
|
||||
fn3 (int foo)
|
||||
{
|
||||
if ((foo & 16) == 10); /* { dg-warning "bitwise comparison always evaluates to false" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
if ((foo & 16) == 10);
|
||||
^~
|
||||
{ dg-end-multiline-output "" { target c } } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
if ((foo & 16) == 10);
|
||||
~~~~~~~~~~ ^~ ~~
|
||||
{ dg-end-multiline-output "" { target c++ } } */
|
||||
}
|
@ -1,8 +1,18 @@
|
||||
// { dg-do compile { target c++11 } }
|
||||
// { dg-options "-fdiagnostics-show-caret" }
|
||||
|
||||
void
|
||||
foo()
|
||||
{
|
||||
float x = operator"" _F(); // { dg-error "13:'operator\"\"_F' was not declared in this scope" }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
float x = operator"" _F();
|
||||
^~~~~~~~~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
float y = 0_F; // { dg-error "unable to find numeric literal operator" }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
float y = 0_F;
|
||||
^~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
@ -11,7 +11,10 @@ void test_1 ()
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
myvec[1] / ptr;
|
||||
~~~~~~~~~^~~~~
|
||||
~~~~~~~~ ^ ~~~
|
||||
| |
|
||||
| const int*
|
||||
__m128 {aka float}
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
@ -28,8 +31,12 @@ int test_2 (void)
|
||||
/* { dg-begin-multiline-output "" }
|
||||
return (some_function ()
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
|
||||
s
|
||||
+ some_other_function ());
|
||||
^~~~~~~~~~~~~~~~~~~~~~~~
|
||||
^ ~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
||||
t
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
@ -37,8 +44,54 @@ int test_3 (struct s param_s, struct t param_t)
|
||||
{
|
||||
return param_s && param_t; // { dg-error "no match for .operator" }
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
return param_s && param_t;
|
||||
~~~~~~~ ^~ ~~~~~~~
|
||||
| |
|
||||
s t
|
||||
{ dg-end-multiline-output "" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
return param_s && param_t;
|
||||
~~~~~~~~^~~~~~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
namespace ns_4
|
||||
{
|
||||
struct s foo;
|
||||
namespace inner {
|
||||
struct t bar;
|
||||
};
|
||||
};
|
||||
|
||||
int test_4a (void)
|
||||
{
|
||||
return ns_4::foo && ns_4::inner::bar; // { dg-error "no match for .operator" }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
return ns_4::foo && ns_4::inner::bar;
|
||||
~~~~~~~~~ ^~ ~~~~~~~~~~~~~~~~
|
||||
| |
|
||||
s t
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
return ns_4::foo && ns_4::inner::bar;
|
||||
~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
int test_4b (void)
|
||||
{
|
||||
return ::ns_4::foo && ns_4::inner::bar; // { dg-error "no match for .operator" }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
return ::ns_4::foo && ns_4::inner::bar;
|
||||
~~~~~~~~~~~ ^~ ~~~~~~~~~~~~~~~~
|
||||
| |
|
||||
s t
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
return ::ns_4::foo && ns_4::inner::bar;
|
||||
~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
@ -204,7 +204,9 @@ int test_10 ()
|
||||
return v10_a - v10_b; // { dg-error "no match for" }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
return v10_a - v10_b;
|
||||
~~~~~~^~~~~~~
|
||||
~~~~~ ^ ~~~~~
|
||||
| |
|
||||
s10 s10
|
||||
{ dg-end-multiline-output "" } */
|
||||
// { dg-message "candidate" "" { target *-*-* } s10_operator }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
|
49
gcc/tree.h
49
gcc/tree.h
@ -5962,4 +5962,53 @@ fndecl_built_in_p (const_tree node, built_in_function name)
|
||||
&& DECL_FUNCTION_CODE (node) == name);
|
||||
}
|
||||
|
||||
/* A struct for encapsulating location information about an operator
|
||||
and the operation built from it.
|
||||
|
||||
m_operator_loc is the location of the operator
|
||||
m_combined_loc is the location of the compound expression.
|
||||
|
||||
For example, given "a && b" the, operator location is:
|
||||
a && b
|
||||
^~
|
||||
and the combined location is:
|
||||
a && b
|
||||
~~^~~~
|
||||
Capturing this information allows for class binary_op_rich_location
|
||||
to provide detailed information about e.g. type mismatches in binary
|
||||
operations where enough location information is available:
|
||||
|
||||
arg_0 op arg_1
|
||||
~~~~~ ^~ ~~~~~
|
||||
| |
|
||||
| arg1 type
|
||||
arg0 type
|
||||
|
||||
falling back to just showing the combined location:
|
||||
|
||||
arg_0 op arg_1
|
||||
~~~~~~^~~~~~~~
|
||||
|
||||
where it is not. */
|
||||
|
||||
struct op_location_t
|
||||
{
|
||||
location_t m_operator_loc;
|
||||
location_t m_combined_loc;
|
||||
|
||||
/* 1-argument ctor, for constructing from a combined location. */
|
||||
op_location_t (location_t combined_loc)
|
||||
: m_operator_loc (UNKNOWN_LOCATION), m_combined_loc (combined_loc)
|
||||
{}
|
||||
|
||||
/* 2-argument ctor, for distinguishing between the operator's location
|
||||
and the combined location. */
|
||||
op_location_t (location_t operator_loc, location_t combined_loc)
|
||||
: m_operator_loc (operator_loc), m_combined_loc (combined_loc)
|
||||
{}
|
||||
|
||||
/* Implicitly convert back to a location_t, using the combined location. */
|
||||
operator location_t () const { return m_combined_loc; }
|
||||
};
|
||||
|
||||
#endif /* GCC_TREE_H */
|
||||
|
Loading…
Reference in New Issue
Block a user