]> granicus.if.org Git - clang/commitdiff
[analyzer] Add checker callback for beginning of function.
authorDevin Coughlin <dcoughlin@apple.com>
Fri, 19 Feb 2016 01:35:10 +0000 (01:35 +0000)
committerDevin Coughlin <dcoughlin@apple.com>
Fri, 19 Feb 2016 01:35:10 +0000 (01:35 +0000)
Add a checker callback that is called when the analyzer starts analyzing a
function either at the top level or when inlined. This will be used by a
follow-on patch making the DeallocChecker path sensitive.

Differential Revision: http://reviews.llvm.org/D17418

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

14 files changed:
include/clang/Analysis/ProgramPoint.h
include/clang/StaticAnalyzer/Core/Checker.h
include/clang/StaticAnalyzer/Core/CheckerManager.h
include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h
lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
lib/StaticAnalyzer/Checkers/TraversalChecker.cpp
lib/StaticAnalyzer/Core/CheckerManager.cpp
lib/StaticAnalyzer/Core/CoreEngine.cpp
lib/StaticAnalyzer/Core/ExprEngine.cpp
lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
test/Analysis/traversal-begin-end-function.c [new file with mode: 0644]
test/Analysis/traversal-path-unification.c

index 6d816fd733ec53c693ce5045372044baf6820510..5045194ef8646bcad35dff8d917cdd74a989282f 100644 (file)
@@ -595,6 +595,13 @@ public:
     return static_cast<const StackFrameContext *>(getData2());
   }
 
+  /// Returns the entry block in the CFG for the entered function.
+  const CFGBlock *getEntry() const {
+    const StackFrameContext *CalleeCtx = getCalleeContext();
+    const CFG *CalleeCFG = CalleeCtx->getCFG();
+    return &(CalleeCFG->getEntry());
+  }
+
 private:
   friend class ProgramPoint;
   CallEnter() {}
index 1410af156815b63afe3eff27e4a05aced6d33a75..137f238cced94671787c0f3d70d927b8834ad004 100644 (file)
@@ -238,6 +238,20 @@ public:
   }
 };
 
