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:
David Malcolm 2018-12-19 15:15:42 +00:00 committed by David Malcolm
parent dfd7fdca2a
commit a14feb3c78
19 changed files with 489 additions and 76 deletions

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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");
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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)
{

View File

@ -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,

View File

@ -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:

View File

@ -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;
}

View File

@ -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));
}

View File

@ -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 */

View File

@ -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

View 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++ } } */
}

View File

@ -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 "" } */
}

View File

@ -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 "" } */
}

View File

@ -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 "" }

View File

@ -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 */