diff options
-rw-r--r-- | include/clang/StaticAnalyzer/Core/CheckerManager.h | 4 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h | 4 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h | 69 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 29 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h | 4 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/CheckerManager.cpp | 15 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/CoreEngine.cpp | 14 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngine.cpp | 305 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineC.cpp | 92 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 66 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 41 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineObjC.cpp | 54 |
12 files changed, 417 insertions, 280 deletions
diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h index 7450df63c4..6da75a948a 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -19,6 +19,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "clang/Analysis/ProgramPoint.h" #include <vector> namespace clang { @@ -221,7 +222,8 @@ public: void runCheckersForBind(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, SVal location, SVal val, - const Stmt *S, ExprEngine &Eng); + const Stmt *S, ExprEngine &Eng, + ProgramPoint::Kind PointKind); /// \brief Run checkers for end of analysis. void runCheckersForEndAnalysis(ExplodedGraph &G, BugReporter &BR, diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h index afb19276da..257f9b10af 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h @@ -69,7 +69,9 @@ public: /// \brief Returns the number of times the current block has been visited /// along the analyzed path. - unsigned getCurrentBlockCount() {return NB.getCurrentBlockCount();} + unsigned getCurrentBlockCount() { + return NB.getContext().getCurrentBlockCount(); + } ASTContext &getASTContext() { return Eng.getContext(); diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h index 47682a2ac5..d981b60885 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h @@ -39,16 +39,17 @@ class NodeBuilder; /// at the statement and block-level. The analyses themselves must implement /// any transfer function logic and the sub-expression level (if any). class CoreEngine { - friend class CommonNodeBuilder; + friend struct NodeBuilderContext; friend class NodeBuilder; friend class StmtNodeBuilder; + friend class CommonNodeBuilder; friend class GenericNodeBuilderImpl; - friend class BranchNodeBuilder; friend class IndirectGotoNodeBuilder; friend class SwitchNodeBuilder; friend class EndOfFunctionNodeBuilder; friend class CallEnterNodeBuilder; friend class CallExitNodeBuilder; + friend class ExprEngine; public: typedef std::vector<std::pair<BlockEdge, const ExplodedNode*> > @@ -174,6 +175,18 @@ struct NodeBuilderContext { ExplodedNode *ContextPred; NodeBuilderContext(CoreEngine &E, const CFGBlock *B, ExplodedNode *N) : Eng(E), Block(B), ContextPred(N) { assert(B); assert(!N->isSink()); } + + /// \brief Return the CFGBlock associated with this builder. + const CFGBlock *getBlock() const { return Block; } + + /// \brief Returns the number of times the current basic block has been + /// visited on the exploded graph path. + unsigned getCurrentBlockCount() const { + return Eng.WList->getBlockCounter().getNumVisited( + ContextPred->getLocationContext()->getCurrentStackFrame(), + Block->getBlockID()); + } + }; /// This is the simplest builder which generates nodes in the ExplodedGraph. @@ -193,8 +206,6 @@ protected: /// the builder dies. ExplodedNodeSet &Frontier; - BlockCounter getBlockCounter() const { return C.Eng.WList->getBlockCounter();} - /// Checkes if the results are ready. virtual bool checkResults() { if (!Finalized) @@ -271,19 +282,8 @@ public: return Frontier.end(); } - /// \brief Return the CFGBlock associated with this builder. - const CFGBlock *getBlock() const { return C.Block; } - const NodeBuilderContext &getContext() { return C; } - /// \brief Returns the number of times the current basic block has been - /// visited on the exploded graph path. - unsigned getCurrentBlockCount() const { - return getBlockCounter().getNumVisited( - C.ContextPred->getLocationContext()->getCurrentStackFrame(), - C.Block->getBlockID()); - } - void takeNodes(const ExplodedNodeSet &S) { for (ExplodedNodeSet::iterator I = S.begin(), E = S.end(); I != E; ++I ) Frontier.erase(*I); @@ -306,7 +306,7 @@ public: class CommonNodeBuilder { protected: ExplodedNode *Pred; - CoreEngine& Eng; + CoreEngine &Eng; CommonNodeBuilder(CoreEngine* E, ExplodedNode *P) : Pred(P), Eng(*E) {} BlockCounter getBlockCounter() const { return Eng.WList->getBlockCounter(); } @@ -314,21 +314,38 @@ protected: class PureStmtNodeBuilder: public NodeBuilder { + NodeBuilder *EnclosingBldr; public: PureStmtNodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet, - const NodeBuilderContext &Ctx) - : NodeBuilder(SrcNode, DstSet, Ctx) {} + const NodeBuilderContext &Ctx, NodeBuilder *Enclosing = 0) + : NodeBuilder(SrcNode, DstSet, Ctx), EnclosingBldr(Enclosing) { + if (EnclosingBldr) + EnclosingBldr->takeNodes(SrcNode); + } PureStmtNodeBuilder(ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet, - const NodeBuilderContext &Ctx) - : NodeBuilder(SrcSet, DstSet, Ctx) {} + const NodeBuilderContext &Ctx, NodeBuilder *Enclosing = 0) + : NodeBuilder(SrcSet, DstSet, Ctx), EnclosingBldr(Enclosing) { + if (EnclosingBldr) + for (ExplodedNodeSet::iterator I = SrcSet.begin(), + E = SrcSet.end(); I != E; ++I ) + EnclosingBldr->takeNodes(*I); + + } + + virtual ~PureStmtNodeBuilder() { + if (EnclosingBldr) + for (ExplodedNodeSet::iterator I = Frontier.begin(), + E = Frontier.end(); I != E; ++I ) + EnclosingBldr->addNodes(*I); + } ExplodedNode *generateNode(const Stmt *S, ExplodedNode *Pred, const ProgramState *St, - ProgramPoint::Kind K = ProgramPoint::PostStmtKind, bool MarkAsSink = false, const ProgramPointTag *tag = 0, + ProgramPoint::Kind K = ProgramPoint::PostStmtKind, bool Purging = false) { if (Purging) { assert(K == ProgramPoint::PostStmtKind); @@ -339,6 +356,14 @@ public: Pred->getLocationContext(), tag); return generateNodeImpl(L, St, Pred, MarkAsSink); } + + ExplodedNode *generateNode(const ProgramPoint &PP, + ExplodedNode *Pred, + const ProgramState *State, + bool MarkAsSink = false) { + return generateNodeImpl(PP, State, Pred, MarkAsSink); + } + }; class StmtNodeBuilder : public NodeBuilder { @@ -360,8 +385,6 @@ public: PurgingDeadSymbols(false), BuildSinks(false), hasGeneratedNode(false), PointKind(ProgramPoint::PostStmtKind), Tag(0) {} - virtual ~StmtNodeBuilder(); - ExplodedNode *generateNode(const Stmt *S, const ProgramState *St, ExplodedNode *Pred, diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index a593fac99a..fdd3c47e81 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -120,12 +120,16 @@ public: StmtNodeBuilder &getBuilder() { assert(Builder); return *Builder; } const NodeBuilderContext &getBuilderContext() { - assert(Builder); - return Builder->getContext(); + assert(currentBuilderContext); + return *currentBuilderContext; } bool isObjCGCEnabled() { return ObjCGCEnabled; } + const Stmt *getStmt() const; + + void GenerateAutoTransition(ExplodedNode *N); + /// ViewGraph - Visualize the ExplodedGraph created by executing the /// simulation. void ViewGraph(bool trim = false); @@ -141,17 +145,14 @@ public: /// processCFGElement - Called by CoreEngine. Used to generate new successor /// nodes by processing the 'effects' of a CFG element. - void processCFGElement(const CFGElement E, StmtNodeBuilder& Bldr, - ExplodedNode *Pred); + void processCFGElement(const CFGElement E, ExplodedNode *Pred, + unsigned StmtIdx, NodeBuilderContext *Ctx); - void ProcessStmt(const CFGStmt S, StmtNodeBuilder &builder, - ExplodedNode *Pred); + void ProcessStmt(const CFGStmt S, ExplodedNode *Pred); - void ProcessInitializer(const CFGInitializer I, StmtNodeBuilder &Bldr, - ExplodedNode *Pred); + void ProcessInitializer(const CFGInitializer I, ExplodedNode *Pred); - void ProcessImplicitDtor(const CFGImplicitDtor D, StmtNodeBuilder &builder, - ExplodedNode *Pred); + void ProcessImplicitDtor(const CFGImplicitDtor D, ExplodedNode *Pred); void ProcessAutomaticObjDtor(const CFGAutomaticObjDtor D, StmtNodeBuilder &builder, ExplodedNode *Pred); @@ -431,8 +432,9 @@ public: } protected: - void evalObjCMessage(ExplodedNodeSet &Dst, const ObjCMessage &msg, - ExplodedNode *Pred, const ProgramState *state); + void evalObjCMessage(PureStmtNodeBuilder &Bldr, const ObjCMessage &msg, + ExplodedNode *Pred, const ProgramState *state, + bool GenSink); const ProgramState *invalidateArguments(const ProgramState *State, const CallOrObjCMessage &Call, @@ -444,7 +446,8 @@ protected: /// evalBind - Handle the semantics of binding a value to a specific location. /// This method is used by evalStore, VisitDeclStmt, and others. void evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, ExplodedNode *Pred, - SVal location, SVal Val, bool atDeclInit = false); + SVal location, SVal Val, bool atDeclInit = false, + ProgramPoint::Kind PP = ProgramPoint::PostStmtKind); public: // FIXME: 'tag' should be removed, and a LocationContext should be used diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h index e891e8fc42..a23b71ab31 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h @@ -55,8 +55,8 @@ public: /// Called by CoreEngine. Used to generate new successor /// nodes by processing the 'effects' of a block-level statement. - virtual void processCFGElement(const CFGElement E, StmtNodeBuilder& builder, - ExplodedNode* Pred)=0; + virtual void processCFGElement(const CFGElement E, ExplodedNode* Pred, + unsigned StmtIdx, NodeBuilderContext *Ctx)=0; /// Called by CoreEngine when it starts processing a CFGBlock. The /// SubEngine is expected to populate dstNodes with new nodes representing diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index e77c677be0..aac181fdb4 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -245,6 +245,7 @@ namespace { } /// \brief Run checkers for load/store of a location. + void CheckerManager::runCheckersForLocation(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, SVal location, bool isLoad, @@ -261,18 +262,19 @@ namespace { SVal Val; const Stmt *S; ExprEngine &Eng; + ProgramPoint::Kind PointKind; CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } CheckersTy::const_iterator checkers_end() { return Checkers.end(); } CheckBindContext(const CheckersTy &checkers, - SVal loc, SVal val, const Stmt *s, ExprEngine &eng) - : Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng) { } + SVal loc, SVal val, const Stmt *s, ExprEngine &eng, + ProgramPoint::Kind PK) + : Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng), PointKind(PK) {} void runChecker(CheckerManager::CheckBindFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { - ProgramPoint::Kind K = ProgramPoint::PreStmtKind; - const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, PointKind, Pred->getLocationContext(), checkFn.Checker); CheckerContext C(Bldr, Eng, Pred, L, 0); @@ -285,8 +287,9 @@ namespace { void CheckerManager::runCheckersForBind(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, SVal location, SVal val, - const Stmt *S, ExprEngine &Eng) { - CheckBindContext C(BindCheckers, location, val, S, Eng); + const Stmt *S, ExprEngine &Eng, + ProgramPoint::Kind PointKind) { + CheckBindContext C(BindCheckers, location, val, S, Eng, PointKind); expandGraphWithCheckers(C, Dst, Src); } diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index 6616e065d3..809379e4ac 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -315,9 +315,7 @@ void CoreEngine::HandleBlockEntrance(const BlockEntrance &L, // Process the entrance of the block. if (CFGElement E = L.getFirstElement()) { NodeBuilderContext Ctx(*this, L.getBlock(), Pred); - ExplodedNodeSet Dst; - StmtNodeBuilder Builder(Pred, Dst, 0, Ctx); - SubEng.processCFGElement(E, Builder, Pred); + SubEng.processCFGElement(E, Pred, 0, &Ctx); } else HandleBlockExit(L.getBlock(), Pred); @@ -436,9 +434,7 @@ void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, HandleBlockExit(B, Pred); else { NodeBuilderContext Ctx(*this, B, Pred); - ExplodedNodeSet Dst; - StmtNodeBuilder Builder(Pred, Dst, StmtIdx, Ctx); - SubEng.processCFGElement((*B)[StmtIdx], Builder, Pred); + SubEng.processCFGElement((*B)[StmtIdx], Pred, StmtIdx, &Ctx); } } @@ -510,12 +506,6 @@ ExplodedNode* NodeBuilder::generateNodeImpl(const ProgramPoint &Loc, return (IsNew ? N : 0); } -StmtNodeBuilder::~StmtNodeBuilder() { - for (iterator I=Frontier.begin(), E=Frontier.end(); I!=E; ++I) - if (!(*I)->isSink()) - GenerateAutoTransition(*I); -} - void StmtNodeBuilder::GenerateAutoTransition(ExplodedNode *N) { assert (!N->isSink()); diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 5999dc6bb1..0b40c9aa4d 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -197,30 +197,77 @@ void ExprEngine::processEndWorklist(bool hasWorkRemaining) { getCheckerManager().runCheckersForEndAnalysis(G, BR, *this); } -void ExprEngine::processCFGElement(const CFGElement E, - StmtNodeBuilder& Bldr, - ExplodedNode *Pred) { +void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, + unsigned StmtIdx, NodeBuilderContext *Ctx) { + currentStmtIdx = StmtIdx; + currentBuilderContext = Ctx; + switch (E.getKind()) { case CFGElement::Invalid: llvm_unreachable("Unexpected CFGElement kind."); case CFGElement::Statement: - ProcessStmt(const_cast<Stmt*>(E.getAs<CFGStmt>()->getStmt()), Bldr, Pred); + ProcessStmt(const_cast<Stmt*>(E.getAs<CFGStmt>()->getStmt()), Pred); return; case CFGElement::Initializer: - ProcessInitializer(E.getAs<CFGInitializer>()->getInitializer(), - Bldr, Pred); + ProcessInitializer(E.getAs<CFGInitializer>()->getInitializer(), Pred); return; case CFGElement::AutomaticObjectDtor: case CFGElement::BaseDtor: case CFGElement::MemberDtor: case CFGElement::TemporaryDtor: - ProcessImplicitDtor(*E.getAs<CFGImplicitDtor>(), Bldr, Pred); + ProcessImplicitDtor(*E.getAs<CFGImplicitDtor>(), Pred); return; } } -void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder, +const Stmt *ExprEngine::getStmt() const { + const CFGStmt *CS = (*currentBuilderContext->getBlock())[currentStmtIdx].getAs<CFGStmt>(); + return CS ? CS->getStmt() : 0; +} + +// TODO: Adding nodes to the worklist shoudl be a function inside CoreEngine. +void ExprEngine::GenerateAutoTransition(ExplodedNode *N) { + assert (!N->isSink()); + const CFGBlock *Block = currentBuilderContext->getBlock(); + unsigned Idx = currentStmtIdx; + + // Check if this node entered a callee. + if (isa<CallEnter>(N->getLocation())) { + // Still use the index of the CallExpr. It's needed to create the callee + // StackFrameContext. + Engine.WList->enqueue(N, Block, Idx); + return; + } + + // Do not create extra nodes. Move to the next CFG element. + if (isa<PostInitializer>(N->getLocation())) { + Engine.WList->enqueue(N, Block, Idx+1); + return; + } + + PostStmt Loc(getStmt(), N->getLocationContext()); + + if (Loc == N->getLocation()) { + // Note: 'N' should be a fresh node because otherwise it shouldn't be + // a member of Deferred. + Engine.WList->enqueue(N, Block, Idx+1); + return; + } + + bool IsNew; + ExplodedNode *Succ = Engine.G->getNode(Loc, N->getState(), &IsNew); + Succ->addPredecessor(N, *Engine.G); + + if (IsNew) + Engine.WList->enqueue(Succ, Block, Idx+1); +} + + +void ExprEngine::ProcessStmt(const CFGStmt S, ExplodedNode *Pred) { + ExplodedNodeSet TopDst; + StmtNodeBuilder builder(Pred, TopDst, currentStmtIdx, *currentBuilderContext); + // TODO: Use RAII to remove the unnecessary, tagged nodes. //RegisterCreatedNodes registerCreatedNodes(getGraph()); @@ -230,9 +277,6 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder, StateMgr.recycleUnusedStates(); currentStmt = S.getStmt(); - currentStmtIdx = builder.getIndex(); - currentBuilderContext = &builder.getContext(); - PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), currentStmt->getLocStart(), "Error evaluating statement"); @@ -268,15 +312,15 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder, // up. Since no symbols are dead, we can optimize and not clean out // the constraint manager. CleanedNode = - Builder->generateNode(currentStmt, CleanedState, EntryNode, &cleanupTag); + builder.generateNode(currentStmt, CleanedState, EntryNode, &cleanupTag); Tmp.Add(CleanedNode); } else { - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - SaveOr OldHasGen(Builder->hasGeneratedNode); + SaveAndRestore<bool> OldSink(builder.BuildSinks); + SaveOr OldHasGen(builder.hasGeneratedNode); - SaveAndRestore<bool> OldPurgeDeadSymbols(Builder->PurgingDeadSymbols); - Builder->PurgingDeadSymbols = true; + SaveAndRestore<bool> OldPurgeDeadSymbols(builder.PurgingDeadSymbols); + builder.PurgingDeadSymbols = true; // Call checkers with the non-cleaned state so that they could query the // values of the soon to be dead symbols. @@ -306,18 +350,25 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder, // generate a transition to that state. const ProgramState *CleanedCheckerSt = StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState); - ExplodedNode *CleanedNode = Builder->generateNode(currentStmt, + ExplodedNode *CleanedNode = builder.generateNode(currentStmt, CleanedCheckerSt, *I, &cleanupTag); Tmp.Add(CleanedNode); } } + ExplodedNodeSet AllDst; for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - // TODO: Remove Dest set, it's no longer needed. ExplodedNodeSet Dst; // Visit the statement. Visit(currentStmt, *I, Dst); + AllDst.insert(Dst); + } + + for (ExplodedNodeSet::iterator I = AllDst.begin(), + E = AllDst.end(); I != E; ++I) { + assert(!(*I)->isSink()); + GenerateAutoTransition(*I); } // NULL out these variables to cleanup. @@ -328,8 +379,10 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder, } void ExprEngine::ProcessInitializer(const CFGInitializer Init, - StmtNodeBuilder &builder, ExplodedNode *pred) { + ExplodedNodeSet Dst; + StmtNodeBuilder Bldr(pred, Dst, currentStmtIdx, *currentBuilderContext); + // We don't set EntryNode and currentStmt. And we don't clean up state. const CXXCtorInitializer *BMI = Init.getInitializer(); const StackFrameContext *stackFrame = cast<StackFrameContext>(pred->getLocationContext()); @@ -358,7 +411,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init, PostInitializer PP(BMI, stackFrame); // Builder automatically add the generated node to the deferred set, // which are processed in the builder's dtor. - builder.generateNode(PP, state, Pred); + Bldr.generateNode(PP, state, Pred); } return; } @@ -373,14 +426,20 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init, getStoreManager().evalDerivedToBase(thisVal, ctorExpr->getType()); const MemRegion *baseReg = baseVal.getAsRegion(); assert(baseReg); - Builder = &builder; + Builder = &Bldr; ExplodedNodeSet dst; VisitCXXConstructExpr(ctorExpr, baseReg, pred, dst); + for (ExplodedNodeSet::iterator I = dst.begin(), + E = dst.end(); I != E; ++I) { + GenerateAutoTransition(*I); + } } void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D, - StmtNodeBuilder &builder, - ExplodedNode *Pred) { + ExplodedNode *Pred) { + ExplodedNodeSet Dst; + StmtNodeBuilder builder(Pred, Dst, currentStmtIdx, *currentBuilderContext); + Builder = &builder; switch (D.getKind()) { @@ -399,6 +458,11 @@ void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D, default: llvm_unreachable("Unexpected dtor kind."); } + + for (ExplodedNodeSet::iterator I = Dst.begin(), + E = Dst.end(); I != E; ++I) { + GenerateAutoTransition(*I); + } } void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor dtor, @@ -440,6 +504,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), S->getLocStart(), "Error evaluating statement"); + PureStmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); // Expressions to ignore. if (const Expr *Ex = dyn_cast<Expr>(S)) @@ -449,10 +514,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // this check when we KNOW that there is no block-level subexpression. // The motivation is that this check requires a hashtable lookup. - if (S != currentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(S)) { - Dst.Add(Pred); + if (S != currentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(S)) return; - } switch (S->getStmtClass()) { // C++ and ARC stuff we don't support yet. @@ -479,22 +542,17 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::SubstNonTypeTemplateParmPackExprClass: case Stmt::SEHTryStmtClass: case Stmt::SEHExceptStmtClass: - case Stmt::SEHFinallyStmtClass: - { - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - Builder->BuildSinks = true; - const ExplodedNode *node = MakeNode(Dst, S, Pred, Pred->getState()); - Engine.addAbortedBlock(node, Builder->getBlock()); + case Stmt::SEHFinallyStmtClass: { + const ExplodedNode *node = Bldr.generateNode(S, Pred, Pred->getState()); + Engine.addAbortedBlock(node, currentBuilderContext->getBlock()); break; } // We don't handle default arguments either yet, but we can fake it // for now by just skipping them. case Stmt::SubstNonTypeTemplateParmExprClass: - case Stmt::CXXDefaultArgExprClass: { - Dst.Add(Pred); + case Stmt::CXXDefaultArgExprClass: break; - } case Stmt::ParenExprClass: llvm_unreachable("ParenExprs already handled."); @@ -525,31 +583,33 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // GNU __null is a pointer-width integer, not an actual pointer. const ProgramState *state = Pred->getState(); state = state->BindExpr(S, svalBuilder.makeIntValWithPtrWidth(0, false)); - MakeNode(Dst, S, Pred, state); + Bldr.generateNode(S, Pred, state); break; } case Stmt::ObjCAtSynchronizedStmtClass: + Bldr.takeNodes(Pred); VisitObjCAtSynchronizedStmt(cast<ObjCAtSynchronizedStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::ObjCPropertyRefExprClass: // Implicitly handled by Environment::getSVal(). - Dst.Add(Pred); break; case Stmt::ImplicitValueInitExprClass: { const ProgramState *state = Pred->getState(); QualType ty = cast<ImplicitValueInitExpr>(S)->getType(); SVal val = svalBuilder.makeZeroVal(ty); - MakeNode(Dst, S, Pred, state->BindExpr(S, val)); + Bldr.generateNode(S, Pred, state->BindExpr(S, val)); break; } - case Stmt::ExprWithCleanupsClass: { + case Stmt::ExprWithCleanupsClass: + Bldr.takeNodes(Pred); Visit(cast<ExprWithCleanups>(S)->getSubExpr(), Pred, Dst); + Bldr.addNodes(Dst); break; - } // Cases not handled yet; but will handle some day. case Stmt::DesignatedInitExprClass: @@ -583,39 +643,52 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::FloatingLiteralClass: case Stmt::SizeOfPackExprClass: case Stmt::CXXNullPtrLiteralExprClass: - Dst.Add(Pred); // No-op. Simply propagate the current state unchanged. + // No-op. Simply propagate the current state unchanged. break; case Stmt::ArraySubscriptExprClass: + Bldr.takeNodes(Pred); VisitLvalArraySubscriptExpr(cast<ArraySubscriptExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::AsmStmtClass: + Bldr.takeNodes(Pred); VisitAsmStmt(cast<AsmStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::BlockDeclRefExprClass: { + Bldr.takeNodes(Pred); const BlockDeclRefExpr *BE = cast<BlockDeclRefExpr>(S); VisitCommonDeclRefExpr(BE, BE->getDecl(), Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::BlockExprClass: + Bldr.takeNodes(Pred); VisitBlockExpr(cast<BlockExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::BinaryOperatorClass: { const BinaryOperator* B = cast<BinaryOperator>(S); if (B->isLogicalOp()) { + Bldr.takeNodes(Pred); VisitLogicalExpr(B, Pred, Dst); + Bldr.addNodes(Dst); break; } else if (B->getOpcode() == BO_Comma) { const ProgramState *state = Pred->getState(); - MakeNode(Dst, B, Pred, state->BindExpr(B, state->getSVal(B->getRHS()))); + Bldr.generateNode(B, Pred, + state->BindExpr(B, state->getSVal(B->getRHS()))); break; } + Bldr.takeNodes(Pred); + if (AMgr.shouldEagerlyAssume() && (B->isRelationalOp() || B->isEqualityOp())) { ExplodedNodeSet Tmp; @@ -625,13 +698,16 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, else VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::CallExprClass: case Stmt::CXXOperatorCallExprClass: case Stmt::CXXMemberCallExprClass: { + Bldr.takeNodes(Pred); VisitCallExpr(cast<CallExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; } @@ -640,58 +716,78 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, const CXXConstructExpr *C = cast<CXXConstructExpr>(S); // For block-level CXXConstructExpr, we don't have a destination region. // Let VisitCXXConstructExpr() create one. + Bldr.takeNodes(Pred); VisitCXXConstructExpr(C, 0, Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::CXXNewExprClass: { + Bldr.takeNodes(Pred); const CXXNewExpr *NE = cast<CXXNewExpr>(S); VisitCXXNewExpr(NE, Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::CXXDeleteExprClass: { + Bldr.takeNodes(Pred); const CXXDeleteExpr *CDE = cast<CXXDeleteExpr>(S); VisitCXXDeleteExpr(CDE, Pred, Dst); + Bldr.addNodes(Dst); break; } // FIXME: ChooseExpr is really a constant. We need to fix // the CFG do not model them as explicit control-flow. case Stmt::ChooseExprClass: { // __builtin_choose_expr + Bldr.takeNodes(Pred); const ChooseExpr *C = cast<ChooseExpr>(S); VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::CompoundAssignOperatorClass: + Bldr.takeNodes(Pred); VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::CompoundLiteralExprClass: + Bldr.takeNodes(Pred); VisitCompoundLiteralExpr(cast<CompoundLiteralExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::BinaryConditionalOperatorClass: case Stmt::ConditionalOperatorClass: { // '?' operator + Bldr.takeNodes(Pred); const AbstractConditionalOperator *C = cast<AbstractConditionalOperator>(S); VisitGuardedExpr(C, C->getTrueExpr(), C->getFalseExpr(), Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::CXXThisExprClass: + Bldr.takeNodes(Pred); VisitCXXThisExpr(cast<CXXThisExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::DeclRefExprClass: { + Bldr.takeNodes(Pred); const DeclRefExpr *DE = cast<DeclRefExpr>(S); VisitCommonDeclRefExpr(DE, DE->getDecl(), Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::DeclStmtClass: + Bldr.takeNodes(Pred); VisitDeclStmt(cast<DeclStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::ImplicitCastExprClass: @@ -702,6 +798,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXConstCastExprClass: case Stmt::CXXFunctionalCastExprClass: case Stmt::ObjCBridgedCastExprClass: { + Bldr.takeNodes(Pred); const CastExpr *C = cast<CastExpr>(S); // Handle the previsit checks. ExplodedNodeSet dstPrevisit; @@ -716,58 +813,76 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // Handle the postvisit checks. getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, C, *this); + Bldr.addNodes(Dst); break; } case Expr::MaterializeTemporaryExprClass: { + Bldr.takeNodes(Pred); const MaterializeTemporaryExpr *Materialize = cast<MaterializeTemporaryExpr>(S); if (!Materialize->getType()->isRecordType()) CreateCXXTemporaryObject(Materialize, Pred, Dst); else Visit(Materialize->GetTemporaryExpr(), Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::InitListExprClass: + Bldr.takeNodes(Pred); VisitInitListExpr(cast<InitListExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::MemberExprClass: + Bldr.takeNodes(Pred); VisitMemberExpr(cast<MemberExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; + case Stmt::ObjCIvarRefExprClass: + Bldr.takeNodes(Pred); VisitLvalObjCIvarRefExpr(cast<ObjCIvarRefExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::ObjCForCollectionStmtClass: + Bldr.takeNodes(Pred); VisitObjCForCollectionStmt(cast<ObjCForCollectionStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::ObjCMessageExprClass: + Bldr.takeNodes(Pred); VisitObjCMessage(cast<ObjCMessageExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::ObjCAtThrowStmtClass: { // FIXME: This is |