Hi,

I have a new patch I like to get reviewed. It checks for incorrect logics in 
relational,equal and logic operators in cfgbuilder when trying to evaluate 
bool. A warning is emitted in Sema when an error occurs. 

eg.
if (x != 2 || x != 3) { } always true
if ((x < y) <= 10) {}  always true

Thanks,
Anders 
Index: test/Sema/warn-compint.c
===================================================================
--- test/Sema/warn-compint.c	(revision 0)
+++ test/Sema/warn-compint.c	(working copy)
@@ -0,0 +1,39 @@
+// RUN: %clang %s -fsyntax-only -Xclang -verify -fblocks -Wunreachable-code
+
+#define mydefine 2
+
+void err(int x, int y) {
+
+    if (!x == 10) {}  // expected-warning {{comparison of a boolean expression with an integer other than 0 or 1}}
+    if (!x != 10) {}  // expected-warning {{comparison of a boolean expression with an integer other than 0 or 1}}
+    if (!x <  10) {}  // expected-warning {{comparison of a boolean expression with an integer other than 0 or 1}}
+    if (!x >  10) {}  // expected-warning {{comparison of a boolean expression with an integer other than 0 or 1}}
+    if (!x >= 10) {}  // expected-warning {{comparison of a boolean expression with an integer other than 0 or 1}}
+    if (!x <= 10) {}  // expected-warning {{comparison of a boolean expression with an integer other than 0 or 1}}
+
+    if ((x < y) == 10) {}  // expected-warning {{comparison of a boolean expression with an integer other than 0 or 1}}
+    if ((x < y) != 10) {}  // expected-warning {{comparison of a boolean expression with an integer other than 0 or 1}}
+    if ((x < y) <  10) {}  // expected-warning {{comparison of a boolean expression with an integer other than 0 or 1}}
+    if ((x < y) >  10) {}  // expected-warning {{comparison of a boolean expression with an integer other than 0 or 1}}
+    if ((x < y) >= 10) {}  // expected-warning {{comparison of a boolean expression with an integer other than 0 or 1}}
+    if ((x < y) <= 10) {}  // expected-warning {{comparison of a boolean expression with an integer other than 0 or 1}}
+}
+
+void noerr(int x, int y) {
+
+    if (!x == 1) {}
+    if (!x != 0) {}
+    if (!x <  1) {}
+    if (!x >  0) {}
+    if (!x >= 1) {}
+    if (!x <= 0) {}
+
+    if ((x < y) == 1) {}
+    if ((x < y) != 0) {}
+    if ((x < y) <  1) {}
+    if ((x < y) >  0) {}
+    if ((x < y) >= 1) {}
+    if ((x < y) <= 0) {}
+
+    if ((x < mydefine) <= 10) {}
+} // no-warning
Index: test/Sema/warn-overlap.c
===================================================================
--- test/Sema/warn-overlap.c	(revision 0)
+++ test/Sema/warn-overlap.c	(working copy)
@@ -0,0 +1,51 @@
+// RUN: %clang %s -fsyntax-only -Xclang -verify -fblocks -Wunreachable-code
+
+#define mydefine 2
+
+void f(int x) {
+  int y = 0;
+  // > || <
+  if (x > 2 || x < 1) { }
+  if (x > 2 || x < 2) { }
+  if (x != 2 || x != 3) { } // expected-warning {{logical disjunction always evaluates to true}}
+  if (x > 2 || x < 3) { } // expected-warning {{logical disjunction always evaluates to true}}
+
+  if (x > 2 || x <= 1) { }
+  if (x > 2 || x <= 2) { } // expected-warning {{logical disjunction always evaluates to true}}
+  if (x > 2 || x <= 3) { } // expected-warning {{logical disjunction always evaluates to true}}
+
+  if (x >= 2 || x < 1) { }
+  if (x >= 2 || x < 2) { } // expected-warning {{logical disjunction always evaluates to true}}
+  if (x >= 2 || x < 3) { } // expected-warning {{logical disjunction always evaluates to true}}
+
+  if (x >= 2 || x <= 1) { }
+  if (x >= 2 || x <= 2) { } // expected-warning {{logical disjunction always evaluates to true}}
+  if (x >= 2 || x <= 3) { } // expected-warning {{logical disjunction always evaluates to true}}
+
+  // > && <
+  if (x > 2 && x < 1) { }  // expected-warning {{logical disjunction always evaluates to false}}
+  if (x > 2 && x < 2) { }  // expected-warning {{logical disjunction always evaluates to false}}
+  if (x > 2 && x < 3) { }  // False negative. There should be a warning here. This is always false.
+
+  if (x > 2 && x <= 1) { }  // expected-warning {{logical disjunction always evaluates to false}}
+  if (x > 2 && x <= 2) { }
+  if (x > 2 && x <= 3) { }
+
+  if (x >= 2 && x < 1) { }  // expected-warning {{logical disjunction always evaluates to false}}
+  if (x >= 2 && x < 2) { }
+  if (x >= 2 && x < 3) { }
+
+  if (x >= 2 && x <= 1) { }  // expected-warning {{logical disjunction always evaluates to false}}
+  if (x >= 2 && x <= 2) { }
+  if (x >= 2 && x <= 3) { }
+
+  // !=, ==, ..
+  if (x != 2 || x != 3) { }  // expected-warning {{logical disjunction always evaluates to true}}
+  if (x != 2 || x < 3) { }
+  if (x == 2 && x == 3) { }  // expected-warning {{logical disjunction always evaluates to false}}
+  if (x == 2 && x > 3) { }  // expected-warning {{logical disjunction always evaluates to false}}
+
+  if (x == mydefine && x > 3) { }  // no-warning
+  if (x == 2 && y > 3) { }  // no-warning
+}
+
Index: lib/Analysis/CFG.cpp
===================================================================
--- lib/Analysis/CFG.cpp	(revision 192595)
+++ lib/Analysis/CFG.cpp	(working copy)
@@ -483,6 +483,207 @@
     B->addSuccessor(S, cfg->getBumpVectorContext());
   }
 
