From: Jordan Rose Date: Mon, 30 Jul 2012 20:22:09 +0000 (+0000) Subject: [analyzer] Only allow CallEvents to be created by CallEventManager. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d563d3fb73879df7147b8a5302c3bf0e1402ba18;p=clang [analyzer] Only allow CallEvents to be created by CallEventManager. This ensures that it is valid to reference-count any CallEvents, and we won't accidentally try to reclaim a CallEvent that lives on the stack. It also hides an ugly switch statement for handling CallExprs! There should be no functionality change here. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@160986 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index d121baf83f..97612d60f1 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -48,8 +48,18 @@ enum CallEventKind { }; class CallEvent; -typedef IntrusiveRefCntPtr CallEventRef; +class CallEventManager; +template +class CallEventRef : public IntrusiveRefCntPtr { +public: + CallEventRef(const T *Call) : IntrusiveRefCntPtr(Call) {} + CallEventRef(const CallEventRef &Orig) : IntrusiveRefCntPtr(Orig) {} + + CallEventRef cloneWithState(ProgramStateRef State) const { + return this->getPtr()->template cloneWithState(State); + } +}; /// \brief Represents an abstract call to a function or method along a /// particular path. @@ -89,6 +99,8 @@ private: void Release() const; protected: + friend class CallEventManager; + CallEvent(const Expr *E, ProgramStateRef state, const LocationContext *lctx) : State(state), LCtx(lctx), Origin(E), RefCount(0) {} @@ -100,6 +112,7 @@ protected: : State(Original.State), LCtx(Original.LCtx), Origin(Original.Origin), Data(Original.Data), Location(Original.Location), RefCount(0) {} + ProgramStateRef getState() const { return State; } @@ -229,10 +242,10 @@ public: /// Returns a copy of this CallEvent, but using the given state. template - IntrusiveRefCntPtr cloneWithState(ProgramStateRef NewState) const; + CallEventRef cloneWithState(ProgramStateRef NewState) const; /// Returns a copy of this CallEvent, but using the given state. - CallEventRef cloneWithState(ProgramStateRef NewState) const { + CallEventRef<> cloneWithState(ProgramStateRef NewState) const { return cloneWithState(NewState); } @@ -364,15 +377,17 @@ public: /// /// Example: \c fun() class FunctionCall : public SimpleCall { -protected: - FunctionCall(const FunctionCall &Other) : SimpleCall(Other) {} - virtual void cloneTo(void *Dest) const { new (Dest) FunctionCall(*this); } + friend class CallEventManager; -public: +protected: FunctionCall(const CallExpr *CE, ProgramStateRef St, const LocationContext *LCtx) : SimpleCall(CE, St, LCtx) {} + FunctionCall(const FunctionCall &Other) : SimpleCall(Other) {} + virtual void cloneTo(void *Dest) const { new (Dest) FunctionCall(*this); } + +public: virtual Kind getKind() const { return CE_Function; } static bool classof(const CallEvent *CA) { @@ -405,15 +420,17 @@ public: /// /// Example: \c obj.fun() class CXXMemberCall : public CXXInstanceCall { -protected: - CXXMemberCall(const CXXMemberCall &Other) : CXXInstanceCall(Other) {} - virtual void cloneTo(void *Dest) const { new (Dest) CXXMemberCall(*this); } + friend class CallEventManager; -public: +protected: CXXMemberCall(const CXXMemberCallExpr *CE, ProgramStateRef St, const LocationContext *LCtx) : CXXInstanceCall(CE, St, LCtx) {} + CXXMemberCall(const CXXMemberCall &Other) : CXXInstanceCall(Other) {} + virtual void cloneTo(void *Dest) const { new (Dest) CXXMemberCall(*this); } + +public: virtual const CXXMemberCallExpr *getOriginExpr() const { return cast(SimpleCall::getOriginExpr()); } @@ -432,7 +449,13 @@ public: /// /// Example: iter + 1 class CXXMemberOperatorCall : public CXXInstanceCall { + friend class CallEventManager; + protected: + CXXMemberOperatorCall(const CXXOperatorCallExpr *CE, ProgramStateRef St, + const LocationContext *LCtx) + : CXXInstanceCall(CE, St, LCtx) {} + CXXMemberOperatorCall(const CXXMemberOperatorCall &Other) : CXXInstanceCall(Other) {} virtual void cloneTo(void *Dest) const { @@ -440,10 +463,6 @@ protected: } public: - CXXMemberOperatorCall(const CXXOperatorCallExpr *CE, ProgramStateRef St, - const LocationContext *LCtx) - : CXXInstanceCall(CE, St, LCtx) {} - virtual const CXXOperatorCallExpr *getOriginExpr() const { return cast(SimpleCall::getOriginExpr()); } @@ -468,7 +487,13 @@ public: /// /// Example: ^{ /* ... */ }() class BlockCall : public SimpleCall { + friend class CallEventManager; + protected: + BlockCall(const CallExpr *CE, ProgramStateRef St, + const LocationContext *LCtx) + : SimpleCall(CE, St, LCtx) {} + BlockCall(const BlockCall &Other) : SimpleCall(Other) {} virtual void cloneTo(void *Dest) const { new (Dest) BlockCall(*this); } @@ -477,10 +502,6 @@ protected: virtual QualType getDeclaredResultType() const; public: - BlockCall(const CallExpr *CE, ProgramStateRef St, - const LocationContext *LCtx) - : SimpleCall(CE, St, LCtx) {} - /// \brief Returns the region associated with this instance of the block. /// /// This may be NULL if the block's origin is unknown. @@ -515,27 +536,28 @@ public: /// /// Example: \c T(1) class CXXConstructorCall : public AnyFunctionCall { -protected: - CXXConstructorCall(const CXXConstructorCall &Other) : AnyFunctionCall(Other){} - virtual void cloneTo(void *Dest) const { new (Dest) CXXConstructorCall(*this); } - - virtual void getExtraInvalidatedRegions(RegionList &Regions) const; - -public: - /// Represents a constructor call to a new or unknown region. - CXXConstructorCall(const CXXConstructExpr *CE, ProgramStateRef St, - const LocationContext *LCtx) - : AnyFunctionCall(CE, St, LCtx) { - Data = 0; - } + friend class CallEventManager; - /// Represents a constructor call on an existing object region. +protected: + /// Creates a constructor call. + /// + /// \param CE The constructor expression as written in the source. + /// \param Target The region where the object should be constructed. If NULL, + /// a new symbolic region will be used. + /// \param St The path-sensitive state at this point in the program. + /// \param LCtx The location context at this point in the program. CXXConstructorCall(const CXXConstructExpr *CE, const MemRegion *target, ProgramStateRef St, const LocationContext *LCtx) : AnyFunctionCall(CE, St, LCtx) { Data = target; } + CXXConstructorCall(const CXXConstructorCall &Other) : AnyFunctionCall(Other){} + virtual void cloneTo(void *Dest) const { new (Dest) CXXConstructorCall(*this); } + + virtual void getExtraInvalidatedRegions(RegionList &Regions) const; + +public: virtual const CXXConstructExpr *getOriginExpr() const { return cast(AnyFunctionCall::getOriginExpr()); } @@ -564,13 +586,9 @@ public: /// This can occur at the end of a scope (for automatic objects), at the end /// of a full-expression (for temporaries), or as part of a delete. class CXXDestructorCall : public AnyFunctionCall { -protected: - CXXDestructorCall(const CXXDestructorCall &Other) : AnyFunctionCall(Other) {} - virtual void cloneTo(void *Dest) const { new (Dest) CXXDestructorCall(*this); } - - virtual void getExtraInvalidatedRegions(RegionList &Regions) const; + friend class CallEventManager; -public: +protected: /// Creates an implicit destructor. /// /// \param DD The destructor that will be called. @@ -586,6 +604,12 @@ public: Location = Trigger->getLocEnd(); } + CXXDestructorCall(const CXXDestructorCall &Other) : AnyFunctionCall(Other) {} + virtual void cloneTo(void *Dest) const { new (Dest) CXXDestructorCall(*this); } + + virtual void getExtraInvalidatedRegions(RegionList &Regions) const; + +public: virtual SourceRange getSourceRange() const { return Location; } virtual unsigned getNumArgs() const { return 0; } @@ -603,15 +627,17 @@ public: /// /// This is a call to "operator new". class CXXAllocatorCall : public AnyFunctionCall { -protected: - CXXAllocatorCall(const CXXAllocatorCall &Other) : AnyFunctionCall(Other) {} - virtual void cloneTo(void *Dest) const { new (Dest) CXXAllocatorCall(*this); } + friend class CallEventManager; -public: +protected: CXXAllocatorCall(const CXXNewExpr *E, ProgramStateRef St, const LocationContext *LCtx) : AnyFunctionCall(E, St, LCtx) {} + CXXAllocatorCall(const CXXAllocatorCall &Other) : AnyFunctionCall(Other) {} + virtual void cloneTo(void *Dest) const { new (Dest) CXXAllocatorCall(*this); } + +public: virtual const CXXNewExpr *getOriginExpr() const { return cast(AnyFunctionCall::getOriginExpr()); } @@ -652,9 +678,17 @@ enum ObjCMessageKind { /// /// This includes all of the kinds listed in ObjCMessageKind. class ObjCMethodCall : public CallEvent { + friend class CallEventManager; + const PseudoObjectExpr *getContainingPseudoObjectExpr() const; protected: + ObjCMethodCall(const ObjCMessageExpr *Msg, ProgramStateRef St, + const LocationContext *LCtx) + : CallEvent(Msg, St, LCtx) { + Data = 0; + } + ObjCMethodCall(const ObjCMethodCall &Other) : CallEvent(Other) {} virtual void cloneTo(void *Dest) const { new (Dest) ObjCMethodCall(*this); } @@ -665,12 +699,6 @@ protected: ObjCInterfaceDecl *ClassDecl) const; public: - ObjCMethodCall(const ObjCMessageExpr *Msg, ProgramStateRef St, - const LocationContext *LCtx) - : CallEvent(Msg, St, LCtx) { - Data = 0; - } - virtual const ObjCMessageExpr *getOriginExpr() const { return cast(CallEvent::getOriginExpr()); } @@ -763,7 +791,7 @@ class CallEventManager { friend class CallEvent; llvm::BumpPtrAllocator &Alloc; - SmallVector Cache; + SmallVector Cache; void reclaim(const void *Memory) { Cache.push_back(const_cast(Memory)); @@ -777,16 +805,64 @@ class CallEventManager { return Cache.pop_back_val(); } + template + T *create(Arg A, ProgramStateRef St, const LocationContext *LCtx) { + return new (allocate()) T(A, St, LCtx); + } + + template + T *create(Arg1 A1, Arg2 A2, ProgramStateRef St, const LocationContext *LCtx) { + return new (allocate()) T(A1, A2, St, LCtx); + } + + template + T *create(Arg1 A1, Arg2 A2, Arg3 A3, ProgramStateRef St, + const LocationContext *LCtx) { + return new (allocate()) T(A1, A2, A3, St, LCtx); + } + public: CallEventManager(llvm::BumpPtrAllocator &alloc) : Alloc(alloc) {} + + CallEventRef + getSimpleCall(const CallExpr *E, ProgramStateRef State, + const LocationContext *LCtx); + + CallEventRef + getObjCMethodCall(const ObjCMessageExpr *E, ProgramStateRef State, + const LocationContext *LCtx) { + return create(E, State, LCtx); + } + + CallEventRef + getCXXConstructorCall(const CXXConstructExpr *E, const MemRegion *Target, + ProgramStateRef State, const LocationContext *LCtx) { + return create(E, Target, State, LCtx); + } + + CallEventRef + getCXXDestructorCall(const CXXDestructorDecl *DD, const Stmt *Trigger, + const MemRegion *Target, ProgramStateRef State, + const LocationContext *LCtx) { + return create(DD, Trigger, Target, State, LCtx); + } + + CallEventRef + getCXXAllocatorCall(const CXXNewExpr *E, ProgramStateRef State, + const LocationContext *LCtx) { + return create(E, State, LCtx); + } }; template -IntrusiveRefCntPtr CallEvent::cloneWithState(ProgramStateRef NewState) const{ +CallEventRef CallEvent::cloneWithState(ProgramStateRef NewState) const { assert(isa(*this) && "Cloning to unrelated type"); assert(sizeof(T) == sizeof(CallEvent) && "Subclasses may not add fields"); + if (NewState == State) + return cast(this); + CallEventManager &Mgr = State->getStateManager().getCallEventManager(); T *Copy = static_cast(Mgr.allocate()); cloneTo(Copy); diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 638c1e6714..5a82b1fe13 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -43,7 +43,6 @@ namespace ento { class AnalysisManager; class CallEvent; class SimpleCall; -class ObjCMethodCall; class ExprEngine : public SubEngine { AnalysisManager &AMgr; @@ -348,7 +347,7 @@ public: void VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, ExplodedNode *Pred, ExplodedNodeSet &Dst); - void VisitObjCMessage(const ObjCMethodCall &Msg, ExplodedNode *Pred, + void VisitObjCMessage(const ObjCMessageExpr *ME, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitReturnStmt - Transfer function logic for return statements. diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index 488549c343..53656cf6a0 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -1975,8 +1975,11 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, } else { assert(isa(S)); - ObjCMethodCall Call(cast(S), CurrSt, LCtx); - switch (Call.getMessageKind()) { + CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager(); + CallEventRef Call + = Mgr.getObjCMethodCall(cast(S), CurrSt, LCtx); + + switch (Call->getMessageKind()) { case OCM_Message: os << "Method"; break; diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index 2375784681..ab4b3832ee 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -620,3 +620,24 @@ ObjCMethodDecl *ObjCMethodCall::LookupClassMethodDefinition(Selector Sel, return Method; } + +CallEventRef +CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State, + const LocationContext *LCtx) { + if (const CXXMemberCallExpr *MCE = dyn_cast(CE)) + return create(MCE, State, LCtx); + + if (const CXXOperatorCallExpr *OpCE = dyn_cast(CE)) { + const FunctionDecl *DirectCallee = OpCE->getDirectCallee(); + if (const CXXMethodDecl *MD = dyn_cast(DirectCallee)) + if (MD->isInstance()) + return create(OpCE, State, LCtx); + + } else if (CE->getCallee()->getType()->isBlockPointerType()) { + return create(CE, State, LCtx); + } + + // Otherwise, it's a normal function call, static member function call, or + // something we can't reason about. + return create(CE, State, LCtx); +} diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 5e3ce7a72d..e1d6a26193 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -192,15 +192,10 @@ namespace { void runChecker(CheckerManager::CheckObjCMessageFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { - ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind : - ProgramPoint::PostStmtKind; - const ProgramPoint &L = - ProgramPoint::getProgramPoint(Msg.getOriginExpr(), - K, Pred->getLocationContext(), - checkFn.Checker); + const ProgramPoint &L = Msg.getProgramPoint(IsPreVisit,checkFn.Checker); CheckerContext C(Bldr, Eng, Pred, L); - checkFn(Msg, C); + checkFn(*Msg.cloneWithState(Pred->getState()), C); } }; } @@ -237,10 +232,10 @@ namespace { void runChecker(CheckerManager::CheckCallFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { - const ProgramPoint &L = Call.getProgramPoint(IsPreVisit, checkFn.Checker); + const ProgramPoint &L = Call.getProgramPoint(IsPreVisit,checkFn.Checker); CheckerContext C(Bldr, Eng, Pred, L); - checkFn(Call, C); + checkFn(*Call.cloneWithState(Pred->getState()), C); } }; } diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 7db519c578..a1cd23dc6b 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -875,15 +875,11 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; - case Stmt::ObjCMessageExprClass: { + case Stmt::ObjCMessageExprClass: Bldr.takeNodes(Pred); - ObjCMethodCall Call(cast(S), - Pred->getState(), - Pred->getLocationContext()); - VisitObjCMessage(Call, Pred, Dst); + VisitObjCMessage(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; - } case Stmt::ObjCAtThrowStmtClass: { // FIXME: This is not complete. We basically treat @throw as diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 3072234e0c..d0a0e32f74 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -125,23 +125,25 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, } } - CXXConstructorCall Call(CE, Target, State, LCtx); + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef Call = + CEMgr.getCXXConstructorCall(CE, Target, State, LCtx); ExplodedNodeSet DstPreVisit; getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this); ExplodedNodeSet DstPreCall; getCheckerManager().runCheckersForPreCall(DstPreCall, DstPreVisit, - Call, *this); + *Call, *this); ExplodedNodeSet DstInvalidated; StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currentBuilderContext); for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); I != E; ++I) - defaultEvalCall(Bldr, *I, Call); + defaultEvalCall(Bldr, *I, *Call); ExplodedNodeSet DstPostCall; getCheckerManager().runCheckersForPostCall(DstPostCall, DstInvalidated, - Call, *this); + *Call, *this); getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this); } @@ -150,36 +152,39 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + const LocationContext *LCtx = Pred->getLocationContext(); + ProgramStateRef State = Pred->getState(); + // FIXME: We need to run the same destructor on every element of the array. // This workaround will just run the first destructor (which will still // invalidate the entire array). if (const ArrayType *AT = getContext().getAsArrayType(ObjectType)) { ObjectType = AT->getElementType(); - Dest = Pred->getState()->getLValue(ObjectType, - getSValBuilder().makeZeroArrayIndex(), - loc::MemRegionVal(Dest)).getAsRegion(); + Dest = State->getLValue(ObjectType, getSValBuilder().makeZeroArrayIndex(), + loc::MemRegionVal(Dest)).getAsRegion(); } const CXXRecordDecl *RecordDecl = ObjectType->getAsCXXRecordDecl(); assert(RecordDecl && "Only CXXRecordDecls should have destructors"); const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor(); - CXXDestructorCall Call(DtorDecl, S, Dest, Pred->getState(), - Pred->getLocationContext()); + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef Call = + CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, State, LCtx); ExplodedNodeSet DstPreCall; getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, - Call, *this); + *Call, *this); ExplodedNodeSet DstInvalidated; StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currentBuilderContext); for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); I != E; ++I) - defaultEvalCall(Bldr, *I, Call); + defaultEvalCall(Bldr, *I, *Call); ExplodedNodeSet DstPostCall; getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated, - Call, *this); + *Call, *this); } void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, @@ -196,9 +201,14 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, svalBuilder.getConjuredSymbolVal(0, CNE, LCtx, CNE->getType(), blockCount); ProgramStateRef State = Pred->getState(); + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef Call = + CEMgr.getCXXAllocatorCall(CNE, State, LCtx); + // Invalidate placement args. - CXXAllocatorCall Call(CNE, State, LCtx); - State = Call.invalidateRegions(blockCount); + // FIXME: Once we figure out how we want allocators to work, + // we should be using the usual pre-/(default-)eval-/post-call checks here. + State = Call->invalidateRegions(blockCount); if (CNE->isArray()) { // FIXME: allocating an array requires simulating the constructors. diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 0392a2490f..cbd5ea7553 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -22,23 +22,6 @@ using namespace clang; using namespace ento; -static CallEventKind classifyCallExpr(const CallExpr *CE) { - if (isa(CE)) - return CE_CXXMember; - - const CXXOperatorCallExpr *OpCE = dyn_cast(CE); - if (OpCE) { - const FunctionDecl *DirectCallee = CE->getDirectCallee(); - if (const CXXMethodDecl *MD = dyn_cast(DirectCallee)) - if (MD->isInstance()) - return CE_CXXMemberOperator; - } else if (CE->getCallee()->getType()->isBlockPointerType()) { - return CE_Block; - } - - return CE_Function; -} - void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { // Get the entry block in the CFG of the callee. const StackFrameContext *calleeCtx = CE.getCalleeContext(); @@ -387,42 +370,18 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, ExplodedNodeSet dstPreVisit; getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, CE, *this); - // Get the callee kind. - CallEventKind K = classifyCallExpr(CE); + // Get the call in its initial state. We use this as a template to perform + // all the checks. + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef CallTemplate + = CEMgr.getSimpleCall(CE, Pred->getState(), Pred->getLocationContext()); // Evaluate the function call. We try each of the checkers // to see if the can evaluate the function call. ExplodedNodeSet dstCallEvaluated; for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end(); I != E; ++I) { - ProgramStateRef State = (*I)->getState(); - const LocationContext *LCtx = (*I)->getLocationContext(); - - // Evaluate the call. - switch (K) { - case CE_Function: { - FunctionCall Call(CE, State, LCtx); - evalCall(dstCallEvaluated, *I, Call); - break; - } - case CE_CXXMember: { - CXXMemberCall Call(cast(CE), State, LCtx); - evalCall(dstCallEvaluated, *I, Call); - break; - } - case CE_CXXMemberOperator: { - CXXMemberOperatorCall Call(cast(CE), State, LCtx); - evalCall(dstCallEvaluated, *I, Call); - break; - } - case CE_Block: { - BlockCall Call(CE, State, LCtx); - evalCall(dstCallEvaluated, *I, Call); - break; - } - default: - llvm_unreachable("Non-CallExpr CallEventKind"); - } + evalCall(dstCallEvaluated, *I, *CallTemplate); } // Finally, perform the post-condition check of the CallExpr and store @@ -435,6 +394,13 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, const SimpleCall &Call) { + // WARNING: At this time, the state attached to 'Call' may be older than the + // state in 'Pred'. This is a minor optimization since CheckerManager will + // use an updated CallEvent instance when calling checkers, but if 'Call' is + // ever used directly in this function all callers should be updated to pass + // the most recent state. (It is probably not worth doing the work here since + // for some callers this will not be necessary.) + // Run any pre-call checks using the generic call interface. ExplodedNodeSet dstPreVisit; getCheckerManager().runCheckersForPreCall(dstPreVisit, Pred, Call, *this); @@ -483,36 +449,35 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, } void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, - const CallEvent &Call) { - ProgramStateRef State = 0; - const Expr *E = Call.getOriginExpr(); + const CallEvent &CallTemplate) { + // Make sure we have the most recent state attached to the call. + ProgramStateRef State = Pred->getState(); + CallEventRef<> Call = CallTemplate.cloneWithState(State); // Try to inline the call. // The origin expression here is just used as a kind of checksum; - // for CallEvents that do not have origin expressions, this should still be - // safe. - State = getInlineFailedState(Pred->getState(), E); - if (State == 0 && inlineCall(Call, Pred)) { - // If we inlined the call, the successor has been manually added onto - // the work list and we should not consider it for subsequent call - // handling steps. + // this should still be safe even for CallEvents that don't come from exprs. + const Expr *E = Call->getOriginExpr(); + ProgramStateRef InlinedFailedState = getInlineFailedState(State, E); + + if (InlinedFailedState) { + // If we already tried once and failed, make sure we don't retry later. + State = InlinedFailedState; + } else if (inlineCall(*Call, Pred)) { + // If we decided to inline the call, the successor has been manually + // added onto the work list and we should not perform our generic + // call-handling steps. Bldr.takeNodes(Pred); return; } // If we can't inline it, handle the return value and invalidate the regions. - if (State == 0) - State = Pred->getState(); - - // Invalidate any regions touched by the call. unsigned Count = currentBuilderContext->getCurrentBlockCount(); - State = Call.invalidateRegions(Count, State); - - // Construct and bind the return value. - State = bindReturnValue(Call, Pred->getLocationContext(), State); + State = Call->invalidateRegions(Count, State); + State = bindReturnValue(*Call, Pred->getLocationContext(), State); // And make the result node. - Bldr.generateNode(Call.getProgramPoint(), State, Pred); + Bldr.generateNode(Call->getProgramPoint(), State, Pred); } void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred, diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index e61ea79594..e3bc498b51 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -140,17 +140,20 @@ static bool isSubclass(const ObjCInterfaceDecl *Class, IdentifierInfo *II) { return isSubclass(Class->getSuperClass(), II); } -void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg, +void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef Msg = + CEMgr.getObjCMethodCall(ME, Pred->getState(), Pred->getLocationContext()); + // Handle the previsits checks. ExplodedNodeSet dstPrevisit; getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred, - msg, *this); + *Msg, *this); ExplodedNodeSet dstGenericPrevisit; getCheckerManager().runCheckersForPreCall(dstGenericPrevisit, dstPrevisit, - msg, *this); + *Msg, *this); // Proceed with evaluate the message expression. ExplodedNodeSet dstEval; @@ -159,16 +162,17 @@ void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg, for (ExplodedNodeSet::iterator DI = dstGenericPrevisit.begin(), DE = dstGenericPrevisit.end(); DI != DE; ++DI) { ExplodedNode *Pred = *DI; + ProgramStateRef State = Pred->getState(); + CallEventRef UpdatedMsg = Msg.cloneWithState(State); - if (msg.isInstanceMessage()) { - SVal recVal = msg.getReceiverSVal(); + if (UpdatedMsg->isInstanceMessage()) { + SVal recVal = UpdatedMsg->getReceiverSVal(); if (!recVal.isUndef()) { // Bifurcate the state into nil and non-nil ones. DefinedOrUnknownSVal receiverVal = cast(recVal); - ProgramStateRef state = Pred->getState(); ProgramStateRef notNilState, nilState; - llvm::tie(notNilState, nilState) = state->assume(receiverVal); + llvm::tie(notNilState, nilState) = State->assume(receiverVal); // There are three cases: can be nil or non-nil, must be nil, must be // non-nil. We ignore must be nil, and merge the rest two into non-nil. @@ -180,20 +184,20 @@ void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg, // Check if the "raise" message was sent. assert(notNilState); - if (msg.getSelector() == RaiseSel) { + if (Msg->getSelector() == RaiseSel) { // If we raise an exception, for now treat it as a sink. // Eventually we will want to handle exceptions properly. - Bldr.generateNode(currentStmt, Pred, Pred->getState(), true); + Bldr.generateNode(currentStmt, Pred, State, true); continue; } // Generate a transition to non-Nil state. - if (notNilState != state) + if (notNilState != State) Pred = Bldr.generateNode(currentStmt, Pred, notNilState); } } else { // Check for special class methods. - if (const ObjCInterfaceDecl *Iface = msg.getReceiverInterface()) { + if (const ObjCInterfaceDecl *Iface = Msg->getReceiverInterface()) { if (!NSExceptionII) { ASTContext &Ctx = getContext(); NSExceptionII = &Ctx.Idents.get("NSException"); @@ -222,7 +226,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg, Ctx.Selectors.getSelector(II.size(), &II[0]); } - Selector S = msg.getSelector(); + Selector S = Msg->getSelector(); bool RaisesException = false; for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) { if (S == NSExceptionInstanceRaiseSelectors[i]) { @@ -240,16 +244,17 @@ void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg, } } } - // Evaluate the call. - defaultEvalCall(Bldr, Pred, msg); + // Evaluate the call. + defaultEvalCall(Bldr, Pred, *UpdatedMsg); } ExplodedNodeSet dstPostvisit; - getCheckerManager().runCheckersForPostCall(dstPostvisit, dstEval, msg, *this); + getCheckerManager().runCheckersForPostCall(dstPostvisit, dstEval, + *Msg, *this); // Finally, perform the post-condition check of the ObjCMessageExpr and store // the created nodes in 'Dst'. getCheckerManager().runCheckersForPostObjCMessage(Dst, dstPostvisit, - msg, *this); + *Msg, *this); }