diff options
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/Analysis/AnalysisDeclContext.cpp | 4 | ||||
-rw-r--r-- | clang/lib/Analysis/CFG.cpp | 86 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp | 1 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp | 8 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 23 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp | 1 |
6 files changed, 99 insertions, 24 deletions
diff --git a/clang/lib/Analysis/AnalysisDeclContext.cpp b/clang/lib/Analysis/AnalysisDeclContext.cpp index 9874bb87b28..2b8259bad72 100644 --- a/clang/lib/Analysis/AnalysisDeclContext.cpp +++ b/clang/lib/Analysis/AnalysisDeclContext.cpp @@ -67,7 +67,8 @@ AnalysisDeclContextManager::AnalysisDeclContextManager( ASTContext &ASTCtx, bool useUnoptimizedCFG, bool addImplicitDtors, bool addInitializers, bool addTemporaryDtors, bool addLifetime, bool addLoopExit, bool synthesizeBodies, bool addStaticInitBranch, - bool addCXXNewAllocator, CodeInjector *injector) + bool addCXXNewAllocator, bool addRichCXXConstructors, + CodeInjector *injector) : Injector(injector), FunctionBodyFarm(ASTCtx, injector), SynthesizeBodies(synthesizeBodies) { cfgBuildOptions.PruneTriviallyFalseEdges = !useUnoptimizedCFG; @@ -78,6 +79,7 @@ AnalysisDeclContextManager::AnalysisDeclContextManager( cfgBuildOptions.AddLoopExit = addLoopExit; cfgBuildOptions.AddStaticInitBranches = addStaticInitBranch; cfgBuildOptions.AddCXXNewAllocator = addCXXNewAllocator; + cfgBuildOptions.AddRichCXXConstructors = addRichCXXConstructors; } void AnalysisDeclContextManager::clear() { Contexts.clear(); } diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index 714b85d3a9f..63f1d724eb7 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -472,6 +472,11 @@ class CFGBuilder { using LabelSetTy = llvm::SmallSetVector<LabelDecl *, 8>; LabelSetTy AddressTakenLabels; + // Information about the currently visited C++ object construction site. + // This is set in the construction trigger and read when the constructor + // itself is being visited. + ConstructionContext CurrentConstructionContext = {}; + bool badCFG = false; const CFG::BuildOptions &BuildOpts; @@ -643,6 +648,18 @@ private: return Block; } + // Scan the child statement \p Child to find the constructor that might + // have been directly triggered by the current node, \p Trigger. If such + // constructor has been found, set current construction context to point + // to the trigger statement. The construction context will be unset once + // it is consumed when the CFG building procedure processes the + // construct-expression and adds the respective CFGConstructor element. + void EnterConstructionContextIfNecessary(Stmt *Trigger, Stmt *Child); + // Unset the construction context after consuming it. This is done immediately + // after adding the CFGConstructor element, so there's no need to + // do this manually in every Visit... function. + void ExitConstructionContext(); + void autoCreateBlock() { if (!Block) Block = createBlock(); } CFGBlock *createBlock(bool add_successor = true); CFGBlock *createNoReturnBlock(); @@ -682,6 +699,20 @@ private: B->appendStmt(const_cast<Stmt*>(S), cfg->getBumpVectorContext()); } + void appendConstructor(CFGBlock *B, CXXConstructExpr *CE) { + if (BuildOpts.AddRichCXXConstructors) { + if (!CurrentConstructionContext.isNull()) { + B->appendConstructor(CE, CurrentConstructionContext, + cfg->getBumpVectorContext()); + ExitConstructionContext(); + return; + } + } + + // No valid construction context found. Fall back to statement. + B->appendStmt(CE, cfg->getBumpVectorContext()); + } + void appendInitializer(CFGBlock *B, CXXCtorInitializer *I) { B->appendInitializer(I, cfg->getBumpVectorContext()); } @@ -1116,6 +1147,26 @@ static const VariableArrayType *FindVA(const Type *t) { return nullptr; } +void CFGBuilder::EnterConstructionContextIfNecessary(Stmt *Trigger, + Stmt *Child) { + if (!BuildOpts.AddRichCXXConstructors) + return; + if (!Child) + return; + if (auto *Constructor = dyn_cast<CXXConstructExpr>(Child)) { + assert(CurrentConstructionContext.isNull() && + "Already within a construction context!"); + CurrentConstructionContext = ConstructionContext(Trigger); + } +} + +void CFGBuilder::ExitConstructionContext() { + assert(!CurrentConstructionContext.isNull() && + "Cannot exit construction context without the context!"); + CurrentConstructionContext = ConstructionContext(); +} + + /// BuildCFG - Constructs a CFG from an AST (a Stmt*). The AST can represent an /// arbitrary statement. Examples include a single expression or a function /// body (compound statement). The ownership of the returned CFG is @@ -3872,7 +3923,7 @@ CFGBlock *CFGBuilder::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E, CFGBlock *CFGBuilder::VisitCXXConstructExpr(CXXConstructExpr *C, AddStmtChoice asc) { autoCreateBlock(); - appendStmt(Block, C); + appendConstructor(Block, C); return VisitChildren(C); } @@ -3882,15 +3933,22 @@ CFGBlock *CFGBuilder::VisitCXXNewExpr(CXXNewExpr *NE, autoCreateBlock(); appendStmt(Block, NE); + EnterConstructionContextIfNecessary( + NE, const_cast<CXXConstructExpr *>(NE->getConstructExpr())); + if (NE->getInitializer()) Block = Visit(NE->getInitializer()); + if (BuildOpts.AddCXXNewAllocator) appendNewAllocator(Block, NE); + if (NE->isArray()) Block = Visit(NE->getArraySize()); + for (CXXNewExpr::arg_iterator I = NE->placement_arg_begin(), E = NE->placement_arg_end(); I != E; ++I) Block = Visit(*I); + return Block; } @@ -4210,11 +4268,12 @@ std::unique_ptr<CFG> CFG::buildCFG(const Decl *D, Stmt *Statement, const CXXDestructorDecl * CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const { switch (getKind()) { - case CFGElement::Statement: case CFGElement::Initializer: case CFGElement::NewAllocator: case CFGElement::LoopExit: case CFGElement::LifetimeEnds: + case CFGElement::Statement: + case CFGElement::Constructor: llvm_unreachable("getDestructorDecl should only be used with " "ImplicitDtors"); case CFGElement::AutomaticObjectDtor: { @@ -4343,8 +4402,8 @@ public: switch (stmt->getStmtClass()) { case Stmt::DeclStmtClass: - DeclMap[cast<DeclStmt>(stmt)->getSingleDecl()] = P; - break; + DeclMap[cast<DeclStmt>(stmt)->getSingleDecl()] = P; + break; case Stmt::IfStmtClass: { const VarDecl *var = cast<IfStmt>(stmt)->getConditionVariable(); if (var) @@ -4575,14 +4634,19 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, if (isa<CXXOperatorCallExpr>(S)) { OS << " (OperatorCall)"; - } - else if (isa<CXXBindTemporaryExpr>(S)) { + } else if (isa<CXXBindTemporaryExpr>(S)) { OS << " (BindTemporary)"; - } - else if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(S)) { - OS << " (CXXConstructExpr, " << CCE->getType().getAsString() << ")"; - } - else if (const CastExpr *CE = dyn_cast<CastExpr>(S)) { + } else if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(S)) { + OS << " (CXXConstructExpr, "; + if (Optional<CFGConstructor> CE = E.getAs<CFGConstructor>()) { + if (const Stmt *S = CE->getTriggerStmt()) + Helper.handledStmt((const_cast<Stmt *>(S)), OS); + else + llvm_unreachable("Unexpected trigger kind!"); + OS << ", "; + } + OS << CCE->getType().getAsString() << ")"; + } else if (const CastExpr *CE = dyn_cast<CastExpr>(S)) { OS << " (" << CE->getStmtClassName() << ", " << CE->getCastKindName() << ", " << CE->getType().getAsString() diff --git a/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 1cc08f0d9fe..53199f746c3 100644 --- a/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -29,6 +29,7 @@ AnalysisManager::AnalysisManager( Options.shouldSynthesizeBodies(), Options.shouldConditionalizeStaticInitializers(), /*addCXXNewAllocator=*/true, + Options.includeRichConstructorsInCFG(), injector), Ctx(ASTCtx), Diags(diags), LangOpts(lang), PathConsumers(PDC), CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr), diff --git a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index ca05e38608c..0e3e207c15e 100644 --- a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -204,7 +204,13 @@ bool AnalyzerOptions::includeLifetimeInCFG() { bool AnalyzerOptions::includeLoopExitInCFG() { return getBooleanOption(IncludeLoopExitInCFG, "cfg-loopexit", - /* Default = */ false); + /* Default = */ false); +} + +bool AnalyzerOptions::includeRichConstructorsInCFG() { + return getBooleanOption(IncludeRichConstructorsInCFG, + "cfg-rich-constructors", + /* Default = */ true); } bool AnalyzerOptions::mayInlineCXXStandardLibrary() { diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 3df69e891b7..fef7116d905 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -454,10 +454,11 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, switch (E.getKind()) { case CFGElement::Statement: - ProcessStmt(const_cast<Stmt*>(E.castAs<CFGStmt>().getStmt()), Pred); + case CFGElement::Constructor: + ProcessStmt(E.castAs<CFGStmt>().getStmt(), Pred); return; case CFGElement::Initializer: - ProcessInitializer(E.castAs<CFGInitializer>().getInitializer(), Pred); + ProcessInitializer(E.castAs<CFGInitializer>(), Pred); return; case CFGElement::NewAllocator: ProcessNewAllocator(E.castAs<CFGNewAllocator>().getAllocatorExpr(), @@ -479,7 +480,7 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, } static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, - const CFGStmt S, + const Stmt *S, const ExplodedNode *Pred, const LocationContext *LC) { @@ -492,17 +493,17 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, return true; // Is this on a non-expression? - if (!isa<Expr>(S.getStmt())) + if (!isa<Expr>(S)) return true; // Run before processing a call. - if (CallEvent::isCallStmt(S.getStmt())) + if (CallEvent::isCallStmt(S)) return true; // Is this an expression that is consumed by another expression? If so, // postpone cleaning out the state. ParentMap &PM = LC->getAnalysisDeclContext()->getParentMap(); - return !PM.isConsumedExpr(cast<Expr>(S.getStmt())); + return !PM.isConsumedExpr(cast<Expr>(S)); } void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, @@ -594,20 +595,20 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, } } -void ExprEngine::ProcessStmt(const CFGStmt S, - ExplodedNode *Pred) { +void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) { // Reclaim any unnecessary nodes in the ExplodedGraph. G.reclaimRecentlyAllocatedNodes(); - const Stmt *currStmt = S.getStmt(); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), currStmt->getLocStart(), "Error evaluating statement"); // Remove dead bindings and symbols. ExplodedNodeSet CleanedStates; - if (shouldRemoveDeadBindings(AMgr, S, Pred, Pred->getLocationContext())){ - removeDead(Pred, CleanedStates, currStmt, Pred->getLocationContext()); + if (shouldRemoveDeadBindings(AMgr, currStmt, Pred, + Pred->getLocationContext())) { + removeDead(Pred, CleanedStates, currStmt, + Pred->getLocationContext()); } else CleanedStates.Add(Pred); diff --git a/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 3d4b377627f..561490b09aa 100644 --- a/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -551,6 +551,7 @@ getLocationForCaller(const StackFrameContext *SFC, switch (Source.getKind()) { case CFGElement::Statement: + case CFGElement::Constructor: return PathDiagnosticLocation(Source.castAs<CFGStmt>().getStmt(), SM, CallerCtx); case CFGElement::Initializer: { |