+  /// \brief Find a relational comparison with an expression evaluating to a
+  /// boolean and a constant other than 0 and 1.
+  /// e.g. if ((x < y) == 10)
+  TryResult checkIncorrectRelationalOperator(const BinaryOperator *B) {
+    if (!B->isRelationalOp())
+      return TryResult();
+
+    const IntegerLiteral *IntLiteral =
+        dyn_cast<IntegerLiteral>(B->getLHS()->IgnoreParens());
+    const Expr *BoolExpr = B->getRHS()->IgnoreParens();
+    bool IntFirst = true;
+    if (!IntLiteral) {
+      IntLiteral = dyn_cast<IntegerLiteral>(B->getRHS()->IgnoreParens());
+      BoolExpr = B->getLHS()->IgnoreParens();
+      IntFirst = false;
+    }
+
+    if (!IntLiteral || !BoolExpr->isKnownToHaveBooleanValue())
+      return TryResult();
+
+    llvm::APInt IntValue = IntLiteral->getValue();
+    if ((IntValue != 1) && (IntValue != 0)) {
+      CFGObserver::notifyCompareBoolWithInt(B);
+      bool IntLarger = !IntValue.isNegative();
+      BinaryOperatorKind Bok = B->getOpcode();
+      if (Bok == BO_GT ||
+          Bok == BO_GE) {
+        return TryResult(IntFirst != IntLarger);
+      } else {
+        return TryResult(IntFirst == IntLarger);
+      }
+    }
+    return TryResult();
+  }
+
+  /// Find a equality comparison with an expression evaluating to a boolean and
+  /// a constant other than 0 and 1.
+  /// e.g. if (!x == 10)
+  TryResult checkIncorrectEqualityOperator(const BinaryOperator *B) {
+    if (!B->isEqualityOp())
+      return TryResult();
+
+    const IntegerLiteral *IntLiteral =
+        dyn_cast<IntegerLiteral>(B->getLHS()->IgnoreParens());
+    const Expr *BoolExpr = B->getRHS()->IgnoreParens();
+
+    if (!IntLiteral) {
+      IntLiteral = dyn_cast<IntegerLiteral>(B->getRHS()->IgnoreParens());
+      BoolExpr = B->getLHS()->IgnoreParens();
+    }
+
+    if (!IntLiteral || !BoolExpr->isKnownToHaveBooleanValue())
+      return TryResult();
+
+    llvm::APInt IntValue = IntLiteral->getValue();
+    if ((IntValue != 1) && (IntValue != 0)) {
+      CFGObserver::notifyCompareBoolWithInt(B);
+      return TryResult(B->getOpcode() != BO_EQ);
+    }
+    return TryResult();
+  }
+
+  enum LogicRelation { Equal, NotEqual, Less, LessEqual, More, MoreEqual };
+
+  bool analyzeLogicOperatorCondition(const IntegerLiteral *FirstConstant,
+                                     const IntegerLiteral *SecondConstant,
+                                     LogicRelation Relation) {
+    llvm::APSInt f1,f2;
+    if (FirstConstant->EvaluateAsInt(f1,*Context) &&
+        SecondConstant->EvaluateAsInt(f2,*Context))
+      return (Relation == Equal     && (f1 == f2)) ||
+             (Relation == NotEqual  && (f1 != f2)) ||
+             (Relation == Less      && (f1 <  f2)) ||
+             (Relation == LessEqual && (f1 <= f2)) ||
+             (Relation == More      && (f1 >  f2)) ||
+             (Relation == MoreEqual && (f1 >= f2));
+    else
+      return false;
+  }
+
+  /// \brief Find a pair of comparison expressions with or without parentheses
+  /// with a shared variable and constants and a logical operator between them.
+  /// e.g. if (x != 3 || x != 4)
+  TryResult checkIncorrectLogicOperator(const BinaryOperator *B) {
+    if (!B->isLogicalOp())
+      return TryResult();
+
+    const BinaryOperator *LHS =
+        dyn_cast<BinaryOperator>(B->getLHS()->IgnoreParens());
+    const BinaryOperator *RHS =
+        dyn_cast<BinaryOperator>(B->getRHS()->IgnoreParens());
+    if (!LHS || !RHS)
+      return TryResult();
+
+    if (!LHS->isComparisonOp() || !RHS->isComparisonOp())
+      return TryResult();
+
+    BinaryOperatorKind BO1 = LHS->getOpcode();
+    const DeclRefExpr *Decl1 =
+        dyn_cast<DeclRefExpr>(LHS->getLHS()->IgnoreImpCasts()->IgnoreParens());
+    const IntegerLiteral *Literal1 =
+        dyn_cast<IntegerLiteral>(LHS->getRHS()->IgnoreParens());
+
+    if (!Decl1 && !Literal1) {
+      if (BO1 == BO_GT)
+        BO1 = BO_LT;
+      else if (BO1 == BO_GE)
+        BO1 = BO_LE;
+      else if (BO1 == BO_LT)
+        BO1 = BO_GT;
+      else if (BO1 == BO_LE)
+        BO1 = BO_GE;
+      Decl1 =
+          dyn_cast<DeclRefExpr>(LHS->getRHS()->IgnoreImpCasts()->IgnoreParens());
+      Literal1 = dyn_cast<IntegerLiteral>(LHS->getLHS()->IgnoreParens());
+    }
+
+    if (!Decl1 || !Literal1)
+      return TryResult();
+
+    BinaryOperatorKind BO2 = RHS->getOpcode();
+    const DeclRefExpr *Decl2 =
+        dyn_cast<DeclRefExpr>(RHS->getLHS()->IgnoreImpCasts()->IgnoreParens());
+    const IntegerLiteral *Literal2 =
+        dyn_cast<IntegerLiteral>(RHS->getRHS()->IgnoreParens());
+
+    if (!Decl2 && !Literal2) {
+      if (BO2 == BO_GT)
+        BO2 = BO_LT;
+      else if (BO2 == BO_GE)
+        BO2 = BO_LE;
+      else if (BO2 == BO_LT)
+        BO2 = BO_GT;
+      else if (BO2 == BO_LE)
+        BO2 = BO_GE;
+      Decl2 =
+          dyn_cast<DeclRefExpr>(RHS->getRHS()->IgnoreImpCasts()->IgnoreParens());
+      Literal2 = dyn_cast<IntegerLiteral>(RHS->getLHS()->IgnoreParens());
+    }
+
+    if (!Decl2 || !Literal2)
+      return TryResult();
+
+    // Check that it is the same variable on both sides.
+    if (Decl1->getNameInfo().getAsString() != Decl2->getNameInfo().getAsString())
+      return TryResult();
+
+    static const struct LinkedConditions {
+        BinaryOperatorKind  C1;
+        BinaryOperatorKind  Bop;
+        BinaryOperatorKind  C2;
+        LogicRelation       Relation;
+        bool                Result;
+    } Conditions[] = {
+        { BO_NE, BO_LOr,  BO_NE, NotEqual,  true },
+        { BO_EQ, BO_LAnd, BO_EQ, NotEqual,  false  },
+        { BO_GT, BO_LOr,  BO_LT, Less,      true },
+        { BO_GE, BO_LOr,  BO_LT, LessEqual, true },
+        { BO_GE, BO_LOr,  BO_LE, LessEqual, true },
+        { BO_GT, BO_LOr,  BO_LE, LessEqual, true },
+        { BO_LT, BO_LAnd, BO_GT, LessEqual, false  },
+        { BO_LE, BO_LAnd, BO_GT, Less,      false  },
+        { BO_LE, BO_LAnd, BO_GE, Less,      false  },
+        { BO_LT, BO_LAnd, BO_GE, Less,      false  },
+        { BO_GT, BO_LAnd, BO_EQ, MoreEqual, false  },
+        { BO_LT, BO_LAnd, BO_EQ, LessEqual, false  },
+        { BO_GE, BO_LAnd, BO_EQ, More,      false  },
+        { BO_LE, BO_LAnd, BO_EQ, Less,      false  },
+        { BO_NE, BO_LAnd, BO_EQ, Equal,     false  },
+        { BO_NE, BO_LOr,  BO_EQ, Equal,     true }
+    };
+    BinaryOperatorKind Bok = B->getOpcode();
+    for (unsigned int i = 0; i < (sizeof(Conditions) / sizeof(Conditions[0]));
+        i++) {
+      if (Bok != Conditions[i].Bop)
+        continue;
+
+      bool Error = false;
+      if (BO1 == Conditions[i].C1 && BO2 == Conditions[i].C2) {
+        Error = analyzeLogicOperatorCondition( Literal1,
+                                               Literal2,
+                                               Conditions[i].Relation);
+      } else if (BO1 == Conditions[i].C2 && BO2 == Conditions[i].C1) {
+        Error = analyzeLogicOperatorCondition( Literal2,
+                                               Literal1,
+                                               Conditions[i].Relation);
+      } else {
+        // Continue looking..
+        continue;
+      }
+
+      if (Error) {
+        CFGObserver::notifyCompareAlwaysTrue(B,Conditions[i].Result);
+        return TryResult(Conditions[i].Result);
+      }
+
+      return TryResult();
+    }
+    return TryResult();
+  }
+
   /// Try and evaluate an expression to an integer constant.
   bool tryEvaluate(Expr *S, Expr::EvalResult &outResult) {
     if (!BuildOpts.PruneTriviallyFalseEdges)
@@ -565,13 +766,24 @@
             // is determined by the RHS: X && 0 -> 0, X || 1 -> 1.
             if (RHS.isTrue() == (Bop->getOpcode() == BO_LOr))
               return RHS.isTrue();
+          } else {
+            TryResult BopRes = checkIncorrectLogicOperator(Bop);
+            if (BopRes.isKnown())
+              return BopRes.isTrue();
           }
         }
 
         return TryResult();