+class BeginFunction {
+  template <typename CHECKER>
+  static void _checkBeginFunction(void *checker, CheckerContext &C) {
+    ((const CHECKER *)checker)->checkBeginFunction(C);
+  }
+
+public:
+  template <typename CHECKER>
+  static void _register(CHECKER *checker, CheckerManager &mgr) {
+    mgr._registerForBeginFunction(CheckerManager::CheckBeginFunctionFunc(
+        checker, _checkBeginFunction<CHECKER>));
+  }
+};
+
 class EndFunction {
   template <typename CHECKER>
   static void _checkEndFunction(void *checker,
index bc9af496b054a6b9d0841206c09a60101463a03c..612e1056a79ca87577cf930c5ef4d6fd3ebd1db9 100644 (file)
@@ -287,6 +287,12 @@ public:
   void runCheckersForEndAnalysis(ExplodedGraph &G, BugReporter &BR,
                                  ExprEngine &Eng);
 
+  /// \brief Run checkers on begining of function.
+  void runCheckersForBeginFunction(ExplodedNodeSet &Dst,
+                                   const BlockEdge &L,
+                                   ExplodedNode *Pred,
+                                   ExprEngine &Eng);
+
   /// \brief Run checkers on end of function.
   void runCheckersForEndFunction(NodeBuilderContext &BC,
                                  ExplodedNodeSet &Dst,
@@ -425,7 +431,10 @@ public:
   
   typedef CheckerFn<void (ExplodedGraph &, BugReporter &, ExprEngine &)>
       CheckEndAnalysisFunc;
-  
+
+  typedef CheckerFn<void (CheckerContext &)>
+      CheckBeginFunctionFunc;
+
   typedef CheckerFn<void (CheckerContext &)>
       CheckEndFunctionFunc;
   
@@ -484,6 +493,7 @@ public:
 
   void _registerForEndAnalysis(CheckEndAnalysisFunc checkfn);
 
+  void _registerForBeginFunction(CheckEndFunctionFunc checkfn);
   void _registerForEndFunction(CheckEndFunctionFunc checkfn);
 
   void _registerForBranchCondition(CheckBranchConditionFunc checkfn);
@@ -593,6 +603,7 @@ private:
 
   std::vector<CheckEndAnalysisFunc> EndAnalysisCheckers;
 
+  std::vector<CheckBeginFunctionFunc> BeginFunctionCheckers;
   std::vector<CheckEndFunctionFunc> EndFunctionCheckers;
 
   std::vector<CheckBranchConditionFunc> BranchConditionCheckers;
index d5822e22448199ff93c51f6707c808b0294f158b..0fa736d76c99d779056d5f558d3d09b964e8c113 100644 (file)
@@ -91,6 +91,9 @@ private:
   void HandleBlockEdge(const BlockEdge &E, ExplodedNode *Pred);
   void HandleBlockEntrance(const BlockEntrance &E, ExplodedNode *Pred);
   void HandleBlockExit(const CFGBlock *B, ExplodedNode *Pred);
+
+  void HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred);
+
   void HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, ExplodedNode *Pred);
 
   void HandleBranch(const Stmt *Cond, const Stmt *Term, const CFGBlock *B,
index 99083c9d6135b7d5638d228d6cd462579a565123..ec96560a2f0be09f87d99e3b887e744652666645 100644 (file)
@@ -253,8 +253,14 @@ public:
   ///  nodes by processing the 'effects' of a switch statement.
   void processSwitch(SwitchNodeBuilder& builder) override;
 
-  /// Called by CoreEngine.  Used to generate end-of-path
-  /// nodes when the control reaches the end of a function.
+  /// Called by CoreEngine.  Used to notify checkers that processing a
+  /// function has begun. Called for both inlined and and top-level functions.
+  void processBeginOfFunction(NodeBuilderContext &BC,
+                              ExplodedNode *Pred, ExplodedNodeSet &Dst,
+                              const BlockEdge &L) override;
+
+  /// Called by CoreEngine.  Used to notify checkers that processing a
+  /// function has ended. Called for both inlined and and top-level functions.
   void processEndOfFunction(NodeBuilderContext& BC,
                             ExplodedNode *Pred) override;
 
@@ -264,7 +270,8 @@ public:
                                  ExplodedNodeSet &Dst);
 
   /// Generate the entry node of the callee.
-  void processCallEnter(CallEnter CE, ExplodedNode *Pred) override;
+  void processCallEnter(NodeBuilderContext& BC, CallEnter CE,
+                        ExplodedNode *Pred) override;
 
   /// Generate the sequence of nodes that simulate the call exit and the post
   /// visit for CallExpr.
index 741ba0e2f290ab0d798ac3d1db96888141cb61d3..1a731cf865ea37790f64f16e51cbc86723ff457c 100644 (file)
@@ -99,13 +99,21 @@ public:
   /// nodes by processing the 'effects' of a switch statement.
   virtual void processSwitch(SwitchNodeBuilder& builder) = 0;
 
-  /// Called by CoreEngine.  Used to generate end-of-path
-  /// nodes when the control reaches the end of a function.
+  /// Called by CoreEngine.  Used to notify checkers that processing a
+  /// function has begun. Called for both inlined and and top-level functions.
+  virtual void processBeginOfFunction(NodeBuilderContext &BC,
+                                      ExplodedNode *Pred,
+                                      ExplodedNodeSet &Dst,
+                                      const BlockEdge &L) = 0;
+
+  /// Called by CoreEngine.  Used to notify checkers that processing a
+  /// function has ended. Called for both inlined and and top-level functions.
   virtual void processEndOfFunction(NodeBuilderContext& BC,
                                     ExplodedNode *Pred) = 0;
 
   // Generate the entry node of the callee.
-  virtual void processCallEnter(CallEnter CE, ExplodedNode *Pred) = 0;
+  virtual void processCallEnter(NodeBuilderContext& BC, CallEnter CE,
+                                ExplodedNode *Pred) = 0;
 
   // Generate the first post callsite node.
   virtual void processCallExit(ExplodedNode *Pred) = 0;
index 00c7ee9b5ec2065a08989ba33856a19f8651017b..74d05e27e8eb00cb11eb23d7211afc417e8b6fee 100644 (file)
@@ -162,8 +162,16 @@ public:
   /// check::DeadSymbols
   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const {}
 
+
+  /// \brief Called when the analyzer core starts analyzing a function,
+  /// regardless of whether it is analyzed at the top level or is inlined.
+  ///
+  /// check::BeginFunction
+  void checkBeginFunction(CheckerContext &Ctx) const {}
+
   /// \brief Called when the analyzer core reaches the end of a
-  /// function being analyzed.
+  /// function being analyzed regardless of whether it is analyzed at the top
+  /// level or is inlined.
   ///
   /// check::EndFunction
   void checkEndFunction(CheckerContext &Ctx) const {}
index d02d2df1c5070bc313f0d9b2110dcdc91d73222a..8ad962875b060dee6ea9205cfa4421aa5053d43d 100644 (file)
@@ -25,9 +25,11 @@ using namespace ento;
 
 namespace {
 class TraversalDumper : public Checker< check::BranchCondition,
+                                        check::BeginFunction,
                                         check::EndFunction > {
 public:
   void checkBranchCondition(const Stmt *Condition, CheckerContext &C) const;
+  void checkBeginFunction(CheckerContext &C) const;
   void checkEndFunction(CheckerContext &C) const;
 };
 }
@@ -50,6 +52,10 @@ void TraversalDumper::checkBranchCondition(const Stmt *Condition,
                << Parent->getStmtClassName() << "\n";
 }
 
+void TraversalDumper::checkBeginFunction(CheckerContext &C) const {
+  llvm::outs() << "--BEGIN FUNCTION--\n";
+}
+
 void TraversalDumper::checkEndFunction(CheckerContext &C) const {
   llvm::outs() << "--END FUNCTION--\n";
 }
index 008e8ef31cda74b7d3f36c95cba6b41f60a1cd0e..d8382e88691a54f330c623b013bb5502c205a3d4 100644 (file)
@@ -377,6 +377,40 @@ void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G,
     EndAnalysisCheckers[i](G, BR, Eng);
 }
 
