Implement initial static analysis inlining support for C++ methods.

llvm-svn: 159047
This commit is contained in:
Ted Kremenek 2012-06-22 23:55:50 +00:00
parent 9cb8e9fc89
commit 72b3452c2b
9 changed files with 98 additions and 57 deletions

View File

@ -397,13 +397,6 @@ public:
void CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME,
ExplodedNode *Pred,
ExplodedNodeSet &Dst);
/// Synthesize CXXThisRegion.
const CXXThisRegion *getCXXThisRegion(const CXXRecordDecl *RD,
const StackFrameContext *SFC);
const CXXThisRegion *getCXXThisRegion(const CXXMethodDecl *decl,
const StackFrameContext *frameCtx);
/// evalEagerlyAssume - Given the nodes in 'Src', eagerly assume symbolic
/// expressions of the form 'x != 0' and generate new nodes (stored in Dst)

View File

@ -310,6 +310,13 @@ public:
return loc::ConcreteInt(BasicVals.getValue(integer));
}
/// Return a memory region for the 'this' object reference.
loc::MemRegionVal getCXXThis(const CXXMethodDecl *D,
const StackFrameContext *SFC);
/// Return a memory region for the 'this' object reference.
loc::MemRegionVal getCXXThis(const CXXRecordDecl *D,
const StackFrameContext *SFC);
};
SValBuilder* createSimpleSValBuilder(llvm::BumpPtrAllocator &alloc,

View File

@ -161,7 +161,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
// analyzing an "open" program.
const StackFrameContext *SFC = InitLoc->getCurrentStackFrame();
if (SFC->getParent() == 0) {
loc::MemRegionVal L(getCXXThisRegion(MD, SFC));
loc::MemRegionVal L = svalBuilder.getCXXThis(MD, SFC);
SVal V = state->getSVal(L);
if (const Loc *LV = dyn_cast<Loc>(&V)) {
state = state->assume(*LV, true);
@ -373,9 +373,8 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init,
cast<StackFrameContext>(Pred->getLocationContext());
const CXXConstructorDecl *decl =
cast<CXXConstructorDecl>(stackFrame->getDecl());
const CXXThisRegion *thisReg = getCXXThisRegion(decl, stackFrame);
SVal thisVal = Pred->getState()->getSVal(thisReg);
SVal thisVal = Pred->getState()->getSVal(svalBuilder.getCXXThis(decl,
stackFrame));
if (BMI->isAnyMemberInitializer()) {
// Evaluate the initializer.
@ -1511,6 +1510,7 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
StmtNodeBuilder Bldr(Pred, TopDst, *currentBuilderContext);
ExplodedNodeSet Dst;
Decl *member = M->getMemberDecl();
if (VarDecl *VD = dyn_cast<VarDecl>(member)) {
assert(M->isGLValue());
Bldr.takeNodes(Pred);
@ -1518,7 +1518,18 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
Bldr.addNodes(Dst);
return;
}
// Handle C++ method calls.
if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(member)) {
Bldr.takeNodes(Pred);
SVal MDVal = svalBuilder.getFunctionPointer(MD);
ProgramStateRef state =
Pred->getState()->BindExpr(M, Pred->getLocationContext(), MDVal);
Bldr.generateNode(M, Pred, state);
return;
}
FieldDecl *field = dyn_cast<FieldDecl>(member);
if (!field) // FIXME: skipping member expressions for non-fields
return;

View File

@ -21,19 +21,6 @@
using namespace clang;
using namespace ento;
const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXRecordDecl *D,
const StackFrameContext *SFC) {
const Type *T = D->getTypeForDecl();
QualType PT = getContext().getPointerType(QualType(T, 0));
return svalBuilder.getRegionManager().getCXXThisRegion(PT, SFC);
}
const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXMethodDecl *decl,
const StackFrameContext *frameCtx) {
return svalBuilder.getRegionManager().
getCXXThisRegion(decl->getThisType(getContext()), frameCtx);
}
void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
@ -161,12 +148,10 @@ void ExprEngine::VisitCXXDestructor(const CXXDestructorDecl *DD,
getStackFrame(Pred->getLocationContext(), S,
currentBuilderContext->getBlock(), currentStmtIdx);
const CXXThisRegion *ThisR = getCXXThisRegion(DD->getParent(), SFC);
CallEnter PP(S, SFC, Pred->getLocationContext());
ProgramStateRef state = Pred->getState();
state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest));
state = state->bindLoc(svalBuilder.getCXXThis(DD->getParent(), SFC),
loc::MemRegionVal(Dest));
Bldr.generateNode(PP, Pred, state);
}

View File