+      } else if (Bop->isEqualityOp()) {
+          TryResult BopRes = checkIncorrectEqualityOperator(Bop);
+          if (BopRes.isKnown())
+            return BopRes.isTrue();
+      } else if (Bop->isRelationalOp()) {
+        TryResult BopRes = checkIncorrectRelationalOperator(Bop);
+        if (BopRes.isKnown())
+          return BopRes.isTrue();
       }
     }
-
     bool Result;
     if (E->EvaluateAsBooleanCondition(Result, *Context))
       return Result;
@@ -1311,6 +1523,8 @@
     else {
       RHSBlock->setTerminator(Term);
       TryResult KnownVal = tryEvaluateBool(RHS);
+      if (!KnownVal.isKnown())
+        KnownVal = tryEvaluateBool(B);
       addSuccessor(RHSBlock, KnownVal.isFalse() ? NULL : TrueBlock);
       addSuccessor(RHSBlock, KnownVal.isTrue() ? NULL : FalseBlock);
     }
@@ -3974,7 +4188,35 @@
   }
 }
 
+std::vector<CFGCallback*> CFGObserver::Observers;
+void CFGObserver::attach(CFGCallback *Observer) {
+  Observers.push_back(Observer);
+}
 
+void CFGObserver::detach(CFGCallback *Observer) {
+  Observers.erase(std::remove(Observers.begin(), Observers.end(), Observer),
+                  Observers.end());
+}
+
+void CFGObserver::notifyCompareAlwaysTrue(const BinaryOperator* B,
+                                          bool IsAlwaysTrue) {
+  for(std::vector<CFGCallback*>::const_iterator I = Observers.begin();
+      I != Observers.end(); ++I) {
+    if(*I != 0) {
+      (*I)->compareAlwaysTrue(B,IsAlwaysTrue);
+    }
+  }
+}
+
+void CFGObserver::notifyCompareBoolWithInt(const BinaryOperator* B) {
+  for(std::vector<CFGCallback*>::const_iterator I = Observers.begin();
+      I != Observers.end(); ++I) {
+    if(*I != 0) {
+      (*I)->compareBoolWithInt(B);
+    }
+  }
+}
+
 /// dump - A simple pretty printer of a CFG that outputs to stderr.
 void CFG::dump(const LangOptions &LO, bool ShowColors) const {
   print(llvm::errs(), LO, ShowColors);
Index: lib/Sema/AnalysisBasedWarnings.cpp
===================================================================
--- lib/Sema/AnalysisBasedWarnings.cpp	(revision 192595)
+++ lib/Sema/AnalysisBasedWarnings.cpp	(working copy)
@@ -77,6 +77,60 @@
   reachable_code::FindUnreachableCode(AC, UC);
 }
 
