]> granicus.if.org Git - clang/commitdiff
[analyzer] Perform post-call checks for all inlined calls.
authorJordan Rose <jordan_rose@apple.com>
Mon, 30 Jul 2012 23:39:47 +0000 (23:39 +0000)
committerJordan Rose <jordan_rose@apple.com>
Mon, 30 Jul 2012 23:39:47 +0000 (23:39 +0000)
Previously, we were only checking the origin expressions of inlined calls.
Checkers using the generic postCall and older postObjCMessage callbacks were
ignored. Now that we have CallEventManager, it is much easier to create
a CallEvent generically when exiting an inlined function, which we can then
use for post-call checks.

No test case because we don't (yet) have any checkers that depend on this
behavior (which is why it hadn't been fixed before now).

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@161005 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/StaticAnalyzer/Core/CheckerManager.h
include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
lib/StaticAnalyzer/Core/CallEvent.cpp
lib/StaticAnalyzer/Core/CheckerManager.cpp
lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp

index 7e1b07a5849de29c105a92a5327627442d1ed98c..aef9e71297a7da670718ba717cf655202c1d1ad0 100644 (file)
@@ -211,15 +211,18 @@ public:
   void runCheckersForPostObjCMessage(ExplodedNodeSet &Dst,
                                      const ExplodedNodeSet &Src,
                                      const ObjCMethodCall &msg,
-                                     ExprEngine &Eng) {
-    runCheckersForObjCMessage(/*isPreVisit=*/false, Dst, Src, msg, Eng);
+                                     ExprEngine &Eng,
+                                     bool wasInlined = false) {
+    runCheckersForObjCMessage(/*isPreVisit=*/false, Dst, Src, msg, Eng,
+                              wasInlined);
   }
 
   /// \brief Run checkers for visiting obj-c messages.
   void runCheckersForObjCMessage(bool isPreVisit,
                                  ExplodedNodeSet &Dst,
                                  const ExplodedNodeSet &Src,
-                                 const ObjCMethodCall &msg, ExprEngine &Eng);
+                                 const ObjCMethodCall &msg, ExprEngine &Eng,
+                                 bool wasInlined = false);
 
   /// \brief Run checkers for pre-visiting obj-c messages.
   void runCheckersForPreCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src,
@@ -229,14 +232,17 @@ public:
 
   /// \brief Run checkers for post-visiting obj-c messages.
   void runCheckersForPostCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src,
-                              const CallEvent &Call, ExprEngine &Eng) {
-    runCheckersForCallEvent(/*isPreVisit=*/false, Dst, Src, Call, Eng);
+                              const CallEvent &Call, ExprEngine &Eng,
+                              bool wasInlined = false) {
+    runCheckersForCallEvent(/*isPreVisit=*/false, Dst, Src, Call, Eng,
+                            wasInlined);
   }
 
   /// \brief Run checkers for visiting obj-c messages.
   void runCheckersForCallEvent(bool isPreVisit, ExplodedNodeSet &Dst,
                                const ExplodedNodeSet &Src,
-                               const CallEvent &Call, ExprEngine &Eng);
+                               const CallEvent &Call, ExprEngine &Eng,
+                               bool wasInlined = false);
 
   /// \brief Run checkers for load/store of a location.
   void runCheckersForLocation(ExplodedNodeSet &Dst,
index 74ac253d99a328034968691a0720d3497642ffe4..bc9320f283a19f12fc2c21045f71cefb772bd6f0 100644 (file)
@@ -59,6 +59,13 @@ public:
   CallEventRef<T> cloneWithState(ProgramStateRef State) const {
     return this->getPtr()->template cloneWithState<T>(State);
   }
+
+  // Allow implicit conversions to a superclass type, since CallEventRef
+  // behaves like a pointer-to-const.
+  template <typename SuperT>
+  operator CallEventRef<SuperT> () const {
+    return this->getPtr();
+  }
 };
 
 /// \brief Represents an abstract call to a function or method along a
