PR61271 showed that this warning, recently added to clang, is quite
useful and detected several reckless cases in the GCC codebase.
This patch adds this warning even for GCC.  Due to PR61271,
it's not a part of -Wall, that would break the bootstrap, but
after that is fixed, I think we want this warning be a part of
-Wall or -Wextra.

It's possible to suppress the warning by enclosing the LHS
in parentheses.  This proved to be hard to achieve in the C++
FE, so I had to go all the way up to parser...  Jason, does the
"bool parenthesized_not_lhs_warn" line looks reasonable?

Also for C++, I think we don't want this warning to trigger when the
operator (==, !=, >, <, <=, >=) is overloaded.  But I admit
I haven't even tried that, and I don't know how to detect overloaded
operators except DECL_OVERLOADED_OPERATOR_P.

Regtested/bootstrapped on x86_64-unknown-linux-gnu, ok for trunk?

2014-06-02  Marek Polacek  <pola...@redhat.com>

        PR c/49706
        * doc/invoke.texi: Document -Wlogical-not-parentheses.
c-family/
        * c-common.c (warn_logical_not_parentheses): New function.
        * c-common.h (warn_logical_not_parentheses): Declare.
        * c.opt (Wlogical-not-parentheses): New option.
c/
        * c-typeck.c (parser_build_binary_op): Warn when logical not is used
        on the left hand side operand of a comparison. 
cp/
        * parser.c (cp_parser_binary_expression): Warn when logical not is
        used on the left hand side operand of a comparison.
testsuite/
        * c-c++-common/pr49706.c: New test.

diff --git gcc/c-family/c-common.c gcc/c-family/c-common.c
index 6ec14fc..650afaf 100644
--- gcc/c-family/c-common.c
+++ gcc/c-family/c-common.c
@@ -1722,6 +1722,25 @@ warn_logical_operator (location_t location, enum 
tree_code code, tree type,
     }
 }
 
+/* Warn about logical not used on the left hand side operand of a comparison.
+   This function assumes that the LHS is inside of TRUTH_NOT_EXPR.
+   Do not warn if the LHS or RHS is of a boolean or a vector type.  */
+
+void
+warn_logical_not_parentheses (location_t location, enum tree_code code,
+                             tree lhs, tree rhs)
+{
+  if (TREE_CODE_CLASS (code) != tcc_comparison
+      || TREE_CODE (TREE_TYPE (lhs)) == BOOLEAN_TYPE
+      || TREE_CODE (TREE_TYPE (rhs)) == BOOLEAN_TYPE
+      || VECTOR_TYPE_P (TREE_TYPE (lhs))
+      || VECTOR_TYPE_P (TREE_TYPE (rhs)))
+    return;
+
+  warning_at (location, OPT_Wlogical_not_parentheses,
+             "logical not is only applied to the left hand side of "
+             "comparison");
+}
 
 /* Warn if EXP contains any computations whose results are not used.
    Return true if a warning is printed; false otherwise.  LOCUS is the
diff --git gcc/c-family/c-common.h gcc/c-family/c-common.h
index 0d34004..83d5dee 100644
--- gcc/c-family/c-common.h
+++ gcc/c-family/c-common.h
@@ -772,6 +772,8 @@ extern void overflow_warning (location_t, tree);
 extern bool warn_if_unused_value (const_tree, location_t);
 extern void warn_logical_operator (location_t, enum tree_code, tree,
                                   enum tree_code, tree, enum tree_code, tree);
+extern void warn_logical_not_parentheses (location_t, enum tree_code, tree,
+                                         tree);
 extern void check_main_parameter_types (tree decl);
 extern bool c_determine_visibility (tree);
 extern bool vector_types_compatible_elements_p (tree, tree);
diff --git gcc/c-family/c.opt gcc/c-family/c.opt
index c586e65..d51c890 100644
--- gcc/c-family/c.opt
+++ gcc/c-family/c.opt
@@ -490,6 +490,10 @@ Wlogical-op
 C ObjC C++ ObjC++ Var(warn_logical_op) Init(0) Warning 
 Warn when a logical operator is suspiciously always evaluating to true or false
 
+Wlogical-not-parentheses
+C ObjC C++ ObjC++ Var(warn_logical_not_paren) Warning
+Warn when logical not is used on the left hand side operand of a comparison
+
 Wlong-long
 C ObjC C++ ObjC++ Var(warn_long_long) Init(-1) Warning
 Do not warn about using \"long long\" when -pedantic
diff --git gcc/c/c-typeck.c gcc/c/c-typeck.c
index 6ca584b..f3b142f 100644
--- gcc/c/c-typeck.c
+++ gcc/c/c-typeck.c
@@ -3401,6 +3401,11 @@ parser_build_binary_op (location_t location, enum 
tree_code code,
     warn_logical_operator (location, code, TREE_TYPE (result.value),
                           code1, arg1.value, code2, arg2.value);
 
+  if (warn_logical_not_paren
+      && code1 == TRUTH_NOT_EXPR
+      && TREE_CODE (arg1.value) == EQ_EXPR)
+    warn_logical_not_parentheses (location, code, arg1.value, arg2.value);
+
   /* Warn about comparisons against string literals, with the exception
      of testing for equality or inequality of a string literal with NULL.  */
   if (code == EQ_EXPR || code == NE_EXPR)
