Ping re this patch:
"[PATCH 2/2] v2: C++: improvements to binary operator diagnostics (PR
c++/87504)"
https://gcc.gnu.org/ml/gcc-patches/2018-12/msg00236.html
(...which is dependent on:
"[PATCH 1/2] v3: C++: more location wrapper nodes (PR c++/43064, PR
c++/43486)"
https://gcc.gnu.org/ml/gcc-patches/2018-12/msg00500.html )
Thanks
Dave
On Tue, 2018-12-04 at 17:35 -0500, David Malcolm wrote:
> The v1 patch:
> https://gcc.gnu.org/ml/gcc-patches/2018-11/msg00303.html
> has bitrotten somewhat, so here's v2 of the patch, updated relative
> to r266740.
>
> Blurb from v1 patch follows:
>
> 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 from 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);
> | ^~
>
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu, in
> conjunction with the previous patch.
>
> OK for trunk?
> Dave
>
> 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): Strip location
> wrapper 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.
>
> This is the 2nd commit message:
>
> FIXME: column and multiline fixes to * g++.dg/cpp0x/pr51420.C
> ---
> gcc/c-family/c-common.h | 3 +-
> gcc/c-family/c-warn.c | 57 +++++++++++-
> --
> gcc/c/c-typeck.c | 41 +---------
> gcc/cp/call.c | 28 ++++---
> gcc/cp/cp-tree.h | 10 ++-
> gcc/cp/parser.c | 32 ++++++--
> gcc/cp/typeck.c | 14 ++--
> gcc/gcc-rich-location.c | 89
> ++++++++++++++++++++++
> gcc/gcc-rich-location.h | 57
> ++++++++++++++
> .../c-c++-common/Wtautological-compare-ranges.c | 42 ++++++++++
> gcc/testsuite/g++.dg/cpp0x/pr51420.C | 10 +++
> gcc/testsuite/g++.dg/diagnostic/bad-binary-ops.C | 57
> +++++++++++++-
> .../g++.dg/diagnostic/param-type-mismatch-2.C | 4 +-
> gcc/tree.h | 49 ++++++++++++
> 14 files changed, 417 insertions(+), 76 deletions(-)
> create mode 100644 gcc/testsuite/c-c++-common/Wtautological-compare-
> ranges.c
>
> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> index 4187343..0b9ddf6 100644
> --- a/gcc/c-family/c-common.h
> +++ b/gcc/c-family/c-common.h
> @@ -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);
> diff --git a/gcc/c-family/c-warn.c b/gcc/c-family/c-warn.c
> index fc7f87c..fce9d84 100644
> --- a/gcc/c-family/c-warn.c
> +++ b/gcc/c-family/c-warn.c
> @@ -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 (tree_strip_any_location_wrapper (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");
> }
> }
> diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
> index 8fbecfc..33aad1c 100644
> --- a/gcc/c/c-typeck.c
> +++ b/gcc/c/c-typeck.c
> @@ -11310,38 +11310,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.
> @@ -12472,12 +12440,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;
> }
> diff --git a/gcc/cp/call.c b/gcc/cp/call.c
> index 6dd8744..ca8799d 100644
> --- a/gcc/cp/call.c
> +++ b/gcc/cp/call.c
> @@ -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_flag
> s_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)
> {
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 111a123..e80334a 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -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> **,
> @@ -7338,7 +7340,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 *,
> @@ -7405,7 +7407,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,
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index 4acc79d..321ff39 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -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;
> }
>
> @@ -9547,7 +9560,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));
> @@ -15382,8 +15396,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:
> diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
> index 820cfc5..a2f75c8 100644
> --- a/gcc/cp/typeck.c
> +++ b/gcc/cp/typeck.c
> @@ -4114,7 +4114,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)
> @@ -4303,7 +4303,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)
> {
> @@ -5300,9 +5300,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;
> }
>
> diff --git a/gcc/gcc-rich-location.c b/gcc/gcc-rich-location.c
> index 81beb61..25a604f 100644
> --- a/gcc/gcc-rich-location.c
> +++ b/gcc/gcc-rich-location.c
> @@ -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));
> +}
> diff --git a/gcc/gcc-rich-location.h b/gcc/gcc-rich-location.h
> index 200bbb5..202d4f4 100644
> --- a/gcc/gcc-rich-location.h
> +++ b/gcc/gcc-rich-location.h
> @@ -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 */
> diff --git a/gcc/testsuite/c-c++-common/Wtautological-compare-
> ranges.c b/gcc/testsuite/c-c++-common/Wtautological-compare-ranges.c
> new file mode 100644
> index 0000000..2634d27
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wtautological-compare-ranges.c
> @@ -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++ } } */
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp0x/pr51420.C
> b/gcc/testsuite/g++.dg/cpp0x/pr51420.C
> index fc70d46..1612cef 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/pr51420.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/pr51420.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 "" } */
> }
> diff --git a/gcc/testsuite/g++.dg/diagnostic/bad-binary-ops.C
> b/gcc/testsuite/g++.dg/diagnostic/bad-binary-ops.C
> index 4ab7656..fab5849 100644
> --- a/gcc/testsuite/g++.dg/diagnostic/bad-binary-ops.C
> +++ b/gcc/testsuite/g++.dg/diagnostic/bad-binary-ops.C
> @@ -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 "" } */
> }
>
> @@ -39,6 +46,52 @@ int test_3 (struct s param_s, struct t param_t)
>
> /* { 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 "" } */
> +}
> diff --git a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
> b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
> index b19655d..de7570a 100644
> --- a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
> +++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
> @@ -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 "" }
> diff --git a/gcc/tree.h b/gcc/tree.h
> index bf685ed..5527ef1 100644
> --- a/gcc/tree.h
> +++ b/gcc/tree.h
> @@ -5959,4 +5959,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 */