+/// \brief Warn on logical operator errors in CFGBuilder
+class LogicalErrorsHandler : public CFGCallback {
+  Sema &S;
+public:
+  LogicalErrorsHandler(Sema &S) : CFGCallback(),S(S) {}
+  void compareAlwaysTrue(const BinaryOperator* B, bool isAlwaysTrue) {
+    if (B->getLHS()->getExprLoc().isMacroID() ||
+        B->getRHS()->getExprLoc().isMacroID())
+      return;
+
+    const BinaryOperator *LHS =
+        dyn_cast<BinaryOperator>(B->getLHS()->IgnoreParens());
+    const BinaryOperator *RHS =
+        dyn_cast<BinaryOperator>(B->getRHS()->IgnoreParens());
+
+    if (LHS)
+      if (LHS->getLHS()->getExprLoc().isMacroID() ||
+          LHS->getRHS()->getExprLoc().isMacroID())
+            return;
+    if (RHS)
+      if (RHS->getLHS()->getExprLoc().isMacroID() ||
+          RHS->getRHS()->getExprLoc().isMacroID())
+            return;
+
+    SourceRange DiagRange(B->getLHS()->getLocStart(), B->getRHS()->getLocEnd());
+    S.Diag(B->getExprLoc(), diag::warn_operator_always_true_comparison)
+        << DiagRange << (isAlwaysTrue ? "true" : "false");
+  }
+
+  void compareBoolWithInt(const BinaryOperator* B) {
+    if (B->getLHS()->getExprLoc().isMacroID() ||
+        B->getRHS()->getExprLoc().isMacroID())
+      return;
+
+    const BinaryOperator *LHS =
+        dyn_cast<BinaryOperator>(B->getLHS()->IgnoreParens());
+    const BinaryOperator *RHS =
+        dyn_cast<BinaryOperator>(B->getRHS()->IgnoreParens());
+
+    if (LHS)
+      if (LHS->getLHS()->getExprLoc().isMacroID() ||
+          LHS->getRHS()->getExprLoc().isMacroID())
+            return;
+    if (RHS)
+      if (RHS->getLHS()->getExprLoc().isMacroID() ||
+          RHS->getRHS()->getExprLoc().isMacroID())
+            return;
+
+    SourceRange DiagRange(B->getLHS()->getLocStart(), B->getRHS()->getLocEnd());
+    S.Diag(B->getExprLoc(), diag::warn_bool_and_int_comparison) << DiagRange;
+  }
+};
+
+
 //===----------------------------------------------------------------------===//
 // Check for missing return value.
 //===----------------------------------------------------------------------===//