diff --git gcc/cp/parser.c gcc/cp/parser.c
index 2591ae5..1ced05a 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -7883,6 +7883,8 @@ cp_parser_binary_expression (cp_parser* parser, bool 
cast_p,
   enum tree_code rhs_type;
   enum cp_parser_prec new_prec, lookahead_prec;
   tree overload;
+  bool parenthesized_not_lhs_warn
+    = cp_lexer_next_token_is (parser->lexer, CPP_NOT);
 
   /* Parse the first expression.  */
   current.lhs = cp_parser_cast_expression (parser, /*address_p=*/false,
@@ -7985,6 +7987,14 @@ cp_parser_binary_expression (cp_parser* parser, bool 
cast_p,
       else if (current.tree_type == TRUTH_ORIF_EXPR)
        c_inhibit_evaluation_warnings -= current.lhs == truthvalue_true_node;
 
+      if (warn_logical_not_paren
+         && (complain_flags (decltype_p) & tf_warning)
+         && !processing_template_decl
+         && TREE_CODE (current.lhs) == EQ_EXPR
+         && parenthesized_not_lhs_warn)
+       warn_logical_not_parentheses (current.loc, current.tree_type,
+                                     TREE_OPERAND (current.lhs, 0), rhs);
+
       overload = NULL;
       /* ??? Currently we pass lhs_type == ERROR_MARK and rhs_type ==
         ERROR_MARK for everything that is not a binary expression.
diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi
index 9475594..afea361 100644
--- gcc/doc/invoke.texi
+++ gcc/doc/invoke.texi
@@ -256,7 +256,7 @@ Objective-C and Objective-C++ Dialects}.
 -Winit-self  -Winline -Wmaybe-uninitialized @gol
 -Wno-int-to-pointer-cast -Wno-invalid-offsetof @gol
 -Winvalid-pch -Wlarger-than=@var{len}  -Wunsafe-loop-optimizations @gol
--Wlogical-op -Wlong-long @gol
+-Wlogical-op -Wlogical-not-parentheses -Wlong-long @gol
 -Wmain -Wmaybe-uninitialized -Wmissing-braces  -Wmissing-field-initializers 
@gol
 -Wmissing-include-dirs @gol
 -Wno-multichar  -Wnonnull  -Wno-overflow -Wopenmp-simd @gol
@@ -4687,6 +4687,24 @@ Warn about suspicious uses of logical operators in 
expressions.
 This includes using logical operators in contexts where a
 bit-wise operator is likely to be expected.
 
+@item -Wlogical-not-parentheses
+@opindex Wlogical-not-parentheses
+@opindex Wno-logical-not-parentheses
+Warn about logical not used on the left hand side operand of a comparison.
+This option does not warn if the LHS or RHS operand is of a boolean or
+a vector type.  Its purpose is to detect suspicious code like the following:
+@smallexample
+int a;
+...
+if (!a > 1) @{ ... @}
+@end smallexample
+
+It is possible to suppress the warning by wrapping the LHS into
+parentheses:
+@smallexample
+if ((!a) > 1) @{ ... @}
+@end smallexample
+
 @item -Waggregate-return
 @opindex Waggregate-return
 @opindex Wno-aggregate-return
diff --git gcc/testsuite/c-c++-common/pr49706.c 
gcc/testsuite/c-c++-common/pr49706.c
index e69de29..6a59046 100644
--- gcc/testsuite/c-c++-common/pr49706.c
+++ gcc/testsuite/c-c++-common/pr49706.c
@@ -0,0 +1,95 @@
+/* PR c/49706 */
+/* { dg-do compile } */
+/* { dg-options "-Wlogical-not-parentheses" } */
+
+#ifndef __cplusplus
+#define bool _Bool
+#endif
+enum E { A, B };
+bool b;
+extern enum E foo_e (void);
+extern bool foo_b (void);
+extern int foo_i (void);
+
+void
+fn1 (int i1, int i2, bool b1, bool b2)
+{
+  b = !i1 == i2; /* { dg-warning "logical not is only applied to the left hand 
side of comparison" } */
+  b = !i1 != i2; /* { dg-warning "logical not is only applied to the left hand 
side of comparison" } */
+  b = !i1 < i2; /* { dg-warning "logical not is only applied to the left hand 
side of comparison" } */
+  b = !i1 > i2; /* { dg-warning "logical not is only applied to the left hand 
side of comparison" } */
+  b = !i1 <= i2; /* { dg-warning "logical not is only applied to the left hand 
side of comparison" } */
+  b = !i1 >= i2; /* { dg-warning "logical not is only applied to the left hand 
side of comparison" } */
+
+  b = i1 == i2;
+  b = i1 != i2;
+  b = i1 < i2;
+  b = i1 > i2;
+  b = i1 <= i2;
+  b = i1 >= i2;
+
+  /* Parens suppress the warning.  */
+  b = (!i1) == i2;
+  b = (!i1) != i2;
+  b = (!i1) < i2;
+  b = (!i1) > i2;
+  b = (!i1) <= i2;
+  b = (!i1) >= i2;
+
+  /* ...but not these parens.  */
+  b = (!i1 == i2); /* { dg-warning "logical not is only applied to the left 
hand side of comparison" } */
+  b = (!i1 != i2); /* { dg-warning "logical not is only applied to the left 
hand side of comparison" } */
+  b = (!i1 < i2); /* { dg-warning "logical not is only applied to the left 
hand side of comparison" } */
+  b = (!i1 > i2); /* { dg-warning "logical not is only applied to the left 
hand side of comparison" } */
+  b = (!i1 <= i2); /* { dg-warning "logical not is only applied to the left 
hand side of comparison" } */
+  b = (!i1 >= i2); /* { dg-warning "logical not is only applied to the left 
hand side of comparison" } */
+
+  b = !b1 == b2;
+  b = !b1 != b2;
+  b = !b1 < b2;
+  b = !b1 > b2;
+  b = !b1 <= b2;
+  b = !b1 >= b2;
+
+  b = !foo_i () == i1; /* { dg-warning "logical not is only applied to the 
left hand side of comparison" } */
+  b = (!foo_i ()) == i1;
+  b = !foo_b () == b1;
+
+  b = !!i1 == i2;
+  b = !!i1 != i2;
+  b = !!i1 < i2;
+  b = !!i1 > i2;
+  b = !!i1 <= i2;
+  b = !!i1 >= i2;
+  b = !!foo_i () == i1;
+
+  /* Be careful here.  */
+  b = (i1 == 0) != 0;
+  b = (i1 == 0) == 0;
+  b = (i1 != 0) != 0;
+  b = (i1 != 0) == 0;
+}
+
+void
+fn2 (enum E e)
+{
+  b = e == B;
+  b = e == foo_e ();
+  b = foo_e () == A;
+  b = foo_e () == foo_e ();
+
+  b = !e == A; /* { dg-warning "logical not is only applied to the left hand 
side of comparison" } */
+  b = !e == foo_e (); /* { dg-warning "logical not is only applied to the left 
hand side of comparison" } */
+  b = !foo_e () == A; /* { dg-warning "logical not is only applied to the left 
hand side of comparison" } */
+  b = !foo_e () == foo_e (); /* { dg-warning "logical not is only applied to 
the left hand side of comparison" } */
+
+  b = !(e == A);
+  b = !(e == foo_e ());
+  b = !(foo_e () == A);
+  b = !(foo_e () == foo_e ());
+
+  b = (!e) == A;
+  b = (!e) == foo_e ();
+  b = (!foo_e ()) == A;
+  b = (!foo_e ()) == foo_e ();
+}

        Marek

Reply via email to