Index: include/clang/Analysis/ProgramPoint.h =================================================================== --- include/clang/Analysis/ProgramPoint.h +++ include/clang/Analysis/ProgramPoint.h @@ -595,6 +595,13 @@ return static_cast(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: include/clang/StaticAnalyzer/Core/Checker.h =================================================================== --- include/clang/StaticAnalyzer/Core/Checker.h +++ include/clang/StaticAnalyzer/Core/Checker.h @@ -238,6 +238,20 @@ } }; +class BeginFunction { + template + static void _checkBeginFunction(void *checker, CheckerContext &C) { + ((const CHECKER *)checker)->checkBeginFunction(C); + } + +public: + template + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForBeginFunction(CheckerManager::CheckBeginFunctionFunc( + checker, _checkBeginFunction)); + } +}; + class EndFunction { template static void _checkEndFunction(void *checker, Index: include/clang/StaticAnalyzer/Core/CheckerManager.h =================================================================== --- include/clang/StaticAnalyzer/Core/CheckerManager.h +++ include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -287,6 +287,12 @@ 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 @@ typedef CheckerFn CheckEndAnalysisFunc; - + + typedef CheckerFn + CheckBeginFunctionFunc; + typedef CheckerFn CheckEndFunctionFunc; @@ -484,6 +493,7 @@ void _registerForEndAnalysis(CheckEndAnalysisFunc checkfn); + void _registerForBeginFunction(CheckEndFunctionFunc checkfn); void _registerForEndFunction(CheckEndFunctionFunc checkfn); void _registerForBranchCondition(CheckBranchConditionFunc checkfn); @@ -593,6 +603,7 @@ std::vector EndAnalysisCheckers; + std::vector BeginFunctionCheckers; std::vector EndFunctionCheckers; std::vector BranchConditionCheckers; Index: include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h @@ -91,6 +91,9 @@ 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: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -253,6 +253,12 @@ /// nodes by processing the 'effects' of a switch statement. void processSwitch(SwitchNodeBuilder& builder) override; + /// 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 generate end-of-path /// nodes when the control reaches the end of a function. void processEndOfFunction(NodeBuilderContext& BC, @@ -264,7 +270,8 @@ 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: include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h @@ -99,13 +99,21 @@ /// nodes by processing the 'effects' of a switch statement. virtual void processSwitch(SwitchNodeBuilder& builder) = 0; + /// 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 generate end-of-path /// nodes when the control reaches the end of a function. 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: lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp +++ lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp @@ -162,6 +162,13 @@ /// 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. /// Index: lib/StaticAnalyzer/Checkers/TraversalChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/TraversalChecker.cpp +++ lib/StaticAnalyzer/Checkers/TraversalChecker.cpp @@ -25,9 +25,11 @@ 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 @@ << 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: lib/StaticAnalyzer/Core/CheckerManager.cpp =================================================================== --- lib/StaticAnalyzer/Core/CheckerManager.cpp +++ lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -377,6 +377,40 @@ EndAnalysisCheckers[i](G, BR, Eng); } +namespace { +struct CheckBeginFunctionContext { + typedef std::vector 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 @@ EndAnalysisCheckers.push_back(checkfn); } +void CheckerManager::_registerForBeginFunction(CheckBeginFunctionFunc checkfn) { + BeginFunctionCheckers.push_back(checkfn); +} + void CheckerManager::_registerForEndFunction(CheckEndFunctionFunc checkfn) { EndFunctionCheckers.push_back(checkfn); } Index: lib/StaticAnalyzer/Core/CoreEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/CoreEngine.cpp +++ lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -192,10 +192,18 @@ 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 @@ break; case ProgramPoint::CallEnterKind: { - CallEnter CEnter = Loc.castAs(); - SubEng.processCallEnter(CEnter, Pred); + HandleCallEnter(Loc.castAs(), Pred); break; } @@ -456,6 +463,11 @@ 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: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -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 @@ } #endif +void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const BlockEdge &L) { + SaveAndRestore 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: lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -37,13 +37,12 @@ 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()); @@ -61,8 +60,11 @@ 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 Index: test/Analysis/traversal-begin-end-function.c =================================================================== --- /dev/null +++ test/Analysis/traversal-begin-end-function.c @@ -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: test/Analysis/traversal-path-unification.c =================================================================== --- test/Analysis/traversal-path-unification.c +++ test/Analysis/traversal-path-unification.c @@ -11,6 +11,7 @@ #define CHECK(x) (x) #endif +// CHECK: --BEGIN FUNCTION-- void testRemoveDeadBindings() { int i = a(); if (CHECK(i))