@ -41,7 +41,7 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
// formal arguments.
const LocationContext *callerCtx = Pred->getLocationContext();
ProgramStateRef state = Pred->getState()->enterStackFrame(callerCtx,
calleeCtx);
calleeCtx);
// Construct a new node and add it to the worklist.
bool isNew;
@ -127,10 +127,10 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
// Bind the constructed object value to CXXConstructExpr.
if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) {
const CXXThisRegion *ThisR =
getCXXThisRegion(CCE->getConstructor()->getParent(), calleeCtx);
loc::MemRegionVal This =
svalBuilder.getCXXThis(CCE->getConstructor()->getParent(), calleeCtx);
SVal ThisV = state->getSVal(This);
SVal ThisV = state->getSVal(ThisR);
// Always bind the region to the CXXConstructExpr.
state = state->BindExpr(CCE, CEBNode->getLocationContext(), ThisV);
}
@ -225,35 +225,28 @@ bool ExprEngine::shouldInlineDecl(const Decl *D, ExplodedNode *Pred) {
if (CalleeCFG->getNumBlockIDs() > AMgr.InlineMaxFunctionSize)
return false;
return true;
}
// For now, skip inlining variadic functions.
// We also don't inline blocks.
static bool shouldInlineCallExpr(const CallExpr *CE, ExprEngine *E) {
if (!E->getAnalysisManager().shouldInlineCall())
return false;
QualType callee = CE->getCallee()->getType();
const FunctionProtoType *FT = 0;
if (const PointerType *PT = callee->getAs<PointerType>())
FT = dyn_cast<FunctionProtoType>(PT->getPointeeType());
else if (const BlockPointerType *BT = callee->getAs<BlockPointerType>()) {
FT = dyn_cast<FunctionProtoType>(BT->getPointeeType());
// Do not inline variadic calls (for now).
if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) {
if (BD->isVariadic())
return false;
}
else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
if (FD->isVariadic())
return false;
}
// If we have no prototype, assume the function is okay.
if (!FT)
return true;
// Skip inlining of variadic functions.
return !FT->isVariadic();
return true;
}
bool ExprEngine::InlineCall(ExplodedNodeSet &Dst,
const CallExpr *CE,
ExplodedNode *Pred) {
if (!shouldInlineCallExpr(CE, this))
if (!getAnalysisManager().shouldInlineCall())
return false;
// if (!shouldInlineCallExpr(CE, this))
// return false;
const StackFrameContext *CallerSFC =
Pred->getLocationContext()->getCurrentStackFrame();
@ -269,8 +262,8 @@ bool ExprEngine::InlineCall(ExplodedNodeSet &Dst,
switch (CE->getStmtClass()) {
default:
// FIXME: Handle C++.
break;
case Stmt::CXXMemberCallExprClass:
case Stmt::CallExprClass: {
D = FD;
break;

View File

@ -2097,8 +2097,19 @@ StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state,
svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)),
ArgVal);
}
} else if (const CXXConstructExpr *CE =
dyn_cast<CXXConstructExpr>(calleeCtx->getCallSite())) {
// For C++ method calls, also include the 'this' pointer.
if (const CXXMemberCallExpr *CME = dyn_cast<CXXMemberCallExpr>(CE)) {
loc::MemRegionVal This =
svalBuilder.getCXXThis(cast<CXXMethodDecl>(CME->getCalleeDecl()),
calleeCtx);
SVal CalledObj = state->getSVal(CME->getImplicitObjectArgument(),
callerCtx);
store = Bind(store.getStore(), This, CalledObj);
}
}
else if (const CXXConstructExpr *CE =
dyn_cast<CXXConstructExpr>(calleeCtx->getCallSite())) {
CXXConstructExpr::const_arg_iterator AI = CE->arg_begin(),
AE = CE->arg_end();
@ -2109,8 +2120,10 @@ StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state,
svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)),
ArgVal);
}
} else
}
else {
assert(isa<CXXDestructorDecl>(calleeCtx->getDecl()));
}
return store;
}

View File

@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "clang/AST/ExprCXX.h"
#include "clang/AST/DeclCXX.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
@ -204,6 +205,21 @@ DefinedSVal SValBuilder::getBlockPointer(const BlockDecl *block,
return loc::MemRegionVal(BD);
}
/// Return a memory region for the 'this' object reference.
loc::MemRegionVal SValBuilder::getCXXThis(const CXXMethodDecl *D,
const StackFrameContext *SFC) {
return loc::MemRegionVal(getRegionManager().
getCXXThisRegion(D->getThisType(getContext()), SFC));
}
/// Return a memory region for the 'this' object reference.
loc::MemRegionVal SValBuilder::getCXXThis(const CXXRecordDecl *D,
const StackFrameContext *SFC) {
const Type *T = D->getTypeForDecl();
QualType PT = getContext().getPointerType(QualType(T, 0));
return loc::MemRegionVal(getRegionManager().getCXXThisRegion(PT, SFC));
}
//===----------------------------------------------------------------------===//
SVal SValBuilder::makeSymExprValNN(ProgramStateRef State,

View File

@ -1,6 +1,9 @@
// RUN: %clang --analyze -Xclang -analyzer-checker=core,experimental.cplusplus.Iterators -Xclang -verify %s
// XFAIL: win32
// FIXME: Does not work with inlined C++ methods.
// XFAIL: *
#include <vector>
void fum(std::vector<int>::iterator t);

View File

@ -592,3 +592,23 @@ void rdar11401827() {
}
}
//===---------------------------------------------------------------------===//
// Handle inlining of C++ method calls.
//===---------------------------------------------------------------------===//
struct A {
int *p;
void foo(int *q) {
p = q;
}
void bar() {
*p = 0; // expected-warning {{null pointer}}
}
};
void test_inline() {
A a;
a.foo(0);
a.bar();
}