[analyzer] Perform post-call checks for all inlined calls.

Previously, we were only checking the origin expressions of inlined calls.
Checkers using the generic postCall and older postObjCMessage callbacks were
ignored. Now that we have CallEventManager, it is much easier to create
a CallEvent generically when exiting an inlined function, which we can then
use for post-call checks.

No test case because we don't (yet) have any checkers that depend on this
behavior (which is why it hadn't been fixed before now).

llvm-svn: 161005
This commit is contained in:
Jordan Rose 2012-07-30 23:39:47 +00:00
parent 68c2cd059e
commit c2d249ce2c
5 changed files with 136 additions and 33 deletions

View File

@ -211,15 +211,18 @@ public:
void runCheckersForPostObjCMessage(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
const ObjCMethodCall &msg,
ExprEngine &Eng) {
runCheckersForObjCMessage(/*isPreVisit=*/false, Dst, Src, msg, Eng);
ExprEngine &Eng,
bool wasInlined = false) {
runCheckersForObjCMessage(/*isPreVisit=*/false, Dst, Src, msg, Eng,
wasInlined);
}
/// \brief Run checkers for visiting obj-c messages.
void runCheckersForObjCMessage(bool isPreVisit,
ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
const ObjCMethodCall &msg, ExprEngine &Eng);
const ObjCMethodCall &msg, ExprEngine &Eng,
bool wasInlined = false);
/// \brief Run checkers for pre-visiting obj-c messages.
void runCheckersForPreCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src,
@ -229,14 +232,17 @@ public:
/// \brief Run checkers for post-visiting obj-c messages.
void runCheckersForPostCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src,
const CallEvent &Call, ExprEngine &Eng) {
runCheckersForCallEvent(/*isPreVisit=*/false, Dst, Src, Call, Eng);
const CallEvent &Call, ExprEngine &Eng,
bool wasInlined = false) {
runCheckersForCallEvent(/*isPreVisit=*/false, Dst, Src, Call, Eng,
wasInlined);
}
/// \brief Run checkers for visiting obj-c messages.
void runCheckersForCallEvent(bool isPreVisit, ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
const CallEvent &Call, ExprEngine &Eng);
const CallEvent &Call, ExprEngine &Eng,
bool wasInlined = false);
/// \brief Run checkers for load/store of a location.
void runCheckersForLocation(ExplodedNodeSet &Dst,

View File

@ -59,6 +59,13 @@ public:
CallEventRef<T> cloneWithState(ProgramStateRef State) const {
return this->getPtr()->template cloneWithState<T>(State);
}
// Allow implicit conversions to a superclass type, since CallEventRef
// behaves like a pointer-to-const.
template <typename SuperT>
operator CallEventRef<SuperT> () const {
return this->getPtr();
}
};
/// \brief Represents an abstract call to a function or method along a
@ -807,6 +814,11 @@ class CallEventManager {
public:
CallEventManager(llvm::BumpPtrAllocator &alloc) : Alloc(alloc) {}
CallEventRef<>
getCaller(const StackFrameContext *CalleeCtx, ProgramStateRef State);
CallEventRef<SimpleCall>
getSimpleCall(const CallExpr *E, ProgramStateRef State,
const LocationContext *LCtx);
@ -871,4 +883,16 @@ inline void CallEvent::Release() const {
} // end namespace ento
} // end namespace clang
namespace llvm {
// Support isa<>, cast<>, and dyn_cast<> for CallEventRef.
template<class T> struct simplify_type< clang::ento::CallEventRef<T> > {
typedef const T *SimpleType;
static SimpleType
getSimplifiedValue(const clang::ento::CallEventRef<T>& Val) {
return Val.getPtr();
}
};
}
#endif

View File

