diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h index d8a7245730..a2e211edea 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h @@ -96,6 +96,10 @@ private: void HandleBranch(const Stmt *Cond, const Stmt *Term, const CFGBlock *B, ExplodedNode *Pred); + /// Handle conditional logic for running static initializers. + void HandleStaticInit(const DeclStmt *DS, const CFGBlock *B, + ExplodedNode *Pred); + private: CoreEngine(const CoreEngine &) LLVM_DELETED_FUNCTION; void operator=(const CoreEngine &) LLVM_DELETED_FUNCTION; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 42c0c0488e..33e4431eb4 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -224,6 +224,15 @@ public: const CFGBlock *DstT, const CFGBlock *DstF); + /// Called by CoreEngine. Used to processing branching behavior + /// at static initalizers. + void processStaticInitializer(const DeclStmt *DS, + NodeBuilderContext& BuilderCtx, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const CFGBlock *DstT, + const CFGBlock *DstF); + /// processIndirectGoto - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a computed goto jump. void processIndirectGoto(IndirectGotoNodeBuilder& builder); diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h index 4578f6f323..d4100634a7 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h @@ -72,6 +72,15 @@ public: const CFGBlock *DstT, const CFGBlock *DstF) = 0; + /// Called by CoreEngine. Used to processing branching behavior + /// at static initalizers. + virtual void processStaticInitializer(const DeclStmt *DS, + NodeBuilderContext& BuilderCtx, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const CFGBlock *DstT, + const CFGBlock *DstF) = 0; + /// Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a computed goto jump. virtual void processIndirectGoto(IndirectGotoNodeBuilder& builder) = 0; diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index f436ef3f6a..91a7fdf6bc 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -1665,6 +1665,7 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) { // whether or not they are initialized. if (Block) { Succ = Block; + Block = 0; if (badCFG) return 0; } @@ -1714,15 +1715,14 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) { if (ScopePos && VD == *ScopePos) ++ScopePos; - CFGBlock *B = Block ? Block : LastBlock; + CFGBlock *B = LastBlock; if (blockBeforeInit) { Succ = B; - Block = 0; - CFGBlock *branchBlock = createBlock(false); - branchBlock->setTerminator(DS); - addSuccessor(branchBlock, blockBeforeInit); - addSuccessor(branchBlock, B); - B = branchBlock; + Block = createBlock(false); + Block->setTerminator(DS); + addSuccessor(Block, blockBeforeInit); + addSuccessor(Block, B); + B = Block; } return B; diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index 1326d0d01b..65faa10134 100644 --- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -238,6 +238,6 @@ bool AnalyzerOptions::shouldPrunePaths() { } bool AnalyzerOptions::shouldConditionalizeStaticInitializers() { - return getBooleanOption("conditional-static-initializers", false); + return getBooleanOption("cfg-conditional-static-initializers", true); } diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index c61bcf7d41..b09b2c2ddf 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -346,6 +346,11 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { default: llvm_unreachable("Analysis for this terminator not implemented."); + // Model static initializers. + case Stmt::DeclStmtClass: + HandleStaticInit(cast(Term), B, Pred); + return; + case Stmt::BinaryOperatorClass: // '&&' and '||' HandleBranch(cast(Term)->getLHS(), Term, B, Pred); return; @@ -456,6 +461,19 @@ void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term, enqueue(Dst); } + +void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B, + ExplodedNode *Pred) { + assert(B->succ_size() == 2); + NodeBuilderContext Ctx(*this, B, Pred); + ExplodedNodeSet Dst; + SubEng.processStaticInitializer(DS, Ctx, Pred, Dst, + *(B->succ_begin()), *(B->succ_begin()+1)); + // Enqueue the new frontier onto the worklist. + enqueue(Dst); +} + + void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, ExplodedNode *Pred) { assert(B); diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 8ad5a3dbde..789abb7d86 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1344,6 +1344,34 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, currBldrCtx = 0; } +/// The GDM component containing the set of global variables which have been +/// previously initialized with explicit initializers. +REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedGlobalsSet, + llvm::ImmutableSet) + +void ExprEngine::processStaticInitializer(const DeclStmt *DS, + NodeBuilderContext &BuilderCtx, + ExplodedNode *Pred, + clang::ento::ExplodedNodeSet &Dst, + const CFGBlock *DstT, + const CFGBlock *DstF) { + currBldrCtx = &BuilderCtx; + + const VarDecl *VD = cast(DS->getSingleDecl()); + ProgramStateRef state = Pred->getState(); + bool initHasRun = state->contains(VD); + BranchNodeBuilder builder(Pred, Dst, BuilderCtx, DstT, DstF); + + if (!initHasRun) { + state = state->add(VD); + } + + builder.generateNode(state, initHasRun, Pred); + builder.markInfeasible(!initHasRun); + + currBldrCtx = 0; +} + /// processIndirectGoto - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a computed goto jump. void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 2f2eb8628a..3a3c9713dd 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -423,11 +423,6 @@ void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL, B.generateNode(CL, Pred, state->BindExpr(CL, LC, ILV)); } -/// The GDM component containing the set of global variables which have been -/// previously initialized with explicit initializers. -REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedGlobalsSet, - llvm::ImmutableSet ) - void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, ExplodedNodeSet &Dst) { // Assumption: The CFG has one DeclStmt per Decl. @@ -438,15 +433,6 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, Dst.insert(Pred); return; } - - // Check if a value has been previously initialized. There will be an entry in - // the set for variables with global storage which have been previously - // initialized. - if (VD->hasGlobalStorage()) - if (Pred->getState()->contains(VD)) { - Dst.insert(Pred); - return; - } // FIXME: all pre/post visits should eventually be handled by ::Visit(). ExplodedNodeSet dstPreVisit; @@ -464,11 +450,6 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, // Note in the state that the initialization has occurred. ExplodedNode *UpdatedN = N; - if (VD->hasGlobalStorage()) { - state = state->add(VD); - UpdatedN = B.generateNode(DS, N, state); - } - SVal InitVal = state->getSVal(InitEx, LC); if (isa(InitEx->IgnoreImplicit())) { diff --git a/test/Analysis/analyzer-config.c b/test/Analysis/analyzer-config.c index 7fa299b786..96b948352b 100644 --- a/test/Analysis/analyzer-config.c +++ b/test/Analysis/analyzer-config.c @@ -5,6 +5,7 @@ void bar() {} void foo() { bar(); } // CHECK: [config] +// CHECK-NEXT: cfg-conditional-static-initializers = true // CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 @@ -15,4 +16,5 @@ void foo() { bar(); } // CHECK-NEXT: max-times-inline-large = 32 // CHECK-NEXT: mode = deep // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 9 +// CHECK-NEXT: num-entries = 10 + diff --git a/test/Analysis/analyzer-config.cpp b/test/Analysis/analyzer-config.cpp index 59587f6e11..c3a04c946e 100644 --- a/test/Analysis/analyzer-config.cpp +++ b/test/Analysis/analyzer-config.cpp @@ -14,6 +14,7 @@ public: // CHECK-NEXT: c++-inlining = constructors // CHECK-NEXT: c++-stdlib-inlining = true // CHECK-NEXT: c++-template-inlining = true +// CHECK-NEXT: cfg-conditional-static-initializers = true // CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 @@ -24,4 +25,4 @@ public: // CHECK-NEXT: max-times-inline-large = 32 // CHECK-NEXT: mode = deep // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 12 +// CHECK-NEXT: num-entries = 13