diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -10,8 +10,9 @@ // //===----------------------------------------------------------------------===// -#include "clang/AST/ExprCXX.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/StmtVisitor.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" @@ -37,10 +38,47 @@ return Symbol; } +// This class is responsible to build such an SVal for a BinOp expression where +// all constituent SVals are representing an LValue expression. We use it to +// query constraints that are attached directly to the BinOp. +class LValueSValBuilder : public ConstStmtVisitor { + QualType T; + const LocationContext *LCtx; + ProgramStateRef State; + SValBuilder &SvalBuilder; + +public: + LValueSValBuilder(QualType T, const LocationContext *LCtx, + ProgramStateRef State, SValBuilder &SvalBuilder) + : T(T), LCtx(LCtx), State(State), SvalBuilder(SvalBuilder) {} + + SVal VisitImplicitCastExpr(const ImplicitCastExpr *E) { + if (E->getCastKind() == CK_LValueToRValue) { + SVal LValue = State->getSVal(E->getSubExpr(), LCtx); // LValue. + if (Optional LValueLoc = LValue.getAs()) + return State->getRawSVal(*LValueLoc, T); + } + return SVal(); + } + SVal VisitBinaryOperator(const BinaryOperator *B) { + SVal L = Visit(B->getLHS()); + SVal R = Visit(B->getRHS()); + if (L.isUndef() || R.isUndef()) + return SVal(); + Optional LNL = L.getAs(); + Optional RNL = R.getAs(); + if (!LNL || !RNL) + return SVal(); + SVal SymSym = + SvalBuilder.makeSymExprValNN(B->getOpcode(), *LNL, *RNL, B->getType()); + return SymSym; + } + SVal VisitExpr(const Expr *E) { return SVal(); } +}; + void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - Expr *LHS = B->getLHS()->IgnoreParens(); Expr *RHS = B->getRHS()->IgnoreParens(); @@ -95,9 +133,19 @@ if (B->getOpcode() == BO_PtrMemD) state = createTemporaryRegionIfNeeded(state, LCtx, LHS); - // Process non-assignments except commas or short-circuited - // logical expressions (LAnd and LOr). - SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType()); + SVal Result; + // Query any constraint that may be directly attached to the BinOp. + SVal LValueSymExpr = + LValueSValBuilder(B->getType(), LCtx, state, svalBuilder).Visit(B); + const llvm::APSInt *KnownInt = + state->getStateManager().getConstraintManager().getSymVal( + state, LValueSymExpr.getAsSymbol()); + if (KnownInt) + Result = svalBuilder.makeIntVal(*KnownInt); + // Fall-back to the constant folding mechanism. + if (Result.isUndef()) + Result = evalBinOp(state, Op, LeftV, RightV, B->getType()); + if (!Result.isUnknown()) { state = state->BindExpr(B, LCtx, Result); } else { diff --git a/clang/test/Analysis/find-binop-constraints.cpp b/clang/test/Analysis/find-binop-constraints.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/find-binop-constraints.cpp @@ -0,0 +1,90 @@ +// RUN: %clang_analyze_cc1 %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=debug.ExprInspection \ +// RUN: -verify + +void clang_analyzer_eval(bool); + +int test_legacy_behavior(int x, int y) { + if (y != 0) + return 0; + if (x + y != 0) + return 0; + clang_analyzer_eval(x + y == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(y == 0); // expected-warning{{TRUE}} + return y / (x + y); // expected-warning{{Division by zero}} +} + +int test_rhs_further_constrained(int x, int y) { + if (x + y != 0) + return 0; + if (y != 0) + return 0; + clang_analyzer_eval(x + y == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(y == 0); // expected-warning{{TRUE}} + return 0; +} + +int test_lhs_further_constrained(int x, int y) { + if (x + y != 0) + return 0; + if (x != 0) + return 0; + clang_analyzer_eval(x + y == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(x == 0); // expected-warning{{TRUE}} + return 0; +} + +int test_lhs_and_rhs_further_constrained(int x, int y) { + if (x % y != 1) + return 0; + if (x != 1) + return 0; + if (y != 1) + return 0; + clang_analyzer_eval(x % y == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(y == 1); // expected-warning{{TRUE}} + return 0; +} + +int test_binop_when_height_is_2_r(int a, int x, int y, int z) { + switch (a) { + case 1: { + if (x + y + z != 0) + return 0; + if (z != 0) + return 0; + clang_analyzer_eval(x + y + z == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(z == 0); // expected-warning{{TRUE}} + break; + } + case 2: { + if (x + y + z != 0) + return 0; + if (y != 0) + return 0; + clang_analyzer_eval(x + y + z == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(y == 0); // expected-warning{{TRUE}} + break; + } + case 3: { + if (x + y + z != 0) + return 0; + if (x != 0) + return 0; + clang_analyzer_eval(x + y + z == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(x == 0); // expected-warning{{TRUE}} + break; + } + case 4: { + if (x + y + z != 0) + return 0; + if (x + y != 0) + return 0; + clang_analyzer_eval(x + y + z == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(x + y == 0); // expected-warning{{TRUE}} + break; + } + } + return 0; +}