]> granicus.if.org Git - clang/commitdiff
[analyzer] The result of && or || is always a 1 or 0.
authorJordan Rose <jordan_rose@apple.com>
Mon, 20 Aug 2012 17:04:45 +0000 (17:04 +0000)
committerJordan Rose <jordan_rose@apple.com>
Mon, 20 Aug 2012 17:04:45 +0000 (17:04 +0000)
Forgetting to at least cast the result was giving us Loc/NonLoc problems
in SValBuilder (hitting an assertion). But the standard (both C and C++)
does actually guarantee that && and || will result in the actual values
1 and 0, typed as 'int' in C and 'bool' in C++, and we can easily model that.

PR13461

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@162209 91177308-0d34-0410-b5e6-96231b3b80d8

lib/StaticAnalyzer/Core/ExprEngineC.cpp
test/Analysis/logical-ops.c [new file with mode: 0644]

index 46cba81b14f76f955393137d41ee1c60d02b448e..3e14765875da74c9aeddcc99eb43f8519950b358 100644 (file)
@@ -531,10 +531,28 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred,
   else {
     // If there is no terminator, by construction the last statement
     // in SrcBlock is the value of the enclosing expression.
+    // However, we still need to constrain that value to be 0 or 1.
     assert(!SrcBlock->empty());
     CFGStmt Elem = cast<CFGStmt>(*SrcBlock->rbegin());
-    const Stmt *S = Elem.getStmt();
-    X = N->getState()->getSVal(S, Pred->getLocationContext());
+    const Expr *RHS = cast<Expr>(Elem.getStmt());
+    SVal RHSVal = N->getState()->getSVal(RHS, Pred->getLocationContext());
+
+    DefinedOrUnknownSVal DefinedRHS = cast<DefinedOrUnknownSVal>(RHSVal);
+    ProgramStateRef StTrue, StFalse;
+    llvm::tie(StTrue, StFalse) = N->getState()->assume(DefinedRHS);
+    if (StTrue) {
+      if (StFalse) {
+        // We can't constrain the value to 0 or 1; the best we can do is a cast.
+        X = getSValBuilder().evalCast(RHSVal, B->getType(), RHS->getType());
+      } else {
+        // The value is known to be true.
+        X = getSValBuilder().makeIntVal(1, B->getType());
+      }
+    } else {
+      // The value is known to be false.
+      assert(StFalse && "Infeasible path!");
+      X = getSValBuilder().makeIntVal(0, B->getType());
+    }
   }
 
   Bldr.generateNode(B, Pred, state->BindExpr(B, Pred->getLocationContext(), X));
diff --git a/test/Analysis/logical-ops.c b/test/Analysis/logical-ops.c
new file mode 100644 (file)
index 0000000..a1223b3
--- /dev/null
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify %s
+
+void clang_analyzer_eval(int);
+
+void testAnd(int i, int *p) {
+  int *nullP = 0;
+  int *knownP = &i;
+  clang_analyzer_eval((knownP && knownP) == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval((knownP && nullP) == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval((knownP && p) == 1); // expected-warning{{UNKNOWN}}
+}
+
+void testOr(int i, int *p) {
+  int *nullP = 0;
+  int *knownP = &i;
+  clang_analyzer_eval((nullP || knownP) == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval((nullP || nullP) == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval((nullP || p) == 1); // expected-warning{{UNKNOWN}}
+}
+
+
+// PR13461
+int testTypeIsInt(int i, void *p) {
+  if (i | (p && p))
+    return 1;
+  return 0;
+}