@ -641,3 +641,60 @@ CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State,
// something we can't reason about.
return create<FunctionCall>(CE, State, LCtx);
}
CallEventRef<>
CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
ProgramStateRef State) {
const LocationContext *ParentCtx = CalleeCtx->getParent();
const LocationContext *CallerCtx = ParentCtx->getCurrentStackFrame();
assert(CallerCtx && "This should not be used for top-level stack frames");
const Stmt *CallSite = CalleeCtx->getCallSite();
if (CallSite) {
if (const CallExpr *CE = dyn_cast<CallExpr>(CallSite))
return getSimpleCall(CE, State, CallerCtx);
switch (CallSite->getStmtClass()) {
case Stmt::CXXConstructExprClass: {
SValBuilder &SVB = State->getStateManager().getSValBuilder();
const CXXMethodDecl *Ctor = cast<CXXMethodDecl>(CalleeCtx->getDecl());
Loc ThisPtr = SVB.getCXXThis(Ctor, CalleeCtx);
SVal ThisVal = State->getSVal(ThisPtr);
return getCXXConstructorCall(cast<CXXConstructExpr>(CallSite),
ThisVal.getAsRegion(), State, CallerCtx);
}
case Stmt::CXXNewExprClass:
return getCXXAllocatorCall(cast<CXXNewExpr>(CallSite), State, CallerCtx);
case Stmt::ObjCMessageExprClass:
return getObjCMethodCall(cast<ObjCMessageExpr>(CallSite),
State, CallerCtx);
default:
llvm_unreachable("This is not an inlineable statement.");
}
}
// Fall back to the CFG. The only thing we haven't handled yet is
// destructors, though this could change in the future.
const CFGBlock *B = CalleeCtx->getCallSiteBlock();
CFGElement E = (*B)[CalleeCtx->getIndex()];
assert(isa<CFGImplicitDtor>(E) && "All other CFG elements should have exprs");
assert(!isa<CFGTemporaryDtor>(E) && "We don't handle temporaries yet");
SValBuilder &SVB = State->getStateManager().getSValBuilder();
const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CalleeCtx->getDecl());
Loc ThisPtr = SVB.getCXXThis(Dtor, CalleeCtx);
SVal ThisVal = State->getSVal(ThisPtr);
const Stmt *Trigger;
if (const CFGAutomaticObjDtor *AutoDtor = dyn_cast<CFGAutomaticObjDtor>(&E))
Trigger = AutoDtor->getTriggerStmt();
else
Trigger = Dtor->getBody();
return getCXXDestructorCall(Dtor, Trigger, ThisVal.getAsRegion(),
State, CallerCtx);
}

View File