@@ -1702,8 +1756,12 @@
     bool isTemplateInstantiation = false;
     if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(D))
       isTemplateInstantiation = Function->isTemplateInstantiation();
-    if (!isTemplateInstantiation)
-      CheckUnreachable(S, AC);
+      if (!isTemplateInstantiation) {
+        LogicalErrorsHandler Leh(S);
+        CFGObserver::attach(&Leh);
+        CheckUnreachable(S, AC);
+        CFGObserver::detach(&Leh);
+      }
   }
 
   // Check for thread safety violations
Index: lib/AST/Expr.cpp
===================================================================
--- lib/AST/Expr.cpp	(revision 192595)
+++ lib/AST/Expr.cpp	(working copy)
@@ -151,6 +151,8 @@
     switch (UO->getOpcode()) {
     case UO_Plus:
       return UO->getSubExpr()->isKnownToHaveBooleanValue();
+    case UO_LNot:
+      return true;
     default:
       return false;
     }
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td	(revision 192595)
+++ include/clang/Basic/DiagnosticSemaKinds.td	(working copy)
@@ -4544,6 +4544,12 @@
 def warn_lunsigned_always_true_comparison : Warning<
   "comparison of unsigned%select{| enum}2 expression %0 is always %1">,
   InGroup<TautologicalCompare>;
