diff options
author | Jordan Rose <jordan_rose@apple.com> | 2012-07-30 23:39:47 +0000 |
---|---|---|
committer | Jordan Rose <jordan_rose@apple.com> | 2012-07-30 23:39:47 +0000 |
commit | 57c033621dacd8720ac9ff65a09025f14f70e22f (patch) | |
tree | f5ddbd4787a29c32786be7b60c4b5130759734d5 /lib/StaticAnalyzer/Core | |
parent | d64effc4e31044c05d6e4400150edb26e914983a (diff) |
[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).
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@161005 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/StaticAnalyzer/Core')
-rw-r--r-- | lib/StaticAnalyzer/Core/CallEvent.cpp | 57 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/CheckerManager.cpp | 40 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 30 |
3 files changed, 100 insertions, 27 deletions
diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index 6a5d0f0fa9..ad5a104aa0 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -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); +} + diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index e1d6a26193..564404e565 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -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); } diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 5e9f3f3089..47d732e515 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -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(); |