@ -140,7 +140,7 @@ namespace {
const CheckersTy &Checkers;
const Stmt *S;
ExprEngine &Eng;
bool wasInlined;
bool WasInlined;
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
@ -148,7 +148,7 @@ namespace {
CheckStmtContext(bool isPreVisit, const CheckersTy &checkers,
const Stmt *s, ExprEngine &eng, bool wasInlined = false)
: IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng),
wasInlined(wasInlined) {}
WasInlined(wasInlined) {}
void runChecker(CheckerManager::CheckStmtFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
@ -157,7 +157,7 @@ namespace {
ProgramPoint::PostStmtKind;
const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
Pred->getLocationContext(), checkFn.Checker);
CheckerContext C(Bldr, Eng, Pred, L, wasInlined);
CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
checkFn(S, C);
}
};
@ -169,16 +169,16 @@ void CheckerManager::runCheckersForStmt(bool isPreVisit,
const ExplodedNodeSet &Src,
const Stmt *S,
ExprEngine &Eng,
bool wasInlined) {
bool WasInlined) {
CheckStmtContext C(isPreVisit, *getCachedStmtCheckersFor(S, isPreVisit),
S, Eng, wasInlined);
S, Eng, WasInlined);
expandGraphWithCheckers(C, Dst, Src);
}
namespace {
struct CheckObjCMessageContext {
typedef std::vector<CheckerManager::CheckObjCMessageFunc> CheckersTy;
bool IsPreVisit;
bool IsPreVisit, WasInlined;
const CheckersTy &Checkers;
const ObjCMethodCall &Msg;
ExprEngine &Eng;
@ -187,13 +187,15 @@ namespace {
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
CheckObjCMessageContext(bool isPreVisit, const CheckersTy &checkers,
const ObjCMethodCall &msg, ExprEngine &eng)
: IsPreVisit(isPreVisit), Checkers(checkers), Msg(msg), Eng(eng) { }
const ObjCMethodCall &msg, ExprEngine &eng,
bool wasInlined)
: IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers),
Msg(msg), Eng(eng) { }
void runChecker(CheckerManager::CheckObjCMessageFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
const ProgramPoint &L = Msg.getProgramPoint(IsPreVisit,checkFn.Checker);
CheckerContext C(Bldr, Eng, Pred, L);
CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
checkFn(*Msg.cloneWithState<ObjCMethodCall>(Pred->getState()), C);
}
@ -205,11 +207,12 @@ void CheckerManager::runCheckersForObjCMessage(bool isPreVisit,
ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
const ObjCMethodCall &msg,
ExprEngine &Eng) {
ExprEngine &Eng,
bool WasInlined) {
CheckObjCMessageContext C(isPreVisit,
isPreVisit ? PreObjCMessageCheckers
: PostObjCMessageCheckers,
msg, Eng);
msg, Eng, WasInlined);
expandGraphWithCheckers(C, Dst, Src);
}
@ -218,7 +221,7 @@ namespace {
// Is there a way we can merge the two?
struct CheckCallContext {
typedef std::vector<CheckerManager::CheckCallFunc> CheckersTy;
bool IsPreVisit;
bool IsPreVisit, WasInlined;
const CheckersTy &Checkers;
const CallEvent &Call;
ExprEngine &Eng;
@ -227,13 +230,15 @@ namespace {
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
CheckCallContext(bool isPreVisit, const CheckersTy &checkers,
const CallEvent &call, ExprEngine &eng)
: IsPreVisit(isPreVisit), Checkers(checkers), Call(call), Eng(eng) { }
const CallEvent &call, ExprEngine &eng,
bool wasInlined)
: IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers),
Call(call), Eng(eng) { }
void runChecker(CheckerManager::CheckCallFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
const ProgramPoint &L = Call.getProgramPoint(IsPreVisit,checkFn.Checker);
CheckerContext C(Bldr, Eng, Pred, L);
CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
checkFn(*Call.cloneWithState(Pred->getState()), C);
}
@ -245,11 +250,12 @@ void CheckerManager::runCheckersForCallEvent(bool isPreVisit,
ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
const CallEvent &Call,
ExprEngine &Eng) {
ExprEngine &Eng,
bool WasInlined) {
CheckCallContext C(isPreVisit,
isPreVisit ? PreCallCheckers
: PostCallCheckers,
Call, Eng);
Call, Eng, WasInlined);
expandGraphWithCheckers(C, Dst, Src);
}

View File

@ -132,7 +132,7 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
if (const ReturnStmt *RS = dyn_cast_or_null<ReturnStmt>(LastSt)) {
const LocationContext *LCtx = CEBNode->getLocationContext();
SVal V = state->getSVal(RS, LCtx);
state = state->BindExpr(CE, calleeCtx->getParent(), V);
state = state->BindExpr(CE, callerCtx, V);
}
// Bind the constructed object value to CXXConstructExpr.
@ -142,7 +142,7 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
SVal ThisV = state->getSVal(This);
// Always bind the region to the CXXConstructExpr.
state = state->BindExpr(CCE, calleeCtx->getParent(), ThisV);
state = state->BindExpr(CCE, callerCtx, ThisV);
}
}
@ -190,19 +190,29 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
// Step 5: Perform the post-condition check of the CallExpr and enqueue the
// result onto the work list.
// CEENode -> Dst -> WorkList
ExplodedNodeSet Dst;
NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), CEENode);
SaveAndRestore<const NodeBuilderContext*> NBCSave(currentBuilderContext,
&Ctx);
SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex());
// FIXME: This needs to call PostCall.
// FIXME: If/when we inline Objective-C messages, this also needs to call
// PostObjCMessage.
if (CE)
getCheckerManager().runCheckersForPostStmt(Dst, CEENode, CE, *this, true);
else
Dst.Add(CEENode);
CallEventManager &CEMgr = getStateManager().getCallEventManager();
CallEventRef<> Call = CEMgr.getCaller(calleeCtx, CEEState);
ExplodedNodeSet DstPostCall;
getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode, *Call,
*this, true);
ExplodedNodeSet Dst;
if (isa<ObjCMethodCall>(Call)) {
getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall,
cast<ObjCMethodCall>(*Call),
*this, true);
} else if (CE) {
getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE,
*this, true);
} else {
Dst.insert(DstPostCall);
}
// Enqueue the next element in the block.
for (ExplodedNodeSet::iterator PSI = Dst.begin(), PSE = Dst.end();