@@ -807,6 +814,11 @@ class CallEventManager {
 public:
   CallEventManager(llvm::BumpPtrAllocator &alloc) : Alloc(alloc) {}
 
+
+  CallEventRef<>
+  getCaller(const StackFrameContext *CalleeCtx, ProgramStateRef State);
+
+
   CallEventRef<SimpleCall>
   getSimpleCall(const CallExpr *E, ProgramStateRef State,
                 const LocationContext *LCtx);
@@ -871,4 +883,16 @@ inline void CallEvent::Release() const {
 } // end namespace ento
 } // end namespace clang
 
+namespace llvm {
+  // Support isa<>, cast<>, and dyn_cast<> for CallEventRef.
+  template<class T> struct simplify_type< clang::ento::CallEventRef<T> > {
+    typedef const T *SimpleType;
+
+    static SimpleType
+    getSimplifiedValue(const clang::ento::CallEventRef<T>& Val) {
+      return Val.getPtr();
+    }
+  };
+}
+
 #endif
index 6a5d0f0fa97365588f3a82f6e68bc9823dbff93f..ad5a104aa05ef7b2d2be9b62c791503a66fec62b 100644 (file)
@@ -641,3 +641,60 @@ CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State,
   // something we can't reason about.
   return create<FunctionCall>(CE, State, LCtx);
 }
+
+
+CallEventRef<>
+CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
+                            ProgramStateRef State) {
+  const LocationContext *ParentCtx = CalleeCtx->getParent();
+  const LocationContext *CallerCtx = ParentCtx->getCurrentStackFrame();
+  assert(CallerCtx && "This should not be used for top-level stack frames");
+
+  const Stmt *CallSite = CalleeCtx->getCallSite();
+
+  if (CallSite) {
+    if (const CallExpr *CE = dyn_cast<CallExpr>(CallSite))
+      return getSimpleCall(CE, State, CallerCtx);
+
+    switch (CallSite->getStmtClass()) {
+    case Stmt::CXXConstructExprClass: {
+      SValBuilder &SVB = State->getStateManager().getSValBuilder();
+      const CXXMethodDecl *Ctor = cast<CXXMethodDecl>(CalleeCtx->getDecl());
+      Loc ThisPtr = SVB.getCXXThis(Ctor, CalleeCtx);
+      SVal ThisVal = State->getSVal(ThisPtr);
+
+      return getCXXConstructorCall(cast<CXXConstructExpr>(CallSite),
+                                   ThisVal.getAsRegion(), State, CallerCtx);
+    }
+    case Stmt::CXXNewExprClass:
+      return getCXXAllocatorCall(cast<CXXNewExpr>(CallSite), State, CallerCtx);
+    case Stmt::ObjCMessageExprClass:
+      return getObjCMethodCall(cast<ObjCMessageExpr>(CallSite),
+                               State, CallerCtx);
+    default:
+      llvm_unreachable("This is not an inlineable statement.");
+    }
+  }
+
+  // Fall back to the CFG. The only thing we haven't handled yet is
+  // destructors, though this could change in the future.
+  const CFGBlock *B = CalleeCtx->getCallSiteBlock();
+  CFGElement E = (*B)[CalleeCtx->getIndex()];
+  assert(isa<CFGImplicitDtor>(E) && "All other CFG elements should have exprs");
+  assert(!isa<CFGTemporaryDtor>(E) && "We don't handle temporaries yet");
+
+  SValBuilder &SVB = State->getStateManager().getSValBuilder();
+  const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CalleeCtx->getDecl());
+  Loc ThisPtr = SVB.getCXXThis(Dtor, CalleeCtx);
+  SVal ThisVal = State->getSVal(ThisPtr);
+
+  const Stmt *Trigger;
+  if (const CFGAutomaticObjDtor *AutoDtor = dyn_cast<CFGAutomaticObjDtor>(&E))
+    Trigger = AutoDtor->getTriggerStmt();
+  else
+    Trigger = Dtor->getBody();
+
+  return getCXXDestructorCall(Dtor, Trigger, ThisVal.getAsRegion(),
+                              State, CallerCtx);
+}
+
index e1d6a261932504e44723c342e8532a933c5a9696..564404e5653e9238a0068d5235c02b40d074de1e 100644 (file)
@@ -140,7 +140,7 @@ namespace {
     const CheckersTy &Checkers;
     const Stmt *S;
     ExprEngine &Eng;
-    bool wasInlined;
+    bool WasInlined;
 
     CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
     CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
@@ -148,7 +148,7 @@ namespace {
     CheckStmtContext(bool isPreVisit, const CheckersTy &checkers,
                      const Stmt *s, ExprEngine &eng, bool wasInlined = false)
       : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng),
-        wasInlined(wasInlined) {}
+        WasInlined(wasInlined) {}
 
     void runChecker(CheckerManager::CheckStmtFunc checkFn,
                     NodeBuilder &Bldr, ExplodedNode *Pred) {
@@ -157,7 +157,7 @@ namespace {
                                            ProgramPoint::PostStmtKind;
       const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
                                 Pred->getLocationContext(), checkFn.Checker);
-      CheckerContext C(Bldr, Eng, Pred, L, wasInlined);
+      CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
       checkFn(S, C);
     }
   };