+namespace {
+struct CheckBeginFunctionContext {
+  typedef std::vector<CheckerManager::CheckBeginFunctionFunc> CheckersTy;
+  const CheckersTy &Checkers;
+  ExprEngine &Eng;
+  const ProgramPoint &PP;
+
+  CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+  CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+  CheckBeginFunctionContext(const CheckersTy &Checkers, ExprEngine &Eng,
+                            const ProgramPoint &PP)
+      : Checkers(Checkers), Eng(Eng), PP(PP) {}
+
+  void runChecker(CheckerManager::CheckBeginFunctionFunc checkFn,
+                  NodeBuilder &Bldr, ExplodedNode *Pred) {
+    const ProgramPoint &L = PP.withTag(checkFn.Checker);
+    CheckerContext C(Bldr, Eng, Pred, L);
+
+    checkFn(C);
+  }
+};
+}
+
+void CheckerManager::runCheckersForBeginFunction(ExplodedNodeSet &Dst,
+                                                 const BlockEdge &L,
+                                                 ExplodedNode *Pred,
+                                                 ExprEngine &Eng) {
+  ExplodedNodeSet Src;
+  Src.insert(Pred);
+  CheckBeginFunctionContext C(BeginFunctionCheckers, Eng, L);
+  expandGraphWithCheckers(C, Dst, Src);
+}
+
 /// \brief Run checkers for end of path.
 // Note, We do not chain the checker output (like in expandGraphWithCheckers)
 // for this callback since end of path nodes are expected to be final.
@@ -671,6 +705,10 @@ void CheckerManager::_registerForEndAnalysis(CheckEndAnalysisFunc checkfn) {
   EndAnalysisCheckers.push_back(checkfn);
 }
 
+void CheckerManager::_registerForBeginFunction(CheckBeginFunctionFunc checkfn) {
+  BeginFunctionCheckers.push_back(checkfn);
+}
+
 void CheckerManager::_registerForEndFunction(CheckEndFunctionFunc checkfn) {
   EndFunctionCheckers.push_back(checkfn);
 }
index 39cf7e771755dfe1a448b2ff5d5c7498b3b6fa66..c75fb2e763db2e2429fc3507cbecee1285feaab6 100644 (file)
@@ -192,10 +192,18 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
     WList->setBlockCounter(BCounterFactory.GetEmptyCounter());
 
     if (!InitState)
-      // Generate the root.
-      generateNode(StartLoc, SubEng.getInitialState(L), nullptr);
-    else
-      generateNode(StartLoc, InitState, nullptr);
+      InitState = SubEng.getInitialState(L);
+
+    bool IsNew;
+    ExplodedNode *Node = G.getNode(StartLoc, InitState, false, &IsNew);
+    assert (IsNew);
+    G.addRoot(Node);
+
+    NodeBuilderContext BuilderCtx(*this, StartLoc.getDst(), Node);
+    ExplodedNodeSet DstBegin;
+    SubEng.processBeginOfFunction(BuilderCtx, Node, DstBegin, StartLoc);
+
+    enqueue(DstBegin);
   }
 
   // Check if we have a steps limit
@@ -243,8 +251,7 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc,
       break;
 
     case ProgramPoint::CallEnterKind: {
-      CallEnter CEnter = Loc.castAs<CallEnter>();
-      SubEng.processCallEnter(CEnter, Pred);
+      HandleCallEnter(Loc.castAs<CallEnter>(), Pred);
       break;
     }
 
