C++: improvements to binary operator diagnostics (PR c++/87504)
authorDavid Malcolm <dmalcolm@redhat.com>
Wed, 19 Dec 2018 15:15:42 +0000 (15:15 +0000)
committerDavid Malcolm <dmalcolm@gcc.gnu.org>
Wed, 19 Dec 2018 15:15:42 +0000 (15:15 +0000)
The C frontend is able (where expression locations are available) to print
problems with binary operators in 3-location form, labelling the types of
the expressions:

  arg_0 op arg_1
  ~~~~~ ^~ ~~~~~
    |        |
    |        arg1 type
    arg0 type

The C++ frontend currently just shows the combined location:

  arg_0 op arg_1
  ~~~~~~^~~~~~~~

and fails to highlight where the subexpressions are, or their types.

This patch introduces a op_location_t struct for handling the above
operator-location vs combined-location split, and a new
class binary_op_rich_location for displaying the above, so that the
C++ frontend is able to use the more detailed 3-location form for
type mismatches in binary operators, and for -Wtautological-compare
(where types are not displayed).  Both forms can be seen in this
example:

bad-binary-ops.C:69:20: error: no match for 'operator&&' (operand types are
  's' and 't')
   69 |   return ns_4::foo && ns_4::inner::bar;
      |          ~~~~~~~~~ ^~ ~~~~~~~~~~~~~~~~
      |                |                   |
      |                s                   t
bad-binary-ops.C:69:20: note: candidate: 'operator&&(bool, bool)' <built-in>
   69 |   return ns_4::foo && ns_4::inner::bar;
      |          ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~

The patch also allows for some uses of macros in
-Wtautological-compare, where both sides of the comparison have
been spelled the same way, e.g.:

Wtautological-compare-ranges.c:23:11: warning: self-comparison always
   evaluates to true [-Wtautological-compare]
   23 |   if (FOO == FOO);
      |           ^~

gcc/c-family/ChangeLog:
PR c++/87504
* c-common.h (warn_tautological_cmp): Convert 1st param from
location_t to const op_location_t &.
* c-warn.c (find_array_ref_with_const_idx_r): Call fold_for_warn
when testing for INTEGER_CST.
(warn_tautological_bitwise_comparison): Convert 1st param from
location_t to const op_location_t &; use it to build a
binary_op_rich_location, and use this.
(spelled_the_same_p): New function.
(warn_tautological_cmp): Convert 1st param from location_t to
const op_location_t &.  Warn for macro expansions if
spelled_the_same_p.  Use binary_op_rich_location.

gcc/c/ChangeLog:
PR c++/87504
* c-typeck.c (class maybe_range_label_for_tree_type_mismatch):
Move from here to gcc-rich-location.h and gcc-rich-location.c.
(build_binary_op): Use struct op_location_t and
class binary_op_rich_location.

gcc/cp/ChangeLog:
PR c++/87504
* call.c (op_error): Convert 1st param from location_t to
const op_location_t &.  Use binary_op_rich_location for binary
ops.
(build_conditional_expr_1): Convert 1st param from location_t to
const op_location_t &.
(build_conditional_expr): Likewise.
(build_new_op_1): Likewise.
(build_new_op): Likewise.
* cp-tree.h (build_conditional_expr): Likewise.
(build_new_op): Likewise.
(build_x_binary_op): Likewise.
(cp_build_binary_op): Likewise.
* parser.c (cp_parser_primary_expression): Build a location
for id-expression nodes.
(cp_parser_binary_expression): Use an op_location_t when
calling build_x_binary_op.
(cp_parser_operator): Build a location for user-defined literals.
* typeck.c (build_x_binary_op): Convert 1st param from location_t
to const op_location_t &.
(cp_build_binary_op): Likewise.  Use binary_op_rich_location.

gcc/ChangeLog:
PR c++/87504
* gcc-rich-location.c
(maybe_range_label_for_tree_type_mismatch::get_text): Move here from
c/c-typeck.c.
(binary_op_rich_location::binary_op_rich_location): New ctor.
(binary_op_rich_location::use_operator_loc_p): New function.
* gcc-rich-location.h
(class maybe_range_label_for_tree_type_mismatch)): Move here from
c/c-typeck.c.
(struct op_location_t): New forward decl.
(class binary_op_rich_location): New class.
* tree.h (struct op_location_t): New struct.

gcc/testsuite/ChangeLog:
* c-c++-common/Wtautological-compare-ranges.c: New test.
* g++.dg/cpp0x/pr51420.C: Add -fdiagnostics-show-caret and update
expected output.
* g++.dg/diagnostic/bad-binary-ops.C: Update expected output from
1-location form to 3-location form, with labelling of ranges with
types.  Add examples of id-expression nodes with namespaces.
* g++.dg/diagnostic/param-type-mismatch-2.C: Likewise.

From-SVN: r267273

19 files changed:
gcc/ChangeLog
gcc/c-family/ChangeLog
gcc/c-family/c-common.h
gcc/c-family/c-warn.c
gcc/c/ChangeLog
gcc/c/c-typeck.c
gcc/cp/ChangeLog
gcc/cp/call.c
gcc/cp/cp-tree.h
gcc/cp/parser.c
gcc/cp/typeck.c
gcc/gcc-rich-location.c
gcc/gcc-rich-location.h
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/Wtautological-compare-ranges.c [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/pr51420.C
gcc/testsuite/g++.dg/diagnostic/bad-binary-ops.C
gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
gcc/tree.h

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