diff options
-rw-r--r-- | include/clang/StaticAnalyzer/PathSensitive/CoreEngine.h | 45 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/PathSensitive/ExprEngine.h | 10 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/PathSensitive/SubEngine.h | 16 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/ExprEngine.cpp | 21 | ||||
-rw-r--r-- | lib/StaticAnalyzer/CoreEngine.cpp | 49 | ||||
-rw-r--r-- | test/Analysis/retain-release-region-store.m | 2 |
6 files changed, 118 insertions, 25 deletions
diff --git a/include/clang/StaticAnalyzer/PathSensitive/CoreEngine.h b/include/clang/StaticAnalyzer/PathSensitive/CoreEngine.h index 08a3660cd6..44a321c2f7 100644 --- a/include/clang/StaticAnalyzer/PathSensitive/CoreEngine.h +++ b/include/clang/StaticAnalyzer/PathSensitive/CoreEngine.h @@ -37,6 +37,7 @@ namespace ento { /// any transfer function logic and the sub-expression level (if any). class CoreEngine { friend class StmtNodeBuilder; + friend class GenericNodeBuilderImpl; friend class BranchNodeBuilder; friend class IndirectGotoNodeBuilder; friend class SwitchNodeBuilder; @@ -397,6 +398,50 @@ public: const GRState* getState() const { return Pred->State; } }; +class GenericNodeBuilderImpl { +protected: + CoreEngine &engine; + ExplodedNode *pred; + bool HasGeneratedNode; + ProgramPoint pp; + llvm::SmallVector<ExplodedNode*, 2> sinksGenerated; + + ExplodedNode *generateNodeImpl(const GRState *state, ExplodedNode *pred, + ProgramPoint programPoint, bool asSink); + + GenericNodeBuilderImpl(CoreEngine &eng, ExplodedNode *pr, ProgramPoint p) + : engine(eng), pred(pr), HasGeneratedNode(false), pp(p) {} + +public: + bool hasGeneratedNode() const { return HasGeneratedNode; } + + WorkList &getWorkList() { return *engine.WList; } + + ExplodedNode* getPredecessor() const { return pred; } + + BlockCounter getBlockCounter() const { + return engine.WList->getBlockCounter(); + } + + const llvm::SmallVectorImpl<ExplodedNode*> &sinks() const { + return sinksGenerated; + } +}; + +template <typename PP> +class GenericNodeBuilder : public GenericNodeBuilderImpl { +public: + GenericNodeBuilder(CoreEngine &eng, ExplodedNode *pr, const PP &p) + : GenericNodeBuilderImpl(eng, pr, p) {} + + ExplodedNode *generateNode(const GRState *state, ExplodedNode *pred, + PP programPoint, bool asSink) { + return generateNodeImpl(state, pred, programPoint, asSink); + } + + const PP &getProgramPoint() const { return cast<PP>(pp); } +}; + class EndOfFunctionNodeBuilder { CoreEngine &Eng; const CFGBlock& B; diff --git a/include/clang/StaticAnalyzer/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/PathSensitive/ExprEngine.h index 2991cc7864..747c8ebb2a 100644 --- a/include/clang/StaticAnalyzer/PathSensitive/ExprEngine.h +++ b/include/clang/StaticAnalyzer/PathSensitive/ExprEngine.h @@ -193,12 +193,10 @@ public: void ProcessTemporaryDtor(const CFGTemporaryDtor D, StmtNodeBuilder &builder); - /// processCFGBlockEntrance - Called by CoreEngine when start processing - /// a CFGBlock. This method returns true if the analysis should continue - /// exploring the given path, and false otherwise. - bool processCFGBlockEntrance(const CFGBlock* B, const ExplodedNode *Pred, - BlockCounter BC); - + /// Called by CoreEngine when processing the entrance of a CFGBlock. + virtual void processCFGBlockEntrance(ExplodedNodeSet &dstNodes, + GenericNodeBuilder<BlockEntrance> &nodeBuilder); + /// ProcessBranch - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a branch condition. void processBranch(const Stmt* Condition, const Stmt* Term, diff --git a/include/clang/StaticAnalyzer/PathSensitive/SubEngine.h b/include/clang/StaticAnalyzer/PathSensitive/SubEngine.h index d82e19a707..caf5b389ac 100644 --- a/include/clang/StaticAnalyzer/PathSensitive/SubEngine.h +++ b/include/clang/StaticAnalyzer/PathSensitive/SubEngine.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_GR_SUBENGINE_H #define LLVM_CLANG_GR_SUBENGINE_H +#include "clang/Analysis/ProgramPoint.h" #include "clang/StaticAnalyzer/PathSensitive/SVals.h" namespace clang { @@ -23,8 +24,10 @@ class LocationContext; class Stmt; namespace ento { - + +template <typename PP> class GenericNodeBuilder; class AnalysisManager; +class ExplodedNodeSet; class ExplodedNode; class GRState; class GRStateManager; @@ -52,12 +55,11 @@ public: /// nodes by processing the 'effects' of a block-level statement. virtual void processCFGElement(const CFGElement E, StmtNodeBuilder& builder)=0; - /// Called by CoreEngine when start processing - /// a CFGBlock. This method returns true if the analysis should continue - /// exploring the given path, and false otherwise. - virtual bool processCFGBlockEntrance(const CFGBlock* B, - const ExplodedNode *Pred, - BlockCounter BC) = 0; + /// Called by CoreEngine when it starts processing a CFGBlock. The + /// SubEngine is expected to populate dstNodes with new nodes representing + /// updated analysis state, or generate no nodes at all if it doesn't. + virtual void processCFGBlockEntrance(ExplodedNodeSet &dstNodes, + GenericNodeBuilder<BlockEntrance> &nodeBuilder) = 0; /// Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a branch condition. diff --git a/lib/StaticAnalyzer/Checkers/ExprEngine.cpp b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp index 5dd5d72a4c..e6d9d715d4 100644 --- a/lib/StaticAnalyzer/Checkers/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp @@ -1077,11 +1077,22 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, // Block entrance. (Update counters). //===----------------------------------------------------------------------===// -bool ExprEngine::processCFGBlockEntrance(const CFGBlock* B, - const ExplodedNode *Pred, - BlockCounter BC) { - return BC.getNumVisited(Pred->getLocationContext()->getCurrentStackFrame(), - B->getBlockID()) < AMgr.getMaxVisit(); +void ExprEngine::processCFGBlockEntrance(ExplodedNodeSet &dstNodes, + GenericNodeBuilder<BlockEntrance> &nodeBuilder){ + + // FIXME: Refactor this into a checker. + const CFGBlock *block = nodeBuilder.getProgramPoint().getBlock(); + ExplodedNode *pred = nodeBuilder.getPredecessor(); + + if (nodeBuilder.getBlockCounter().getNumVisited( + pred->getLocationContext()->getCurrentStackFrame(), + block->getBlockID()) >= AMgr.getMaxVisit()) { + + static int tag = 0; + const BlockEntrance &BE = nodeBuilder.getProgramPoint(); + BlockEntrance BE_tagged(BE.getBlock(), BE.getLocationContext(), &tag); + nodeBuilder.generateNode(pred->getState(), pred, BE_tagged, true); + } } //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/CoreEngine.cpp b/lib/StaticAnalyzer/CoreEngine.cpp index d5158ad168..9ccc4472f4 100644 --- a/lib/StaticAnalyzer/CoreEngine.cpp +++ b/lib/StaticAnalyzer/CoreEngine.cpp @@ -288,13 +288,29 @@ void CoreEngine::HandleBlockEdge(const BlockEdge& L, ExplodedNode* Pred) { return; } - // FIXME: Should we allow processCFGBlockEntrance to also manipulate state? - - if (SubEng.processCFGBlockEntrance(Blk, Pred, WList->getBlockCounter())) - generateNode(BlockEntrance(Blk, Pred->getLocationContext()), - Pred->State, Pred); + // Call into the subengine to process entering the CFGBlock. + ExplodedNodeSet dstNodes; + BlockEntrance BE(Blk, Pred->getLocationContext()); + GenericNodeBuilder<BlockEntrance> nodeBuilder(*this, Pred, BE); + SubEng.processCFGBlockEntrance(dstNodes, nodeBuilder); + + if (dstNodes.empty()) { + if (!nodeBuilder.hasGeneratedNode()) { + // Auto-generate a node and enqueue it to the worklist. + generateNode(BE, Pred->State, Pred); + } + } else { - blocksAborted.push_back(std::make_pair(L, Pred)); + for (ExplodedNodeSet::iterator I = dstNodes.begin(), E = dstNodes.end(); + I != E; ++I) { + WList->enqueue(*I); + } + } + + for (llvm::SmallVectorImpl<ExplodedNode*>::const_iterator + I = nodeBuilder.sinks().begin(), E = nodeBuilder.sinks().end(); + I != E; ++I) { + blocksAborted.push_back(std::make_pair(L, *I)); } } @@ -446,6 +462,27 @@ void CoreEngine::generateNode(const ProgramPoint& Loc, if (IsNew) WList->enqueue(Node); } +ExplodedNode * +GenericNodeBuilderImpl::generateNodeImpl(const GRState *state, + ExplodedNode *pred, + ProgramPoint programPoint, + bool asSink) { + + HasGeneratedNode = true; + bool isNew; + ExplodedNode *node = engine.getGraph().getNode(programPoint, state, &isNew); + if (pred) + node->addPredecessor(pred, engine.getGraph()); + if (isNew) { + if (asSink) { + node->markAsSink(); + sinksGenerated.push_back(node); + } + return node; + } + return 0; +} + StmtNodeBuilder::StmtNodeBuilder(const CFGBlock* b, unsigned idx, ExplodedNode* N, CoreEngine* e, GRStateManager &mgr) diff --git a/test/Analysis/retain-release-region-store.m b/test/Analysis/retain-release-region-store.m index 7b9855473d..05b91fcf5c 100644 --- a/test/Analysis/retain-release-region-store.m +++ b/test/Analysis/retain-release-region-store.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=region -verify %s +// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=region -analyzer-max-loop 6 -verify %s //===----------------------------------------------------------------------===// // The following code is reduced using delta-debugging from |