[analyzer] Use Clang's evaluation for global constants and default arguments.

Previously, we were handling only simple integer constants for globals and
the smattering of implicitly-valued expressions handled by Environment for
default arguments. Now, we can use any integer constant expression that
Clang can evaluate, in addition to everything we handled before.

PR15094 / <rdar://problem/12830437>

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@175026 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Jordan Rose 2013-02-13 03:11:06 +00:00
parent 04870edbea
commit 38f68ef19c
5 changed files with 92 additions and 23 deletions

View File

@ -37,9 +37,6 @@ static const Expr *ignoreTransparentExprs(const Expr *E) {
case Stmt::SubstNonTypeTemplateParmExprClass:
E = cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement();
break;
case Stmt::CXXDefaultArgExprClass:
E = cast<CXXDefaultArgExpr>(E)->getExpr();
break;
default:
// This is the base case: we can't look through more than we already have.
return E;
@ -75,7 +72,6 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry,
switch (S->getStmtClass()) {
case Stmt::CXXBindTemporaryExprClass:
case Stmt::CXXDefaultArgExprClass:
case Stmt::ExprWithCleanupsClass:
case Stmt::GenericSelectionExprClass:
case Stmt::OpaqueValueExprClass:

View File

@ -639,7 +639,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::StringLiteralClass:
case Stmt::ObjCStringLiteralClass:
case Stmt::CXXBindTemporaryExprClass:
case Stmt::CXXDefaultArgExprClass:
case Stmt::SubstNonTypeTemplateParmExprClass:
case Stmt::CXXNullPtrLiteralExprClass: {
Bldr.takeNodes(Pred);
@ -650,6 +649,39 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
break;
}
case Stmt::CXXDefaultArgExprClass: {
Bldr.takeNodes(Pred);
ExplodedNodeSet PreVisit;
getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
ExplodedNodeSet Tmp;
StmtNodeBuilder Bldr2(PreVisit, Tmp, *currBldrCtx);
const LocationContext *LCtx = Pred->getLocationContext();
const Expr *ArgE = cast<CXXDefaultArgExpr>(S)->getExpr();
// Avoid creating and destroying a lot of APSInts.
SVal V;
llvm::APSInt Result;
for (ExplodedNodeSet::iterator I = PreVisit.begin(), E = PreVisit.end();
I != E; ++I) {
ProgramStateRef State = (*I)->getState();
if (ArgE->EvaluateAsInt(Result, getContext()))
V = svalBuilder.makeIntVal(Result);
else
V = State->getSVal(ArgE, LCtx);
State = State->BindExpr(S, LCtx, V);
Bldr2.generateNode(S, *I, State);
}
getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);
Bldr.addNodes(Dst);
break;
}
case Expr::ObjCArrayLiteralClass:
case Expr::ObjCDictionaryLiteralClass:
// FIXME: explicitly model with a region and the actual contents

View File

@ -1524,11 +1524,14 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B,
QualType CT = Ctx.getCanonicalType(T);
if (CT.isConstQualified()) {
if (const Expr *Init = VD->getInit()) {
if (const IntegerLiteral *IL =
dyn_cast<IntegerLiteral>(Init->IgnoreParenCasts())) {
const nonloc::ConcreteInt &V = svalBuilder.makeIntVal(IL);
return svalBuilder.evalCast(V, Init->getType(), IL->getType());
}
llvm::APSInt Result;
if (Init->EvaluateAsInt(Result, Ctx))
return svalBuilder.makeIntVal(Result);
if (Init->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull))
return svalBuilder.makeNull();
// FIXME: Handle other possible constant expressions.
}
}

View File

@ -67,15 +67,29 @@ int constIntGlob() {
return 3 / *m; // expected-warning {{Division by zero}}
}
extern const int x;
extern const int y;
int constIntGlobExtern() {
if (x == 0) {
if (y == 0) {
foo();
return 5 / x; // expected-warning {{Division by zero}}
return 5 / y; // expected-warning {{Division by zero}}
}
return 0;
}
static void * const ptr = 0;
void constPtrGlob() {
clang_analyzer_eval(ptr == 0); // expected-warning{{TRUE}}
foo();
clang_analyzer_eval(ptr == 0); // expected-warning{{TRUE}}
}
static const int x2 = x;
void constIntGlob2() {
clang_analyzer_eval(x2 == 0); // expected-warning{{TRUE}}
foo();
clang_analyzer_eval(x2 == 0); // expected-warning{{TRUE}}
}
void testAnalyzerEvalIsPure() {
extern int someGlobal;
if (someGlobal == 0) {

View File

@ -216,7 +216,7 @@ namespace DefaultArgs {
class Secret {
public:
static const int value = 42;
static const int value = 40 + 2;
int get(int i = value) {
return i;
}
@ -225,16 +225,40 @@ namespace DefaultArgs {
void testMethod() {
Secret obj;
clang_analyzer_eval(obj.get(1) == 1); // expected-warning{{TRUE}}
// FIXME: Should be 'TRUE'. See PR13673 or <rdar://problem/11720796>.
clang_analyzer_eval(obj.get() == 42); // expected-warning{{UNKNOWN}}
// FIXME: Even if we constrain the variable, we still have a problem.
// See PR13385 or <rdar://problem/12156507>.
if (Secret::value != 42)
return;
clang_analyzer_eval(obj.get() == 42); // expected-warning{{TRUE}}
clang_analyzer_eval(Secret::value == 42); // expected-warning{{TRUE}}
clang_analyzer_eval(obj.get() == 42); // expected-warning{{UNKNOWN}}
}
enum ABC {
A = 0,
B = 1,
C = 2
};
int enumUser(ABC input = B) {
return static_cast<int>(input);
}
void testEnum() {
clang_analyzer_eval(enumUser(C) == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(enumUser() == 1); // expected-warning{{TRUE}}
}
int exprUser(int input = 2 * 4) {
return input;
}
int complicatedExprUser(int input = 2 * Secret::value) {
return input;
}
void testExprs() {
clang_analyzer_eval(exprUser(1) == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(exprUser() == 8); // expected-warning{{TRUE}}
clang_analyzer_eval(complicatedExprUser(1) == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(complicatedExprUser() == 84); // expected-warning{{TRUE}}
}
}