]> granicus.if.org Git - clang/commitdiff
[analyzer] Use CallEvent for building inlined stack frames.
authorJordan Rose <jordan_rose@apple.com>
Tue, 10 Jul 2012 22:07:57 +0000 (22:07 +0000)
committerJordan Rose <jordan_rose@apple.com>
Tue, 10 Jul 2012 22:07:57 +0000 (22:07 +0000)
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

include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h
include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
lib/StaticAnalyzer/Core/Calls.cpp
lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
lib/StaticAnalyzer/Core/ProgramState.cpp
lib/StaticAnalyzer/Core/RegionStore.cpp
lib/StaticAnalyzer/Core/Store.cpp
test/Analysis/operator-calls.cpp

index e1d30e42c2578fffcac2a424a2125f53629106fa..4d64fd83a62569a13632be1add2bee3aeef67435 100644 (file)
@@ -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;
 
index 3dd2775aa44b5eb59a4b7a415a15b943b876ccdf..fd5f4373cd26ea4739791a11c4c25917cde23cf3 100644 (file)
@@ -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;
index 35097aca6cc42ec55d82887be9dc729e7eef9827..828347254c62e78fa685ff7e99b0dae5158e1c3e 100644 (file)
@@ -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;
index 8ea1336bb7b1ab1b2fc4c1ebab5887ad8d71b869..ced1154da755f31b8a00d19d81cdb139a1594b84 100644 (file)
@@ -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
index 92497dbb1e36dc330ae2be2688c9d0400112a786..2b47bc064daea4eada9988fbc7df575ded45dd0a 100644 (file)
@@ -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);
index d7668dec1a9333dc6387f422c9527bfbfaca119f..529be0a845f779795d8f526600fd1dad646a5603 100644 (file)
@@ -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 {
index fa26c132039b63b0df7146377aa9f2d6fd4f0a13..c084bd2ad0de8c03e3ea4c2282b5c6d57bba94f0 100644 (file)
@@ -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.
 //===----------------------------------------------------------------------===//
index 11748ae54dbce9ff5b8730c2ba08831267de6122..d5c88e8ad81640e91e9fd89d598c626957832087 100644 (file)
@@ -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,
index e81f428c6f50336fc140e99df41855ab019826ab..dbc63bc4bed827eefd15db3e9737bc6dfed1cac5 100644 (file)
@@ -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}}
 }