@@ -169,16 +169,16 @@ void CheckerManager::runCheckersForStmt(bool isPreVisit,
                                         const ExplodedNodeSet &Src,
                                         const Stmt *S,
                                         ExprEngine &Eng,
-                                        bool wasInlined) {
+                                        bool WasInlined) {
   CheckStmtContext C(isPreVisit, *getCachedStmtCheckersFor(S, isPreVisit),
-                     S, Eng, wasInlined);
+                     S, Eng, WasInlined);
   expandGraphWithCheckers(C, Dst, Src);
 }
 
 namespace {
   struct CheckObjCMessageContext {
     typedef std::vector<CheckerManager::CheckObjCMessageFunc> CheckersTy;
-    bool IsPreVisit;
+    bool IsPreVisit, WasInlined;
     const CheckersTy &Checkers;
     const ObjCMethodCall &Msg;
     ExprEngine &Eng;
@@ -187,13 +187,15 @@ namespace {
     CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
 
     CheckObjCMessageContext(bool isPreVisit, const CheckersTy &checkers,
-                            const ObjCMethodCall &msg, ExprEngine &eng)
-      : IsPreVisit(isPreVisit), Checkers(checkers), Msg(msg), Eng(eng) { }
+                            const ObjCMethodCall &msg, ExprEngine &eng,
+                            bool wasInlined)
+      : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers),
+        Msg(msg), Eng(eng) { }
 
     void runChecker(CheckerManager::CheckObjCMessageFunc checkFn,
                     NodeBuilder &Bldr, ExplodedNode *Pred) {
       const ProgramPoint &L = Msg.getProgramPoint(IsPreVisit,checkFn.Checker);
-      CheckerContext C(Bldr, Eng, Pred, L);
+      CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
 
       checkFn(*Msg.cloneWithState<ObjCMethodCall>(Pred->getState()), C);
     }
@@ -205,11 +207,12 @@ void CheckerManager::runCheckersForObjCMessage(bool isPreVisit,
                                                ExplodedNodeSet &Dst,
                                                const ExplodedNodeSet &Src,
                                                const ObjCMethodCall &msg,
-                                               ExprEngine &Eng) {
+                                               ExprEngine &Eng,
+                                               bool WasInlined) {
   CheckObjCMessageContext C(isPreVisit,
                             isPreVisit ? PreObjCMessageCheckers
                                        : PostObjCMessageCheckers,
-                            msg, Eng);
+                            msg, Eng, WasInlined);
   expandGraphWithCheckers(C, Dst, Src);
 }
 
