/// 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:
/// \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;
/// 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));
}
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;
return cast<CXXMemberCallExpr>(SimpleCall::getOriginExpr());
}
+ SVal getCXXThisVal() const;
+
static bool classof(const CallEvent *CA) {
return CA->getKind() == CE_CXXMember;
}
return getOriginExpr()->getArg(Index + 1);
}
+ SVal getCXXThisVal() const;
+
static bool classof(const CallEvent *CA) {
return CA->getKind() == CE_CXXMemberOperator;
}
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;
return CE->getArg(Index);
}
+ SVal getCXXThisVal() const;
+
static bool classof(const CallEvent *CA) {
return CA->getKind() == CE_CXXConstructor;
}
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;
}
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;
/// 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;
/// 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;
}
-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 {
}
-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);
}
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;
}
+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
// 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;
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.
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);
}
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 {
/// 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".
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.
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
#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"
: 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,
-// 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 { };
};
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}}
}