+def warn_operator_always_true_comparison : Warning<
+  "logical disjunction always evaluates to %0">,
+  InGroup<TautologicalCompare>;
+def warn_bool_and_int_comparison : Warning<
+  "comparison of a boolean expression with an integer other than 0 or 1">,
+  InGroup<TautologicalCompare>;
 def warn_out_of_range_compare : Warning<
   "comparison of constant %0 with expression of type %1 is always "
   "%select{false|true}2">, InGroup<TautologicalOutOfRangeCompare>;
Index: include/clang/Analysis/CFG.h
===================================================================
--- include/clang/Analysis/CFG.h	(revision 192595)
+++ include/clang/Analysis/CFG.h	(working copy)
@@ -45,6 +45,7 @@
   class ASTContext;
   class CXXRecordDecl;
   class CXXDeleteExpr;
+  class BinaryOperator;
 
 /// CFGElement - Represents a top-level expression in a basic block.
 class CFGElement {
@@ -609,6 +610,29 @@
   }
 };
 
+/// \brief CFGCallback defines methods that should be called when a logical
+/// operator error is found when building the CFG.
+class CFGCallback {
+public:
+  CFGCallback(){}
+  virtual void compareAlwaysTrue(const BinaryOperator *B, bool isAlwaysTrue) {};
+  virtual void compareBoolWithInt(const BinaryOperator *B) {};
+  virtual ~CFGCallback(){}
+};
+
+/// \brief Keeps track of all observers. Provides functionality to add and
+/// remove observers and notify observers on found logical operator errors.
+class CFGObserver {
+public:
+  static void attach(CFGCallback *Observer);
+  static void detach(CFGCallback *Observer);
+  static void notifyCompareAlwaysTrue(const BinaryOperator* B,
+                                      bool IsAlwaysTrue);
+  static void notifyCompareBoolWithInt(const BinaryOperator* B);
+private:
+  static std::vector<CFGCallback*> Observers;
+};
+
 /// CFG - Represents a source-level, intra-procedural CFG that represents the
 ///  control-flow of a Stmt.  The Stmt can represent an entire function body,
 ///  or a single expression.  A CFG will always contain one empty block that
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to