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,
|
void CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME,
|
||||||
ExplodedNode *Pred,
|
ExplodedNode *Pred,
|
||||||
ExplodedNodeSet &Dst);
|
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
|
/// evalEagerlyAssume - Given the nodes in 'Src', eagerly assume symbolic
|
||||||
/// expressions of the form 'x != 0' and generate new nodes (stored in Dst)
|
/// 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 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,
|
SValBuilder* createSimpleSValBuilder(llvm::BumpPtrAllocator &alloc,
|
||||||
|
|
|
@ -161,7 +161,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
|
||||||
// analyzing an "open" program.
|
// analyzing an "open" program.
|
||||||
const StackFrameContext *SFC = InitLoc->getCurrentStackFrame();
|
const StackFrameContext *SFC = InitLoc->getCurrentStackFrame();
|
||||||
if (SFC->getParent() == 0) {
|
if (SFC->getParent() == 0) {
|
||||||
loc::MemRegionVal L(getCXXThisRegion(MD, SFC));
|
loc::MemRegionVal L = svalBuilder.getCXXThis(MD, SFC);
|
||||||
SVal V = state->getSVal(L);
|
SVal V = state->getSVal(L);
|
||||||
if (const Loc *LV = dyn_cast<Loc>(&V)) {
|
if (const Loc *LV = dyn_cast<Loc>(&V)) {
|
||||||
state = state->assume(*LV, true);
|
state = state->assume(*LV, true);
|
||||||
|
@ -373,9 +373,8 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init,
|
||||||
cast<StackFrameContext>(Pred->getLocationContext());
|
cast<StackFrameContext>(Pred->getLocationContext());
|
||||||
const CXXConstructorDecl *decl =
|
const CXXConstructorDecl *decl =
|
||||||
cast<CXXConstructorDecl>(stackFrame->getDecl());
|
cast<CXXConstructorDecl>(stackFrame->getDecl());
|
||||||
const CXXThisRegion *thisReg = getCXXThisRegion(decl, stackFrame);
|
SVal thisVal = Pred->getState()->getSVal(svalBuilder.getCXXThis(decl,
|
||||||
|
stackFrame));
|
||||||
SVal thisVal = Pred->getState()->getSVal(thisReg);
|
|
||||||
|
|
||||||
if (BMI->isAnyMemberInitializer()) {
|
if (BMI->isAnyMemberInitializer()) {
|
||||||
// Evaluate the initializer.
|
// Evaluate the initializer.
|
||||||
|
@ -1511,6 +1510,7 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
|
||||||
StmtNodeBuilder Bldr(Pred, TopDst, *currentBuilderContext);
|
StmtNodeBuilder Bldr(Pred, TopDst, *currentBuilderContext);
|
||||||
ExplodedNodeSet Dst;
|
ExplodedNodeSet Dst;
|
||||||
Decl *member = M->getMemberDecl();
|
Decl *member = M->getMemberDecl();
|
||||||
|
|
||||||
if (VarDecl *VD = dyn_cast<VarDecl>(member)) {
|
if (VarDecl *VD = dyn_cast<VarDecl>(member)) {
|
||||||
assert(M->isGLValue());
|
assert(M->isGLValue());
|
||||||
Bldr.takeNodes(Pred);
|
Bldr.takeNodes(Pred);
|
||||||
|
@ -1518,7 +1518,18 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
|
||||||
Bldr.addNodes(Dst);
|
Bldr.addNodes(Dst);
|
||||||
return;
|
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);
|
FieldDecl *field = dyn_cast<FieldDecl>(member);
|
||||||
if (!field) // FIXME: skipping member expressions for non-fields
|
if (!field) // FIXME: skipping member expressions for non-fields
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -21,19 +21,6 @@
|
||||||
using namespace clang;
|
using namespace clang;
|
||||||
using namespace ento;
|
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,
|
void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME,
|
||||||
ExplodedNode *Pred,
|
ExplodedNode *Pred,
|
||||||
ExplodedNodeSet &Dst) {
|
ExplodedNodeSet &Dst) {
|
||||||
|
@ -161,12 +148,10 @@ void ExprEngine::VisitCXXDestructor(const CXXDestructorDecl *DD,
|
||||||
getStackFrame(Pred->getLocationContext(), S,
|
getStackFrame(Pred->getLocationContext(), S,
|
||||||
currentBuilderContext->getBlock(), currentStmtIdx);
|
currentBuilderContext->getBlock(), currentStmtIdx);
|
||||||
|
|
||||||
const CXXThisRegion *ThisR = getCXXThisRegion(DD->getParent(), SFC);
|
|
||||||
|
|
||||||
CallEnter PP(S, SFC, Pred->getLocationContext());
|
CallEnter PP(S, SFC, Pred->getLocationContext());
|
||||||
|
|
||||||
ProgramStateRef state = Pred->getState();
|
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);
|
Bldr.generateNode(PP, Pred, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
|
||||||
// formal arguments.
|
// formal arguments.
|
||||||
const LocationContext *callerCtx = Pred->getLocationContext();
|
const LocationContext *callerCtx = Pred->getLocationContext();
|
||||||
ProgramStateRef state = Pred->getState()->enterStackFrame(callerCtx,
|
ProgramStateRef state = Pred->getState()->enterStackFrame(callerCtx,
|
||||||
calleeCtx);
|
calleeCtx);
|
||||||
|
|
||||||
// Construct a new node and add it to the worklist.
|
// Construct a new node and add it to the worklist.
|
||||||
bool isNew;
|
bool isNew;
|
||||||
|
@ -127,10 +127,10 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
|
||||||
|
|
||||||
// Bind the constructed object value to CXXConstructExpr.
|
// Bind the constructed object value to CXXConstructExpr.
|
||||||
if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) {
|
if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) {
|
||||||
const CXXThisRegion *ThisR =
|
loc::MemRegionVal This =
|
||||||
getCXXThisRegion(CCE->getConstructor()->getParent(), calleeCtx);
|
svalBuilder.getCXXThis(CCE->getConstructor()->getParent(), calleeCtx);
|
||||||
|
SVal ThisV = state->getSVal(This);
|
||||||
|
|
||||||
SVal ThisV = state->getSVal(ThisR);
|
|
||||||
// Always bind the region to the CXXConstructExpr.
|
// Always bind the region to the CXXConstructExpr.
|
||||||
state = state->BindExpr(CCE, CEBNode->getLocationContext(), ThisV);
|
state = state->BindExpr(CCE, CEBNode->getLocationContext(), ThisV);
|
||||||
}
|
}
|
||||||
|
@ -225,35 +225,28 @@ bool ExprEngine::shouldInlineDecl(const Decl *D, ExplodedNode *Pred) {
|
||||||
if (CalleeCFG->getNumBlockIDs() > AMgr.InlineMaxFunctionSize)
|
if (CalleeCFG->getNumBlockIDs() > AMgr.InlineMaxFunctionSize)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
// Do not inline variadic calls (for now).
|
||||||
}
|
if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) {
|
||||||
|
if (BD->isVariadic())
|
||||||
// For now, skip inlining variadic functions.
|
return false;
|
||||||
// We also don't inline blocks.
|
}
|
||||||
static bool shouldInlineCallExpr(const CallExpr *CE, ExprEngine *E) {
|
else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
|
||||||
if (!E->getAnalysisManager().shouldInlineCall())
|
if (FD->isVariadic())
|
||||||
return false;
|
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());
|
|
||||||
}
|
}
|
||||||
// If we have no prototype, assume the function is okay.
|
|
||||||
if (!FT)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Skip inlining of variadic functions.
|
return true;
|
||||||
return !FT->isVariadic();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExprEngine::InlineCall(ExplodedNodeSet &Dst,
|
bool ExprEngine::InlineCall(ExplodedNodeSet &Dst,
|
||||||
const CallExpr *CE,
|
const CallExpr *CE,
|
||||||
ExplodedNode *Pred) {
|
ExplodedNode *Pred) {
|
||||||
if (!shouldInlineCallExpr(CE, this))
|
if (!getAnalysisManager().shouldInlineCall())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// if (!shouldInlineCallExpr(CE, this))
|
||||||
|
// return false;
|
||||||
|
|
||||||
const StackFrameContext *CallerSFC =
|
const StackFrameContext *CallerSFC =
|
||||||
Pred->getLocationContext()->getCurrentStackFrame();
|
Pred->getLocationContext()->getCurrentStackFrame();
|
||||||
|
|
||||||
|
@ -269,8 +262,8 @@ bool ExprEngine::InlineCall(ExplodedNodeSet &Dst,
|
||||||
|
|
||||||
switch (CE->getStmtClass()) {
|
switch (CE->getStmtClass()) {
|
||||||
default:
|
default:
|
||||||
// FIXME: Handle C++.
|
|
||||||
break;
|
break;
|
||||||
|
case Stmt::CXXMemberCallExprClass:
|
||||||
case Stmt::CallExprClass: {
|
case Stmt::CallExprClass: {
|
||||||
D = FD;
|
D = FD;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -2097,8 +2097,19 @@ StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state,
|
||||||
svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)),
|
svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)),
|
||||||
ArgVal);
|
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(),
|
CXXConstructExpr::const_arg_iterator AI = CE->arg_begin(),
|
||||||
AE = CE->arg_end();
|
AE = CE->arg_end();
|
||||||
|
|
||||||
|
@ -2109,8 +2120,10 @@ StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state,
|
||||||
svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)),
|
svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)),
|
||||||
ArgVal);
|
ArgVal);
|
||||||
}
|
}
|
||||||
} else
|
}
|
||||||
|
else {
|
||||||
assert(isa<CXXDestructorDecl>(calleeCtx->getDecl()));
|
assert(isa<CXXDestructorDecl>(calleeCtx->getDecl()));
|
||||||
|
}
|
||||||
|
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "clang/AST/ExprCXX.h"
|
#include "clang/AST/ExprCXX.h"
|
||||||
|
#include "clang/AST/DeclCXX.h"
|
||||||
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
|
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
|
||||||
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
|
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
|
||||||
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
|
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
|
||||||
|
@ -204,6 +205,21 @@ DefinedSVal SValBuilder::getBlockPointer(const BlockDecl *block,
|
||||||
return loc::MemRegionVal(BD);
|
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,
|
SVal SValBuilder::makeSymExprValNN(ProgramStateRef State,
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
// RUN: %clang --analyze -Xclang -analyzer-checker=core,experimental.cplusplus.Iterators -Xclang -verify %s
|
// RUN: %clang --analyze -Xclang -analyzer-checker=core,experimental.cplusplus.Iterators -Xclang -verify %s
|
||||||
// XFAIL: win32
|
// XFAIL: win32
|
||||||
|
|
||||||
|
// FIXME: Does not work with inlined C++ methods.
|
||||||
|
// XFAIL: *
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
void fum(std::vector<int>::iterator t);
|
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