Index: lib/StaticAnalyzer/Core/LoopUnrolling.cpp =================================================================== --- lib/StaticAnalyzer/Core/LoopUnrolling.cpp +++ lib/StaticAnalyzer/Core/LoopUnrolling.cpp @@ -12,11 +12,11 @@ /// //===----------------------------------------------------------------------===// -#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h" using namespace clang; using namespace ento; @@ -80,13 +80,14 @@ } static internal::Matcher simpleCondition(StringRef BindName) { - return binaryOperator(anyOf(hasOperatorName("<"), hasOperatorName(">"), - hasOperatorName("<="), hasOperatorName(">="), - hasOperatorName("!=")), - hasEitherOperand(ignoringParenImpCasts(declRefExpr( - to(varDecl(hasType(isInteger())).bind(BindName))))), - hasEitherOperand(ignoringParenImpCasts( - integerLiteral().bind("boundNum")))) + return binaryOperator( + anyOf(hasOperatorName("<"), hasOperatorName(">"), + hasOperatorName("<="), hasOperatorName(">="), + hasOperatorName("!=")), + hasEitherOperand(ignoringParenImpCasts( + declRefExpr(to(varDecl(allOf(hasType(isInteger()), + equalsBoundNode(BindName))))) + .bind("boundVarOperand")))) .bind("conditionOperator"); } @@ -138,17 +139,17 @@ static internal::Matcher forLoopMatcher() { return forStmt( - hasCondition(simpleCondition("initVarName")), // Initialization should match the form: 'int i = 6' or 'i = 42'. - hasLoopInit( - anyOf(declStmt(hasSingleDecl( - varDecl(allOf(hasInitializer(ignoringParenImpCasts( - integerLiteral().bind("initNum"))), - equalsBoundNode("initVarName"))))), - binaryOperator(hasLHS(declRefExpr(to(varDecl( - equalsBoundNode("initVarName"))))), - hasRHS(ignoringParenImpCasts( - integerLiteral().bind("initNum")))))), + hasLoopInit(anyOf( + declStmt(hasSingleDecl( + varDecl(hasInitializer(ignoringParenImpCasts( + integerLiteral().bind("initNum")))) + .bind("initVarName"))), + binaryOperator( + hasLHS(declRefExpr(to(varDecl().bind("initVarName")))), + hasRHS(ignoringParenImpCasts( + integerLiteral().bind("initNum")))))), + hasCondition(simpleCondition("initVarName")), // Incrementation should be a simple increment or decrement // operator call. hasIncrement(unaryOperator( @@ -156,7 +157,8 @@ hasUnaryOperand(declRefExpr( to(varDecl(allOf(equalsBoundNode("initVarName"), hasType(isInteger())))))))), - unless(hasBody(hasSuspiciousStmt("initVarName")))).bind("forLoop"); + unless(hasBody(hasSuspiciousStmt("initVarName")))) + .bind("forLoop"); } static bool isPossiblyEscaped(const VarDecl *VD, ExplodedNode *N) { @@ -196,22 +198,43 @@ bool shouldCompletelyUnroll(const Stmt *LoopStmt, ASTContext &ASTCtx, ExplodedNode *Pred, unsigned &maxStep) { - if (!isLoopStmt(LoopStmt)) return false; - // TODO: Match the cases where the bound is not a concrete literal but an - // integer with known value auto Matches = match(forLoopMatcher(), *LoopStmt, ASTCtx); if (Matches.empty()) return false; + const auto State = Pred->getState(); + auto &SVB = State->getStateManager().getSValBuilder(); + auto CounterVar = Matches[0].getNodeAs("initVarName"); - llvm::APInt BoundNum = - Matches[0].getNodeAs("boundNum")->getValue(); llvm::APInt InitNum = Matches[0].getNodeAs("initNum")->getValue(); auto CondOp = Matches[0].getNodeAs("conditionOperator"); + + const Expr *BoundExpr = CondOp->getLHS()->IgnoreParenImpCasts(); + if (BoundExpr == Matches[0].getNodeAs("boundVarOperand")) + BoundExpr = CondOp->getRHS()->IgnoreParenImpCasts(); + SVal BoundNumVal = Pred->getSVal(BoundExpr); + + // If the value of the expression is unknown and it is a declaration + // reference then try to get the value of the declaration instead + if (BoundNumVal.isUnknown()) { + if (const auto *BoundDeclRefExpr = dyn_cast(BoundExpr)) { + // FIXME: Add other declarations such as Objective-C fields + if (const auto *BoundVarDecl = + dyn_cast(BoundDeclRefExpr->getDecl())) { + BoundNumVal = State->getSVal( + State->getLValue(BoundVarDecl, Pred->getLocationContext())); + } + } + } + const llvm::APSInt *BoundNumPtr = SVB.getKnownValue(State, BoundNumVal); + if (!BoundNumPtr) + return false; + llvm::APInt BoundNum = *BoundNumPtr; + if (InitNum.getBitWidth() != BoundNum.getBitWidth()) { InitNum = InitNum.zextOrSelf(BoundNum.getBitWidth()); BoundNum = BoundNum.zextOrSelf(InitNum.getBitWidth()); @@ -289,5 +312,5 @@ return false; return true; } -} -} +} // namespace ento +} // namespace clang Index: test/Analysis/loop-unrolling.cpp =================================================================== --- test/Analysis/loop-unrolling.cpp +++ test/Analysis/loop-unrolling.cpp @@ -499,3 +499,27 @@ clang_analyzer_numTimesReached(); // expected-warning {{6}} } } + +int unroll_known_value_of_variable1() { + int a[9]; + int k = 42; + int n = 9; + for (int i = 0; i < n; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{9}} + a[i] = 42; + } + int b = 22 / (k - 42); // expected-warning {{Division by zero}} + return 0; +} + +int unroll_known_value_of_variable2() { + int a[9]; + int k = 42; + int n = 9; + for (int i = 0; n > i; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{9}} + a[i] = 42; + } + int b = 22 / (k - 42); // expected-warning {{Division by zero}} + return 0; +}