diff options
-rw-r--r-- | include/clang/StaticAnalyzer/Core/CheckerManager.h | 18 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h | 24 | ||||
-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 |
5 files changed, 136 insertions, 33 deletions
diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h index 7e1b07a584..aef9e71297 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -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, diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index 74ac253d99..bc9320f283 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -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 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(); |