@@ -218,7 +221,7 @@ namespace {
   // Is there a way we can merge the two?
   struct CheckCallContext {
     typedef std::vector<CheckerManager::CheckCallFunc> CheckersTy;
-    bool IsPreVisit;
+    bool IsPreVisit, WasInlined;
     const CheckersTy &Checkers;
     const CallEvent &Call;
     ExprEngine &Eng;
@@ -227,13 +230,15 @@ namespace {
     CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
 
     CheckCallContext(bool isPreVisit, const CheckersTy &checkers,
-                     const CallEvent &call, ExprEngine &eng)
-    : IsPreVisit(isPreVisit), Checkers(checkers), Call(call), Eng(eng) { }
+                     const CallEvent &call, ExprEngine &eng,
+                     bool wasInlined)
+    : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers),
+      Call(call), Eng(eng) { }
 
     void runChecker(CheckerManager::CheckCallFunc checkFn,
                     NodeBuilder &Bldr, ExplodedNode *Pred) {
       const ProgramPoint &L = Call.getProgramPoint(IsPreVisit,checkFn.Checker);
-      CheckerContext C(Bldr, Eng, Pred, L);
+      CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
 
       checkFn(*Call.cloneWithState(Pred->getState()), C);
     }
@@ -245,11 +250,12 @@ void CheckerManager::runCheckersForCallEvent(bool isPreVisit,
                                              ExplodedNodeSet &Dst,
                                              const ExplodedNodeSet &Src,
                                              const CallEvent &Call,
-                                             ExprEngine &Eng) {
+                                             ExprEngine &Eng,
+                                             bool WasInlined) {
   CheckCallContext C(isPreVisit,
                      isPreVisit ? PreCallCheckers
                                 : PostCallCheckers,
-                     Call, Eng);
+                     Call, Eng, WasInlined);
   expandGraphWithCheckers(C, Dst, Src);
 }
 
index 5e9f3f3089327377cf76a0323ca1a527cace08af..47d732e51525e181bb3235e1fc066162b4d7a7cb 100644 (file)
@@ -132,7 +132,7 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
     if (const ReturnStmt *RS = dyn_cast_or_null<ReturnStmt>(LastSt)) {
       const LocationContext *LCtx = CEBNode->getLocationContext();
       SVal V = state->getSVal(RS, LCtx);
-      state = state->BindExpr(CE, calleeCtx->getParent(), V);
+      state = state->BindExpr(CE, callerCtx, V);
     }
 
     // Bind the constructed object value to CXXConstructExpr.
@@ -142,7 +142,7 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
       SVal ThisV = state->getSVal(This);
 
       // Always bind the region to the CXXConstructExpr.
-      state = state->BindExpr(CCE, calleeCtx->getParent(), ThisV);
+      state = state->BindExpr(CCE, callerCtx, ThisV);
     }
   }
 
@@ -190,19 +190,29 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
     // Step 5: Perform the post-condition check of the CallExpr and enqueue the
     // result onto the work list.
     // CEENode -> Dst -> WorkList
-    ExplodedNodeSet Dst;
     NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), CEENode);
     SaveAndRestore<const NodeBuilderContext*> NBCSave(currentBuilderContext,
         &Ctx);
     SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex());
 
-    // FIXME: This needs to call PostCall.
-    // FIXME: If/when we inline Objective-C messages, this also needs to call
-    // PostObjCMessage.
-    if (CE)
-      getCheckerManager().runCheckersForPostStmt(Dst, CEENode, CE, *this, true);
-    else
-      Dst.Add(CEENode);
+    CallEventManager &CEMgr = getStateManager().getCallEventManager();
+    CallEventRef<> Call = CEMgr.getCaller(calleeCtx, CEEState);
+
+    ExplodedNodeSet DstPostCall;
+    getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode, *Call,
+                                               *this, true);
+
+    ExplodedNodeSet Dst;
+    if (isa<ObjCMethodCall>(Call)) {
+      getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall,
+                                                    cast<ObjCMethodCall>(*Call),
+                                                        *this, true);
+    } else if (CE) {
+      getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE,
+                                                 *this, true);
+    } else {
+      Dst.insert(DstPostCall);
+    }
 
     // Enqueue the next element in the block.
     for (ExplodedNodeSet::iterator PSI = Dst.begin(), PSE = Dst.end();