forked from OSchip/llvm-project
Implement initial static analysis inlining support for C++ methods.
llvm-svn: 159047
This commit is contained in:
parent
9cb8e9fc89
commit
72b3452c2b
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue