diff options
author | Jordan Rose <jordan_rose@apple.com> | 2012-07-10 22:07:57 +0000 |
---|---|---|
committer | Jordan Rose <jordan_rose@apple.com> | 2012-07-10 22:07:57 +0000 |
commit | e54cfc7b9990acffd0a8a4ba381717b4bb9f3011 (patch) | |
tree | 2b1073b5acf22805d099103511a4746f23c79901 | |
parent | 852aa0d2c5d2d1faf2d77b5aa3c0848068a342c5 (diff) |
[analyzer] Use CallEvent for building inlined stack frames.
In order to accomplish this, we now build the callee's stack frame
as part of the CallEnter node, rather than the subsequent BlockEdge node.
This should not have any effect on perceived behavior or diagnostics.
This makes it safe to re-enable inlining of member overloaded operators.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@160022 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h | 56 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h | 4 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/Store.h | 6 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/Calls.cpp | 72 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 20 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ProgramState.cpp | 10 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/RegionStore.cpp | 88 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/Store.cpp | 35 | ||||
-rw-r--r-- | test/Analysis/operator-calls.cpp | 5 |
9 files changed, 148 insertions, 148 deletions
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h b/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h index e1d30e42c2..4d64fd83a6 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h @@ -73,10 +73,6 @@ protected: /// result of this call. virtual void addExtraInvalidatedRegions(RegionList &Regions) const {} - typedef const ParmVarDecl * const *param_iterator; - virtual param_iterator param_begin() const = 0; - virtual param_iterator param_end() const = 0; - virtual QualType getDeclaredResultType() const { return QualType(); } public: @@ -147,6 +143,10 @@ public: /// \brief Returns the result type, adjusted for references. QualType getResultType() const; + /// \brief Returns the value of the implicit 'this' object, or UndefinedVal if + /// this is not a C++ member function call. + virtual SVal getCXXThisVal() const { return UndefinedVal(); } + /// \brief Returns true if any of the arguments appear to represent callbacks. bool hasNonZeroCallbackArg() const; @@ -174,18 +174,44 @@ public: /// inlining. static bool mayBeInlined(const Stmt *S); - // Iterator access to parameter types. + // Iterator access to formal parameters and their types. private: typedef std::const_mem_fun_t<QualType, ParmVarDecl> get_type_fun; public: + typedef const ParmVarDecl * const *param_iterator; + + /// Returns an iterator over the call's formal parameters. + /// + /// If UseDefinitionParams is set, this will return the parameter decls + /// used in the callee's definition (suitable for inlining). Most of the + /// time it is better to use the decl found by name lookup, which likely + /// carries more annotations. + /// + /// Remember that the number of formal parameters may not match the number + /// of arguments for all calls. However, the first parameter will always + /// correspond with the argument value returned by \c getArgSVal(0). + /// + /// If the call has no accessible declaration (or definition, if + /// \p UseDefinitionParams is set), \c param_begin() will be equal to + /// \c param_end(). + virtual param_iterator param_begin(bool UseDefinitionParams = false) const = 0; + /// \sa param_begin() + virtual param_iterator param_end(bool UseDefinitionParams = false) const = 0; + typedef llvm::mapped_iterator<param_iterator, get_type_fun> param_type_iterator; + /// Returns an iterator over the types of the call's formal parameters. + /// + /// This uses the callee decl found by default name lookup rather than the + /// definition because it represents a public interface, and probably has + /// more annotations. param_type_iterator param_type_begin() const { return llvm::map_iterator(param_begin(), get_type_fun(&ParmVarDecl::getType)); } + /// \sa param_type_begin() param_type_iterator param_type_end() const { return llvm::map_iterator(param_end(), get_type_fun(&ParmVarDecl::getType)); } @@ -200,8 +226,8 @@ protected: AnyFunctionCall(ProgramStateRef St, const LocationContext *LCtx, Kind K) : CallEvent(St, LCtx, K) {} - param_iterator param_begin() const; - param_iterator param_end() const; + param_iterator param_begin(bool UseDefinitionParams = false) const; + param_iterator param_end(bool UseDefinitionParams = false) const; QualType getDeclaredResultType() const; @@ -282,6 +308,8 @@ public: return cast<CXXMemberCallExpr>(SimpleCall::getOriginExpr()); } + SVal getCXXThisVal() const; + static bool classof(const CallEvent *CA) { return CA->getKind() == CE_CXXMember; } @@ -309,6 +337,8 @@ public: return getOriginExpr()->getArg(Index + 1); } + SVal getCXXThisVal() const; + static bool classof(const CallEvent *CA) { return CA->getKind() == CE_CXXMemberOperator; } @@ -321,8 +351,8 @@ class BlockCall : public SimpleCall { protected: void addExtraInvalidatedRegions(RegionList &Regions) const; - param_iterator param_begin() const; - param_iterator param_end() const; + param_iterator param_begin(bool UseDefinitionParams = false) const; + param_iterator param_end(bool UseDefinitionParams = false) const; QualType getDeclaredResultType() const; @@ -387,6 +417,8 @@ public: return CE->getArg(Index); } + SVal getCXXThisVal() const; + static bool classof(const CallEvent *CA) { return CA->getKind() == CE_CXXConstructor; } @@ -417,6 +449,8 @@ public: const CXXDestructorDecl *getDecl() const { return DD; } unsigned getNumArgs() const { return 0; } + SVal getCXXThisVal() const; + static bool classof(const CallEvent *CA) { return CA->getKind() == CE_CXXDestructor; } @@ -465,8 +499,8 @@ protected: void addExtraInvalidatedRegions(RegionList &Regions) const; - param_iterator param_begin() const; - param_iterator param_end() const; + param_iterator param_begin(bool UseDefinitionParams = false) const; + param_iterator param_end(bool UseDefinitionParams = false) const; QualType getDeclaredResultType() const; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h index 3dd2775aa4..fd5f4373cd 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -223,8 +223,8 @@ public: /// enterStackFrame - Returns the state for entry to the given stack frame, /// preserving the current state. - ProgramStateRef enterStackFrame(const LocationContext *callerCtx, - const StackFrameContext *calleeCtx) const; + ProgramStateRef enterStackFrame(const CallEvent &Call, + const StackFrameContext *CalleeCtx) const; /// Get the lvalue for a variable reference. Loc getLValue(const VarDecl *D, const LocationContext *LC) const; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h b/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h index 35097aca6c..828347254c 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h @@ -199,9 +199,9 @@ public: /// enterStackFrame - Let the StoreManager to do something when execution /// engine is about to execute into a callee. - virtual StoreRef enterStackFrame(ProgramStateRef state, - const LocationContext *callerCtx, - const StackFrameContext *calleeCtx); + StoreRef enterStackFrame(Store store, + const CallEvent &Call, + const StackFrameContext *CalleeCtx); virtual void print(Store store, raw_ostream &Out, const char* nl, const char *sep) = 0; diff --git a/lib/StaticAnalyzer/Core/Calls.cpp b/lib/StaticAnalyzer/Core/Calls.cpp index 8ea1336bb7..ced1154da7 100644 --- a/lib/StaticAnalyzer/Core/Calls.cpp +++ b/lib/StaticAnalyzer/Core/Calls.cpp @@ -219,20 +219,22 @@ bool CallEvent::mayBeInlined(const Stmt *S) { } -CallEvent::param_iterator AnyFunctionCall::param_begin() const { - const FunctionDecl *D = getDecl(); +CallEvent::param_iterator +AnyFunctionCall::param_begin(bool UseDefinitionParams) const { + const Decl *D = UseDefinitionParams ? getDefinition() : getDecl(); if (!D) return 0; - return D->param_begin(); + return cast<FunctionDecl>(D)->param_begin(); } -CallEvent::param_iterator AnyFunctionCall::param_end() const { - const FunctionDecl *D = getDecl(); +CallEvent::param_iterator +AnyFunctionCall::param_end(bool UseDefinitionParams) const { + const Decl *D = UseDefinitionParams ? getDefinition() : getDecl(); if (!D) return 0; - return D->param_end(); + return cast<FunctionDecl>(D)->param_end(); } QualType AnyFunctionCall::getDeclaredResultType() const { @@ -309,23 +311,31 @@ const FunctionDecl *SimpleCall::getDecl() const { } -void CXXMemberCall::addExtraInvalidatedRegions(RegionList &Regions) const { +SVal CXXMemberCall::getCXXThisVal() const { const Expr *Base = getOriginExpr()->getImplicitObjectArgument(); // FIXME: Will eventually need to cope with member pointers. This is // a limitation in getImplicitObjectArgument(). if (!Base) - return; - - if (const MemRegion *R = getSVal(Base).getAsRegion()) + return UnknownVal(); + + return getSVal(Base); +} + +void CXXMemberCall::addExtraInvalidatedRegions(RegionList &Regions) const { + if (const MemRegion *R = getCXXThisVal().getAsRegion()) Regions.push_back(R); } +SVal CXXMemberOperatorCall::getCXXThisVal() const { + const Expr *Base = getOriginExpr()->getArg(0); + return getSVal(Base); +} + void CXXMemberOperatorCall::addExtraInvalidatedRegions(RegionList &Regions) const { - const Expr *Base = getOriginExpr()->getArg(0); - if (const MemRegion *R = getSVal(Base).getAsRegion()) + if (const MemRegion *R = getCXXThisVal().getAsRegion()) Regions.push_back(R); } @@ -337,14 +347,22 @@ const BlockDataRegion *BlockCall::getBlockRegion() const { return dyn_cast_or_null<BlockDataRegion>(DataReg); } -CallEvent::param_iterator BlockCall::param_begin() const { +CallEvent::param_iterator +BlockCall::param_begin(bool UseDefinitionParams) const { + // Blocks don't have distinct declarations and definitions. + (void)UseDefinitionParams; + const BlockDecl *D = getBlockDecl(); if (!D) return 0; return D->param_begin(); } -CallEvent::param_iterator BlockCall::param_end() const { +CallEvent::param_iterator +BlockCall::param_end(bool UseDefinitionParams) const { + // Blocks don't have distinct declarations and definitions. + (void)UseDefinitionParams; + const BlockDecl *D = getBlockDecl(); if (!D) return 0; @@ -366,32 +384,46 @@ QualType BlockCall::getDeclaredResultType() const { } +SVal CXXConstructorCall::getCXXThisVal() const { + if (Target) + return loc::MemRegionVal(Target); + return UnknownVal(); +} + void CXXConstructorCall::addExtraInvalidatedRegions(RegionList &Regions) const { if (Target) Regions.push_back(Target); } +SVal CXXDestructorCall::getCXXThisVal() const { + if (Target) + return loc::MemRegionVal(Target); + return UnknownVal(); +} + void CXXDestructorCall::addExtraInvalidatedRegions(RegionList &Regions) const { if (Target) Regions.push_back(Target); } -CallEvent::param_iterator ObjCMethodCall::param_begin() const { - const ObjCMethodDecl *D = getDecl(); +CallEvent::param_iterator +ObjCMethodCall::param_begin(bool UseDefinitionParams) const { + const Decl *D = UseDefinitionParams ? getDefinition() : getDecl(); if (!D) return 0; - return D->param_begin(); + return cast<ObjCMethodDecl>(D)->param_begin(); } -CallEvent::param_iterator ObjCMethodCall::param_end() const { - const ObjCMethodDecl *D = getDecl(); +CallEvent::param_iterator +ObjCMethodCall::param_end(bool UseDefinitionParams) const { + const Decl *D = UseDefinitionParams ? getDefinition() : getDecl(); if (!D) return 0; - return D->param_end(); + return cast<ObjCMethodDecl>(D)->param_end(); } void diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 92497dbb1e..2b47bc064d 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -55,11 +55,7 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { // Construct an edge representing the starting location in the callee. BlockEdge Loc(Entry, Succ, calleeCtx); - // Construct a new state which contains the mapping from actual to - // formal arguments. - const LocationContext *callerCtx = Pred->getLocationContext(); - ProgramStateRef state = Pred->getState()->enterStackFrame(callerCtx, - calleeCtx); + ProgramStateRef state = Pred->getState(); // Construct a new node and add it to the worklist. bool isNew; @@ -287,14 +283,9 @@ bool ExprEngine::inlineCall(ExplodedNodeSet &Dst, switch (Call.getKind()) { case CE_Function: case CE_CXXMember: + case CE_CXXMemberOperator: // These are always at least possible to inline. break; - case CE_CXXMemberOperator: - // FIXME: This should be possible to inline, but - // RegionStore::enterStackFrame isn't smart enough to handle the first - // argument being 'this'. The correct solution is to use CallEvent in - // enterStackFrame as well. - return false; case CE_CXXConstructor: case CE_CXXDestructor: // Do not inline constructors until we can really model destructors. @@ -337,8 +328,13 @@ bool ExprEngine::inlineCall(ExplodedNodeSet &Dst, currentStmtIdx); CallEnter Loc(CallE, CalleeSFC, Pred->getLocationContext()); + + // Construct a new state which contains the mapping from actual to + // formal arguments. + ProgramStateRef State = Pred->getState()->enterStackFrame(Call, CalleeSFC); + bool isNew; - if (ExplodedNode *N = G.getNode(Loc, Pred->getState(), false, &isNew)) { + if (ExplodedNode *N = G.getNode(Loc, State, false, &isNew)) { N->addPredecessor(Pred, G); if (isNew) Engine.getWorkList()->enqueue(N); diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index d7668dec1a..529be0a845 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -203,11 +203,11 @@ ProgramStateRef ProgramState::unbindLoc(Loc LV) const { } ProgramStateRef -ProgramState::enterStackFrame(const LocationContext *callerCtx, - const StackFrameContext *calleeCtx) const { - const StoreRef &new_store = - getStateManager().StoreMgr->enterStackFrame(this, callerCtx, calleeCtx); - return makeWithStore(new_store); +ProgramState::enterStackFrame(const CallEvent &Call, + const StackFrameContext *CalleeCtx) const { + const StoreRef &NewStore = + getStateManager().StoreMgr->enterStackFrame(getStore(), Call, CalleeCtx); + return makeWithStore(NewStore); } SVal ProgramState::getSValAsScalarOrLoc(const MemRegion *R) const { diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index fa26c13203..c084bd2ad0 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -389,16 +389,6 @@ public: // Part of public interface to class. /// It returns a new Store with these values removed. StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx, SymbolReaper& SymReaper); - - StoreRef enterStackFrame(ProgramStateRef state, - const LocationContext *callerCtx, - const StackFrameContext *calleeCtx); - - StoreRef enterStackFrame(ProgramStateRef state, - const FunctionDecl *FD, - const LocationContext *callerCtx, - const StackFrameContext *calleeCtx); - //===------------------------------------------------------------------===// // Region "extents". @@ -2066,84 +2056,6 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, return StoreRef(B.getRootWithoutRetain(), *this); } -StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state, - const LocationContext *callerCtx, - const StackFrameContext *calleeCtx) -{ - const Decl *D = calleeCtx->getDecl(); - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) - return enterStackFrame(state, FD, callerCtx, calleeCtx); - - // FIXME: when we handle more cases, this will need to be expanded. - - const BlockDecl *BD = cast<BlockDecl>(D); - BlockDecl::param_const_iterator PI = BD->param_begin(), - PE = BD->param_end(); - StoreRef store = StoreRef(state->getStore(), *this); - const CallExpr *CE = cast<CallExpr>(calleeCtx->getCallSite()); - CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); - for (; AI != AE && PI != PE; ++AI, ++PI) { - SVal ArgVal = state->getSVal(*AI, callerCtx); - store = Bind(store.getStore(), - svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)), - ArgVal); - } - - return store; -} - -StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state, - const FunctionDecl *FD, - const LocationContext *callerCtx, - const StackFrameContext *calleeCtx) -{ - FunctionDecl::param_const_iterator PI = FD->param_begin(), - PE = FD->param_end(); - StoreRef store = StoreRef(state->getStore(), *this); - - if (CallExpr const *CE = dyn_cast<CallExpr>(calleeCtx->getCallSite())) { - CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); - - // Copy the arg expression value to the arg variables. We check that - // PI != PE because the actual number of arguments may be different than - // the function declaration. - for (; AI != AE && PI != PE; ++AI, ++PI) { - SVal ArgVal = state->getSVal(*AI, callerCtx); - store = Bind(store.getStore(), - svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)), - ArgVal); - } - - // For C++ method calls, also include the 'this' pointer. - if (const CXXMemberCallExpr *CME = dyn_cast<CXXMemberCallExpr>(CE)) { - loc::MemRegionVal This = - svalBuilder.getCXXThis(cast<CXXMethodDecl>(CME->getCalleeDecl()), - calleeCtx); - SVal CalledObj = state->getSVal(CME->getImplicitObjectArgument(), - callerCtx); - store = Bind(store.getStore(), This, CalledObj); - } - } - else if (const CXXConstructExpr *CE = - dyn_cast<CXXConstructExpr>(calleeCtx->getCallSite())) { - CXXConstructExpr::const_arg_iterator AI = CE->arg_begin(), - AE = CE->arg_end(); - - // Copy the arg expression value to the arg variables. - for (; AI != AE; ++AI, ++PI) { - SVal ArgVal = state->getSVal(*AI, callerCtx); - store = Bind(store.getStore(), - svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)), - ArgVal); - } - } - else { - assert(isa<CXXDestructorDecl>(calleeCtx->getDecl())); - } - - return store; -} - //===----------------------------------------------------------------------===// // Utility methods. //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index 11748ae54d..d5c88e8ad8 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Calls.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/AST/CharUnits.h" #include "clang/AST/DeclObjC.h" @@ -23,10 +24,36 @@ StoreManager::StoreManager(ProgramStateManager &stateMgr) : svalBuilder(stateMgr.getSValBuilder()), StateMgr(stateMgr), MRMgr(svalBuilder.getRegionManager()), Ctx(stateMgr.getContext()) {} -StoreRef StoreManager::enterStackFrame(ProgramStateRef state, - const LocationContext *callerCtx, - const StackFrameContext *calleeCtx) { - return StoreRef(state->getStore(), *this); +StoreRef StoreManager::enterStackFrame(Store OldStore, + const CallEvent &Call, + const StackFrameContext *LCtx) { + StoreRef Store = StoreRef(OldStore, *this); + + unsigned Idx = 0; + for (CallEvent::param_iterator I = Call.param_begin(/*UseDefinition=*/true), + E = Call.param_end(/*UseDefinition=*/true); + I != E; ++I, ++Idx) { + const ParmVarDecl *Decl = *I; + assert(Decl && "Formal parameter has no decl?"); + + SVal ArgVal = Call.getArgSVal(Idx); + if (!ArgVal.isUnknown()) { + Store = Bind(Store.getStore(), + svalBuilder.makeLoc(MRMgr.getVarRegion(Decl, LCtx)), + ArgVal); + } + } + + // FIXME: We will eventually want to generalize this to handle other non- + // parameter arguments besides 'this' (such as 'self' for ObjC methods). + SVal ThisVal = Call.getCXXThisVal(); + if (!ThisVal.isUndef()) { + const CXXMethodDecl *MD = cast<CXXMethodDecl>(Call.getDecl()); + loc::MemRegionVal ThisRegion = svalBuilder.getCXXThis(MD, LCtx); + Store = Bind(Store.getStore(), ThisRegion, ThisVal); + } + + return Store; } const MemRegion *StoreManager::MakeElementRegion(const MemRegion *Base, diff --git a/test/Analysis/operator-calls.cpp b/test/Analysis/operator-calls.cpp index e81f428c6f..dbc63bc4be 100644 --- a/test/Analysis/operator-calls.cpp +++ b/test/Analysis/operator-calls.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core,debug.ExprInspection -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core,debug.ExprInspection -analyzer-ipa=inlining -verify %s void clang_analyzer_eval(bool); struct X0 { }; @@ -28,6 +28,5 @@ struct IntComparable { }; void testMemberOperator(IntComparable B) { - // FIXME: Change this to TRUE when we re-enable inlining. - clang_analyzer_eval(B == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(B == 0); // expected-warning{{TRUE}} } |