From a14feb3c783fba6af8d66b8138214a3a313be5c5 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Wed, 19 Dec 2018 15:15:42 +0000 Subject: [PATCH] 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)' 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 --- gcc/ChangeLog | 15 ++++ gcc/c-family/ChangeLog | 15 ++++ gcc/c-family/c-common.h | 3 +- gcc/c-family/c-warn.c | 57 +++++++++--- gcc/c/ChangeLog | 8 ++ gcc/c/c-typeck.c | 41 +-------- gcc/cp/ChangeLog | 24 +++++ 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 ++++++++++++ gcc/testsuite/ChangeLog | 10 +++ .../Wtautological-compare-ranges.c | 42 +++++++++ gcc/testsuite/g++.dg/cpp0x/pr51420.C | 10 +++ .../g++.dg/diagnostic/bad-binary-ops.C | 57 +++++++++++- .../g++.dg/diagnostic/param-type-mismatch-2.C | 4 +- gcc/tree.h | 49 ++++++++++ 19 files changed, 489 insertions(+), 76 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/Wtautological-compare-ranges.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 4784244002b..f2118d8de8d 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,18 @@ +2018-12-19 David Malcolm + + 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 PR c++/43064 diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index eb148bb25e7..637a288e2ac 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,18 @@ +2018-12-19 David Malcolm + + 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 PR c++/43064 diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 4187343c0b3..0b9ddf68fe0 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 4c0bdf96d4c..b0f6da0e52c 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 (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"); } } diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index 294442cd41b..3232614f430 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,11 @@ +2018-12-19 David Malcolm + + 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 PR sanitizer/88426 diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c index 1a897273088..72ecd46cea6 100644 --- a/gcc/c/c-typeck.c +++ b/gcc/c/c-typeck.c @@ -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; } diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 0ebc3669286..beae2659c74 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,27 @@ +2018-12-19 David Malcolm + + 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 PR c++/43064 diff --git a/gcc/cp/call.c b/gcc/cp/call.c index ef3a02ce01b..e2f8fe10630 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 *); @@ -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_("%"), 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), @@ -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 *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 1d806b782bd..8a9509564dc 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, 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 **, @@ -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 **, 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, diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index c0552b5be91..e5381b45d03 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; } @@ -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: diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 519510dd8b0..94a33d41dba 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -4128,7 +4128,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) @@ -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; } diff --git a/gcc/gcc-rich-location.c b/gcc/gcc-rich-location.c index 81beb61661c..25a604f6bdc 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 200bbb53c67..202d4f44804 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/ChangeLog b/gcc/testsuite/ChangeLog index ecf5ad6c929..4a613382a07 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,13 @@ +2018-12-19 David Malcolm + + * 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 PR c++/43064 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 00000000000..2634d276641 --- /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 fc70d46cc90..1612cef9ee2 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 4ab7656434c..fab5849dfc7 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 "" } */ } @@ -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 "" } */ +} 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 b19655d6496..de7570a6efa 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 ab928ca170e..ed37e545574 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -5962,4 +5962,53 @@ fndecl_built_in_p (const_tree node, built_in_function name) && DECL_FUNCTION_CODE (node) == name); } +/* A struct for encapsulating location information about an operator + and the operation built from it. + + m_operator_loc is the location of the operator + m_combined_loc is the location of the compound expression. + + For example, given "a && b" the, operator location is: + a && b + ^~ + and the combined location is: + a && b + ~~^~~~ + Capturing this information allows for class binary_op_rich_location + to provide detailed information about e.g. type mismatches in binary + operations where enough location information is available: + + arg_0 op arg_1 + ~~~~~ ^~ ~~~~~ + | | + | arg1 type + arg0 type + + falling back to just showing the combined location: + + arg_0 op arg_1 + ~~~~~~^~~~~~~~ + + where it is not. */ + +struct op_location_t +{ + location_t m_operator_loc; + location_t m_combined_loc; + + /* 1-argument ctor, for constructing from a combined location. */ + op_location_t (location_t combined_loc) + : m_operator_loc (UNKNOWN_LOCATION), m_combined_loc (combined_loc) + {} + + /* 2-argument ctor, for distinguishing between the operator's location + and the combined location. */ + op_location_t (location_t operator_loc, location_t combined_loc) + : m_operator_loc (operator_loc), m_combined_loc (combined_loc) + {} + + /* Implicitly convert back to a location_t, using the combined location. */ + operator location_t () const { return m_combined_loc; } +}; + #endif /* GCC_TREE_H */ -- 2.30.2