summaryrefslogtreecommitdiffstats
path: root/clang/lib
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/Analysis/AnalysisDeclContext.cpp4
-rw-r--r--clang/lib/Analysis/CFG.cpp86
-rw-r--r--clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp1
-rw-r--r--clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp8
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngine.cpp23
-rw-r--r--clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp1
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: {
OpenPOWER on IntegriCloud