diff options
author | Jordan Rose <jordan_rose@apple.com> | 2012-08-13 23:46:05 +0000 |
---|---|---|
committer | Jordan Rose <jordan_rose@apple.com> | 2012-08-13 23:46:05 +0000 |
commit | 645baeed6800f952e9ad1d5666e01080385531a2 (patch) | |
tree | 2752f7c6f2e3ffd85616837dcb309510ec3c788b | |
parent | 8ec104b9fffb917924c495ce3dd25694e4e3087a (diff) |
[analyzer] Reduce code duplication: make CXXDestructorCall a CXXInstanceCall.
While there is now some duplication between SimpleCall and the CXXInstanceCall
sub-hierarchy, this is much better than copy-and-pasting the devirtualization
logic shared by both instance methods and destructors.
An unfortunate side effect is that there is no longer a single CallEvent type
that corresponds to "calls written as CallExprs". For the most part this is a
good thing, but the checker callback eval::Call still takes a CallExpr rather
than a CallEvent (since we're not sure if we want to allow checkers to
evaluate other kinds of calls). A mistake here will be caught by a cast<> in
CheckerManager::runCheckersForEvalCall.
No functionality change.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@161809 91177308-0d34-0410-b5e6-96231b3b80d8
6 files changed, 132 insertions, 147 deletions
diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h index aef9e71297..e11b6d518a 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -319,9 +319,11 @@ public: SVal Cond, bool Assumption); /// \brief Run checkers for evaluating a call. + /// + /// Warning: Currently, the CallEvent MUST come from a CallExpr! void runCheckersForEvalCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, - const SimpleCall &CE, ExprEngine &Eng); + const CallEvent &CE, ExprEngine &Eng); /// \brief Run checkers for the entire Translation Unit. void runCheckersOnEndOfTranslationUnit(const TranslationUnitDecl *TU, diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index 0a9590f640..47edfe9fca 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -32,15 +32,15 @@ namespace ento { enum CallEventKind { CE_Function, - CE_CXXMember, - CE_CXXMemberOperator, - CE_BEG_CXX_INSTANCE_CALLS = CE_CXXMember, - CE_END_CXX_INSTANCE_CALLS = CE_CXXMemberOperator, CE_Block, CE_BEG_SIMPLE_CALLS = CE_Function, CE_END_SIMPLE_CALLS = CE_Block, - CE_CXXConstructor, + CE_CXXMember, + CE_CXXMemberOperator, CE_CXXDestructor, + CE_BEG_CXX_INSTANCE_CALLS = CE_CXXMember, + CE_END_CXX_INSTANCE_CALLS = CE_CXXDestructor, + CE_CXXConstructor, CE_CXXAllocator, CE_BEG_FUNCTION_CALLS = CE_Function, CE_END_FUNCTION_CALLS = CE_CXXAllocator, @@ -377,7 +377,7 @@ public: } }; -/// \brief Represents a call to a written as a CallExpr. +/// \brief Represents a call to a non-C++ function, written as a CallExpr. class SimpleCall : public AnyFunctionCall { protected: SimpleCall(const CallExpr *CE, ProgramStateRef St, @@ -426,24 +426,80 @@ public: } }; +/// \brief Represents a call to a block. +/// +/// Example: <tt>^{ /* ... */ }()</tt> +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); } + + virtual void getExtraInvalidatedRegions(RegionList &Regions) const; + + virtual QualType getDeclaredResultType() const; + +public: + /// \brief Returns the region associated with this instance of the block. + /// + /// This may be NULL if the block's origin is unknown. + const BlockDataRegion *getBlockRegion() const; + + /// \brief Gets the declaration of the block. + /// + /// This is not an override of getDecl() because AnyFunctionCall has already + /// assumed that it's a FunctionDecl. + const BlockDecl *getBlockDecl() const { + const BlockDataRegion *BR = getBlockRegion(); + if (!BR) + return 0; + return BR->getDecl(); + } + + virtual RuntimeDefinition getRuntimeDefinition() const { + return RuntimeDefinition(getBlockDecl()); + } + + virtual void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const; + + virtual param_iterator param_begin() const; + virtual param_iterator param_end() const; + + virtual Kind getKind() const { return CE_Block; } + + static bool classof(const CallEvent *CA) { + return CA->getKind() == CE_Block; + } +}; + /// \brief Represents a non-static C++ member function call, no matter how /// it is written. -class CXXInstanceCall : public SimpleCall { +class CXXInstanceCall : public AnyFunctionCall { protected: virtual void getExtraInvalidatedRegions(RegionList &Regions) const; CXXInstanceCall(const CallExpr *CE, ProgramStateRef St, const LocationContext *LCtx) - : SimpleCall(CE, St, LCtx) {} + : AnyFunctionCall(CE, St, LCtx) {} + CXXInstanceCall(const FunctionDecl *D, ProgramStateRef St, + const LocationContext *LCtx) + : AnyFunctionCall(D, St, LCtx) {} + - CXXInstanceCall(const CXXInstanceCall &Other) : SimpleCall(Other) {} + CXXInstanceCall(const CXXInstanceCall &Other) : AnyFunctionCall(Other) {} public: /// \brief Returns the expression representing the implicit 'this' object. - virtual const Expr *getCXXThisExpr() const = 0; + virtual const Expr *getCXXThisExpr() const { return 0; } /// \brief Returns the value of the implicit 'this' object. - SVal getCXXThisVal() const { + virtual SVal getCXXThisVal() const { const Expr *Base = getCXXThisExpr(); // FIXME: This doesn't handle an overloaded ->* operator. if (!Base) @@ -451,6 +507,8 @@ public: return getSVal(Base); } + virtual const FunctionDecl *getDecl() const; + virtual RuntimeDefinition getRuntimeDefinition() const; virtual void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, @@ -478,7 +536,17 @@ protected: public: virtual const CXXMemberCallExpr *getOriginExpr() const { - return cast<CXXMemberCallExpr>(SimpleCall::getOriginExpr()); + return cast<CXXMemberCallExpr>(CXXInstanceCall::getOriginExpr()); + } + + virtual unsigned getNumArgs() const { + if (const CallExpr *CE = getOriginExpr()) + return CE->getNumArgs(); + return 0; + } + + virtual const Expr *getArgExpr(unsigned Index) const { + return getOriginExpr()->getArg(Index); } virtual const Expr *getCXXThisExpr() const; @@ -510,7 +578,7 @@ protected: public: virtual const CXXOperatorCallExpr *getOriginExpr() const { - return cast<CXXOperatorCallExpr>(SimpleCall::getOriginExpr()); + return cast<CXXOperatorCallExpr>(CXXInstanceCall::getOriginExpr()); } virtual unsigned getNumArgs() const { @@ -529,55 +597,43 @@ public: } }; -/// \brief Represents a call to a block. +/// \brief Represents an implicit call to a C++ destructor. /// -/// Example: <tt>^{ /* ... */ }()</tt> -class BlockCall : public SimpleCall { +/// 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 CXXInstanceCall { 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); } - - virtual void getExtraInvalidatedRegions(RegionList &Regions) const; - - virtual QualType getDeclaredResultType() const; - -public: - /// \brief Returns the region associated with this instance of the block. - /// - /// This may be NULL if the block's origin is unknown. - const BlockDataRegion *getBlockRegion() const; - - /// \brief Gets the declaration of the block. + /// Creates an implicit destructor. /// - /// This is not an override of getDecl() because AnyFunctionCall has already - /// assumed that it's a FunctionDecl. - const BlockDecl *getBlockDecl() const { - const BlockDataRegion *BR = getBlockRegion(); - if (!BR) - return 0; - return BR->getDecl(); + /// \param DD The destructor that will be called. + /// \param Trigger The statement whose completion causes this destructor call. + /// \param Target The object region to be destructed. + /// \param St The path-sensitive state at this point in the program. + /// \param LCtx The location context at this point in the program. + CXXDestructorCall(const CXXDestructorDecl *DD, const Stmt *Trigger, + const MemRegion *Target, ProgramStateRef St, + const LocationContext *LCtx) + : CXXInstanceCall(DD, St, LCtx) { + Data = Target; + Location = Trigger->getLocEnd(); } - virtual RuntimeDefinition getRuntimeDefinition() const { - return RuntimeDefinition(getBlockDecl()); - } + CXXDestructorCall(const CXXDestructorCall &Other) : CXXInstanceCall(Other) {} + virtual void cloneTo(void *Dest) const { new (Dest) CXXDestructorCall(*this); } - virtual void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, - BindingsTy &Bindings) const; +public: + virtual SourceRange getSourceRange() const { return Location; } + virtual unsigned getNumArgs() const { return 0; } - virtual param_iterator param_begin() const; - virtual param_iterator param_end() const; + /// \brief Returns the value of the implicit 'this' object. + virtual SVal getCXXThisVal() const; - virtual Kind getKind() const { return CE_Block; } + virtual Kind getKind() const { return CE_CXXDestructor; } static bool classof(const CallEvent *CA) { - return CA->getKind() == CE_Block; + return CA->getKind() == CE_CXXDestructor; } }; @@ -622,7 +678,7 @@ public: } /// \brief Returns the value of the implicit 'this' object. - virtual SVal getCXXThisVal() const; + SVal getCXXThisVal() const; virtual void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, BindingsTy &Bindings) const; @@ -634,53 +690,6 @@ public: } }; -/// \brief Represents an implicit call to a C++ destructor. -/// -/// 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 { - friend class CallEventManager; - -protected: - /// Creates an implicit destructor. - /// - /// \param DD The destructor that will be called. - /// \param Trigger The statement whose completion causes this destructor call. - /// \param Target The object region to be destructed. - /// \param St The path-sensitive state at this point in the program. - /// \param LCtx The location context at this point in the program. - CXXDestructorCall(const CXXDestructorDecl *DD, const Stmt *Trigger, - const MemRegion *Target, ProgramStateRef St, - const LocationContext *LCtx) - : AnyFunctionCall(DD, St, LCtx) { - Data = Target; - 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; } - - /// \brief Returns the value of the implicit 'this' object. - virtual SVal getCXXThisVal() const; - - virtual RuntimeDefinition getRuntimeDefinition() const; - - virtual void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, - BindingsTy &Bindings) const; - - virtual Kind getKind() const { return CE_CXXDestructor; } - - static bool classof(const CallEvent *CA) { - return CA->getKind() == CE_CXXDestructor; - } -}; - /// \brief Represents the memory allocation call in a C++ new-expression. /// /// This is a call to "operator new". @@ -874,7 +883,7 @@ public: getCaller(const StackFrameContext *CalleeCtx, ProgramStateRef State); - CallEventRef<SimpleCall> + CallEventRef<> getSimpleCall(const CallExpr *E, ProgramStateRef State, const LocationContext *LCtx); diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 792b24e5b4..4addb9d4ec 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -463,8 +463,10 @@ public: const LocationContext *LCtx, ProgramStateRef State); + /// Evaluate a call, running pre- and post-call checks and allowing checkers + /// to be responsible for handling the evaluation of the call itself. void evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, - const SimpleCall &Call); + const CallEvent &Call); /// \brief Default implementation of call evaluation. void defaultEvalCall(NodeBuilder &B, ExplodedNode *Pred, diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index cacd347442..e3f4c61e90 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -355,6 +355,18 @@ const FunctionDecl *SimpleCall::getDecl() const { } +const FunctionDecl *CXXInstanceCall::getDecl() const { + const CallExpr *CE = cast_or_null<CallExpr>(getOriginExpr()); + if (!CE) + return AnyFunctionCall::getDecl(); + + const FunctionDecl *D = CE->getDirectCallee(); + if (D) + return D; + + return getSVal(CE->getCallee()).getAsFunctionDecl(); +} + void CXXInstanceCall::getExtraInvalidatedRegions(RegionList &Regions) const { if (const MemRegion *R = getCXXThisVal().getAsRegion()) Regions.push_back(R); @@ -389,7 +401,7 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const { const CXXMethodDecl *MD = cast<CXXMethodDecl>(D); if (!MD->isVirtual()) - return SimpleCall::getRuntimeDefinition(); + return AnyFunctionCall::getRuntimeDefinition(); // If the method is virtual, see if we can find the actual implementation // based on context-sensitivity. @@ -527,46 +539,6 @@ SVal CXXDestructorCall::getCXXThisVal() const { return UnknownVal(); } -void CXXDestructorCall::getExtraInvalidatedRegions(RegionList &Regions) const { - if (Data) - Regions.push_back(static_cast<const MemRegion *>(Data)); -} - -RuntimeDefinition CXXDestructorCall::getRuntimeDefinition() const { - const Decl *D = AnyFunctionCall::getRuntimeDefinition().getDecl(); - if (!D) - return RuntimeDefinition(); - - const CXXMethodDecl *MD = cast<CXXMethodDecl>(D); - if (!MD->isVirtual()) - return RuntimeDefinition(MD); - - // If the method is virtual, see if we can find the actual implementation - // based on context-sensitivity. - // FIXME: Virtual method calls behave differently when an object is being - // constructed or destructed. It's not as simple as "no devirtualization" - // because a /partially/ constructed object can be referred to through a - // base pointer. We'll eventually want to use DynamicTypeInfo here. - if (const CXXMethodDecl *Devirtualized = devirtualize(MD, getCXXThisVal())) - return RuntimeDefinition(Devirtualized); - - return RuntimeDefinition(); -} - -void CXXDestructorCall::getInitialStackFrameContents( - const StackFrameContext *CalleeCtx, - BindingsTy &Bindings) const { - AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings); - - SVal ThisVal = getCXXThisVal(); - if (!ThisVal.isUnknown()) { - SValBuilder &SVB = getState()->getStateManager().getSValBuilder(); - const CXXMethodDecl *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl()); - Loc ThisLoc = SVB.getCXXThis(MD, CalleeCtx); - Bindings.push_back(std::make_pair(ThisLoc, ThisVal)); - } -} - CallEvent::param_iterator ObjCMethodCall::param_begin() const { const ObjCMethodDecl *D = getDecl(); @@ -805,7 +777,7 @@ void ObjCMethodCall::getInitialStackFrameContents( } } -CallEventRef<SimpleCall> +CallEventRef<> CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State, const LocationContext *LCtx) { if (const CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(CE)) diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 564404e565..c7866559c6 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -504,9 +504,9 @@ CheckerManager::runCheckersForEvalAssume(ProgramStateRef state, /// Only one checker will evaluate the call. void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, - const SimpleCall &Call, + const CallEvent &Call, ExprEngine &Eng) { - const CallExpr *CE = Call.getOriginExpr(); + const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr()); for (ExplodedNodeSet::iterator NI = Src.begin(), NE = Src.end(); NI != NE; ++NI) { diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 37c48d406e..8ee6723057 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -445,7 +445,7 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, // Get the call in its initial state. We use this as a template to perform // all the checks. CallEventManager &CEMgr = getStateManager().getCallEventManager(); - CallEventRef<SimpleCall> CallTemplate + CallEventRef<> CallTemplate = CEMgr.getSimpleCall(CE, Pred->getState(), Pred->getLocationContext()); // Evaluate the function call. We try each of the checkers @@ -465,7 +465,7 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, } void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, - const SimpleCall &Call) { + const CallEvent &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 |