From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 20979 invoked by alias); 5 Nov 2018 19:44:25 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Received: (qmail 20970 invoked by uid 89); 5 Nov 2018 19:44:24 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=expansions, Ux, Possible, analogous X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 05 Nov 2018 19:44:20 +0000 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 8D28E30820EE for ; Mon, 5 Nov 2018 19:44:19 +0000 (UTC) Received: from c64.redhat.com (ovpn-112-19.phx2.redhat.com [10.3.112.19]) by smtp.corp.redhat.com (Postfix) with ESMTP id 826BA611CE; Mon, 5 Nov 2018 19:44:18 +0000 (UTC) From: David Malcolm To: gcc-patches@gcc.gnu.org, jason@redhat.com Cc: David Malcolm Subject: [PATCH 2/2] C++: improvements to binary operator diagnostics (PR c++/87504) Date: Mon, 05 Nov 2018 19:44:00 -0000 Message-Id: <1541449869-59851-2-git-send-email-dmalcolm@redhat.com> In-Reply-To: <1541449869-59851-1-git-send-email-dmalcolm@redhat.com> References: <1541449869-59851-1-git-send-email-dmalcolm@redhat.com> X-IsSubscribed: yes X-SW-Source: 2018-11/txt/msg00303.txt.bz2 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)' 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/cpp0x/udlit-declare-neg.C: Update expected columns in output. * g++.dg/cpp0x/udlit-member-neg.C: Likewise. * 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. --- 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 | 31 +++++++- 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 | 12 ++- gcc/testsuite/g++.dg/cpp0x/udlit-declare-neg.C | 8 +- gcc/testsuite/g++.dg/cpp0x/udlit-member-neg.C | 2 +- gcc/testsuite/g++.dg/diagnostic/bad-binary-ops.C | 57 +++++++++++++- .../g++.dg/diagnostic/param-type-mismatch-2.C | 4 +- gcc/tree.h | 49 ++++++++++++ 16 files changed, 424 insertions(+), 80 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 641fe57..1e7ebb9 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -1263,7 +1263,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 f2ed711..765b46d 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 9d09b8d..76e533e 100644 --- a/gcc/c/c-typeck.c +++ b/gcc/c/c-typeck.c @@ -11044,38 +11044,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. @@ -12206,12 +12174,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 d366d35..145ba8f 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_flags_t); static void print_z_candidate (location_t, const char *, struct z_candidate *); @@ -4707,7 +4707,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; @@ -4761,8 +4762,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_("%"), 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_("%"), 2, match), + opname, TREE_TYPE (arg1), TREE_TYPE (arg2)); + } else error_at (loc, op_error_string (G_("% in %<%E %s %E%>"), 2, match), @@ -4861,7 +4866,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; @@ -5455,7 +5461,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; @@ -5644,8 +5651,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 *arglist; @@ -6124,7 +6132,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 26ded3a..8bd8078 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6046,7 +6046,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); @@ -6071,7 +6072,8 @@ extern tree build_new_method_call (tree, tree, extern tree build_special_member_call (tree, tree, vec **, 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 **, @@ -7273,7 +7275,7 @@ extern tree cp_build_function_call_nary (tree, tsubst_flags_t, ...) ATTRIBUTE_SENTINEL; extern tree cp_build_function_call_vec (tree, vec **, 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 *, @@ -7340,7 +7342,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 b3876e2..0103fae 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -5661,7 +5661,22 @@ cp_parser_primary_expression (cp_parser *parser, id_expression.get_location ())); if (error_msg) cp_parser_error (parser, error_msg); - decl.set_location (id_expr_token->location); + + /* 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; } @@ -9371,7 +9386,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)); @@ -15087,7 +15103,16 @@ cp_parser_operator (cp_parser* parser) const char *name = IDENTIFIER_POINTER (id); id = cp_literal_operator_id (name); } - return id; + /* 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 b97d3da..e62fee6 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -4109,7 +4109,7 @@ convert_arguments (tree typelist, vec **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) @@ -4298,7 +4298,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) { @@ -5295,9 +5295,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 d282fd4..db35400 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..76cf77b 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" } + float x = operator"" _F(); // { dg-error "21:'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/cpp0x/udlit-declare-neg.C b/gcc/testsuite/g++.dg/cpp0x/udlit-declare-neg.C index 721f87d..8458c1c 100644 --- a/gcc/testsuite/g++.dg/cpp0x/udlit-declare-neg.C +++ b/gcc/testsuite/g++.dg/cpp0x/udlit-declare-neg.C @@ -2,14 +2,14 @@ // Check that undeclared literal operator calls and literals give appropriate errors. -int i = operator"" _Bar('x'); // { dg-error "9:'operator\"\"_Bar' was not declared in this scope" } +int i = operator"" _Bar('x'); // { dg-error "17:'operator\"\"_Bar' was not declared in this scope" } int j = 'x'_Bar; // { dg-error "unable to find character literal operator|with|argument" } -int ii = operator"" _BarCharStr("Howdy, Pardner!"); // { dg-error "10:'operator\"\"_BarCharStr' was not declared in this scope" } +int ii = operator"" _BarCharStr("Howdy, Pardner!"); // { dg-error "18:'operator\"\"_BarCharStr' was not declared in this scope" } int jj = "Howdy, Pardner!"_BarCharStr; // { dg-error "unable to find string literal operator|Possible missing length argument" } -unsigned long long iULL = operator"" _BarULL(666ULL); // { dg-error "27:'operator\"\"_BarULL' was not declared in this scope" } +unsigned long long iULL = operator"" _BarULL(666ULL); // { dg-error "35:'operator\"\"_BarULL' was not declared in this scope" } unsigned long long jULL = 666_BarULL; // { dg-error "unable to find numeric literal operator" } -long double iLD = operator"" _BarLD(666.0L); // { dg-error "19:'operator\"\"_BarLD' was not declared in this scope" } +long double iLD = operator"" _BarLD(666.0L); // { dg-error "27:'operator\"\"_BarLD' was not declared in this scope" } long double jLD = 666.0_BarLD; // { dg-error "unable to find numeric literal operator" } diff --git a/gcc/testsuite/g++.dg/cpp0x/udlit-member-neg.C b/gcc/testsuite/g++.dg/cpp0x/udlit-member-neg.C index f2eef0f..4d446f0 100644 --- a/gcc/testsuite/g++.dg/cpp0x/udlit-member-neg.C +++ b/gcc/testsuite/g++.dg/cpp0x/udlit-member-neg.C @@ -7,7 +7,7 @@ public: int operator"" _Bar(char32_t); // { dg-error "7:.int Foo::operator\"\"_Bar\\(char32_t\\). must be a non-member function" } }; -int i = operator"" _Bar(U'x'); // { dg-error "9:'operator\"\"_Bar' was not declared in this scope" } +int i = operator"" _Bar(U'x'); // { dg-error "17:'operator\"\"_Bar' was not declared in this scope" } int j = U'x'_Bar; // { dg-error "unable to find character literal operator" } int 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 f74f8d3..5f3c154 100644 --- a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C +++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C @@ -208,7 +208,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 311caa1..3b0772c 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -5915,4 +5915,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 */ -- 1.8.5.3