@@ -456,6 +463,11 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
                Pred->State, Pred);
 }
 
+void CoreEngine::HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred) {
+  NodeBuilderContext BuilderCtx(*this, CE.getEntry(), Pred);
+  SubEng.processCallEnter(BuilderCtx, CE, Pred);
+}
+
 void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
                                 const CFGBlock * B, ExplodedNode *Pred) {
   assert(B->succ_size() == 2);
index 22b32f8414131b4c5ede5624204c7b91dee24871..0b669336a380da7aa8244d0ca15b879c6b689bf7 100644 (file)
@@ -30,6 +30,7 @@
 #include "llvm/ADT/ImmutableList.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/SaveAndRestore.h"
 
 #ifndef NDEBUG
 #include "llvm/Support/GraphWriter.h"
@@ -1749,6 +1750,14 @@ static bool stackFrameDoesNotContainInitializedTemporaries(ExplodedNode &Pred) {
 }
 #endif
 
+void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC,
+                                        ExplodedNode *Pred,
+                                        ExplodedNodeSet &Dst,
+                                        const BlockEdge &L) {
+  SaveAndRestore<const NodeBuilderContext *> NodeContextRAII(currBldrCtx, &BC);
+  getCheckerManager().runCheckersForBeginFunction(Dst, L, Pred, *this);
+}
+
 /// ProcessEndPath - Called by CoreEngine.  Used to generate end-of-path
 ///  nodes when the control reaches the end of a function.
 void ExprEngine::processEndOfFunction(NodeBuilderContext& BC,
index e667194198c3d383793a8db6633c6034f0d64188..39d88bfda14864dbfa3137b7a21e4b018a4746e6 100644 (file)
@@ -37,13 +37,12 @@ STATISTIC(NumInlinedCalls,
 STATISTIC(NumReachedInlineCountMax,
   "The # of times we reached inline count maximum");
 
-void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
+void ExprEngine::processCallEnter(NodeBuilderContext& BC, CallEnter CE,
+                                  ExplodedNode *Pred) {
   // Get the entry block in the CFG of the callee.
   const StackFrameContext *calleeCtx = CE.getCalleeContext();
   PrettyStackTraceLocationContext CrashInfo(calleeCtx);
-
-  const CFG *CalleeCFG = calleeCtx->getCFG();
-  const CFGBlock *Entry = &(CalleeCFG->getEntry());
+  const CFGBlock *Entry = CE.getEntry();
 
   // Validate the CFG.
   assert(Entry->empty());
@@ -57,12 +56,16 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
 
   ProgramStateRef state = Pred->getState();
 
-  // Construct a new node and add it to the worklist.
+  // Construct a new node, notify checkers that analysis of the function has
+  // begun, and add the resultant nodes to the worklist.
   bool isNew;
   ExplodedNode *Node = G.getNode(Loc, state, false, &isNew);
   Node->addPredecessor(Pred, G);
-  if (isNew)
-    Engine.getWorkList()->enqueue(Node);
+  if (isNew) {
+    ExplodedNodeSet DstBegin;
+    processBeginOfFunction(BC, Node, DstBegin, Loc);
+    Engine.enqueue(DstBegin);
+  }
 }
 
 // Find the last statement on the path to the exploded node and the
diff --git a/test/Analysis/traversal-begin-end-function.c b/test/Analysis/traversal-begin-end-function.c
new file mode 100644 (file)
index 0000000..810ce1d
--- /dev/null
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.DumpTraversal %s | FileCheck %s
+
+void inline_callee(int i);
+
+// CHECK: --BEGIN FUNCTION--
+void inline_caller() {
+  // CHECK: --BEGIN FUNCTION--
+  // CHECK: --BEGIN FUNCTION--
+  // CHECK: --BEGIN FUNCTION--
+  inline_callee(3);
+  // CHECK: --END FUNCTION--
+  // CHECK: --END FUNCTION--
+  // CHECK: --END FUNCTION--
+}
+// CHECK: --END FUNCTION--
+
+void inline_callee(int i) {
+  if (i <= 1)
+    return;
+
+  inline_callee(i - 1);
+}
index 83e3b87c2bb347488982e4280ad1aa951f687bbf..3bf6df731c2275f3761f94aab742f9b6dada23ea 100644 (file)
@@ -11,6 +11,7 @@ int c();
 #define CHECK(x) (x)
 #endif
 
+// CHECK: --BEGIN FUNCTION--
 void testRemoveDeadBindings() {
   int i = a();
   if (CHECK(i))