summaryrefslogtreecommitdiffstats
path: root/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/StaticAnalyzer/Core/ExprEngine.cpp')
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngine.cpp103
1 files changed, 93 insertions, 10 deletions
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index f4636ef18aa..b30a4417e9a 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -51,6 +51,15 @@ STATISTIC(NumMaxBlockCountReachedInInlined,
STATISTIC(NumTimesRetriedWithoutInlining,
"The # of times we re-evaluated a call without inlining");
+typedef std::pair<const CXXBindTemporaryExpr *, const StackFrameContext *>
+ CXXBindTemporaryContext;
+
+// Keeps track of whether CXXBindTemporaryExpr nodes have been evaluated.
+// The StackFrameContext assures that nested calls due to inlined recursive
+// functions do not interfere.
+REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedTemporariesSet,
+ llvm::ImmutableSet<CXXBindTemporaryContext>)
+
//===----------------------------------------------------------------------===//
// Engine construction and deletion.
//===----------------------------------------------------------------------===//
@@ -659,13 +668,59 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
+ ExplodedNodeSet CleanDtorState;
+ StmtNodeBuilder StmtBldr(Pred, CleanDtorState, *currBldrCtx);
+ ProgramStateRef State = Pred->getState();
+ assert(State->contains<InitializedTemporariesSet>(
+ std::make_pair(D.getBindTemporaryExpr(), Pred->getStackFrame())));
+ State = State->remove<InitializedTemporariesSet>(
+ std::make_pair(D.getBindTemporaryExpr(), Pred->getStackFrame()));
+ StmtBldr.generateNode(D.getBindTemporaryExpr(), Pred, State);
QualType varType = D.getBindTemporaryExpr()->getSubExpr()->getType();
-
- // FIXME: Inlining of temporary destructors is not supported yet anyway, so we
- // just put a NULL region for now. This will need to be changed later.
+ assert(CleanDtorState.size() == 1);
+ ExplodedNode *CleanPred = *CleanDtorState.begin();
+ // FIXME: Inlining of temporary destructors is not supported yet anyway, so
+ // we just put a NULL region for now. This will need to be changed later.
VisitCXXDestructor(varType, nullptr, D.getBindTemporaryExpr(),
- /*IsBase=*/ false, Pred, Dst);
+ /*IsBase=*/false, CleanPred, Dst);
+}
+
+void ExprEngine::processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
+ NodeBuilderContext &BldCtx,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst,
+ const CFGBlock *DstT,
+ const CFGBlock *DstF) {
+ BranchNodeBuilder TempDtorBuilder(Pred, Dst, BldCtx, DstT, DstF);
+ if (Pred->getState()->contains<InitializedTemporariesSet>(
+ std::make_pair(BTE, Pred->getStackFrame()))) {
+ TempDtorBuilder.markInfeasible(false);
+ TempDtorBuilder.generateNode(Pred->getState(), true, Pred);
+ } else {
+ TempDtorBuilder.markInfeasible(true);
+ TempDtorBuilder.generateNode(Pred->getState(), false, Pred);
+ }
+}
+
+void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE,
+ ExplodedNodeSet &PreVisit,
+ ExplodedNodeSet &Dst) {
+ if (!getAnalysisManager().options.includeTemporaryDtorsInCFG()) {
+ // In case we don't have temporary destructors in the CFG, do not mark
+ // the initialization - we would otherwise never clean it up.
+ Dst = PreVisit;
+ return;
+ }
+ StmtNodeBuilder StmtBldr(PreVisit, Dst, *currBldrCtx);
+ for (ExplodedNode *Node : PreVisit) {
+ ProgramStateRef State = Node->getState();
+ assert(!State->contains<InitializedTemporariesSet>(
+ std::make_pair(BTE, Node->getStackFrame())));
+ State = State->add<InitializedTemporariesSet>(
+ std::make_pair(BTE, Node->getStackFrame()));
+ StmtBldr.generateNode(BTE, Node, State);
+ }
}
void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
@@ -773,6 +828,17 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
// Handled due to fully linearised CFG.
break;
+ case Stmt::CXXBindTemporaryExprClass: {
+ Bldr.takeNodes(Pred);
+ ExplodedNodeSet PreVisit;
+ getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
+ ExplodedNodeSet Next;
+ VisitCXXBindTemporaryExpr(cast<CXXBindTemporaryExpr>(S), PreVisit, Next);
+ getCheckerManager().runCheckersForPostStmt(Dst, Next, S, *this);
+ Bldr.addNodes(Dst);
+ break;
+ }
+
// Cases not handled yet; but will handle some day.
case Stmt::DesignatedInitExprClass:
case Stmt::ExtVectorElementExprClass:
@@ -810,7 +876,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::SizeOfPackExprClass:
case Stmt::StringLiteralClass:
case Stmt::ObjCStringLiteralClass:
- case Stmt::CXXBindTemporaryExprClass:
case Stmt::CXXPseudoDestructorExprClass:
case Stmt::SubstNonTypeTemplateParmExprClass:
case Stmt::CXXNullPtrLiteralExprClass: {
@@ -1405,11 +1470,8 @@ static const Stmt *ResolveCondition(const Stmt *Condition,
if (!BO || !BO->isLogicalOp())
return Condition;
- // FIXME: This is a workaround until we handle temporary destructor branches
- // correctly; currently, temporary destructor branches lead to blocks that
- // only have a terminator (and no statements). These blocks violate the
- // invariant this function assumes.
- if (B->getTerminator().isTemporaryDtorsBranch()) return Condition;
+ assert(!B->getTerminator().isTemporaryDtorsBranch() &&
+ "Temporary destructor branches handled by processBindTemporary.");
// For logical operations, we still have the case where some branches
// use the traditional "merge" approach and others sink the branch
@@ -1438,6 +1500,8 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
ExplodedNodeSet &Dst,
const CFGBlock *DstT,
const CFGBlock *DstF) {
+ assert((!Condition || !isa<CXXBindTemporaryExpr>(Condition)) &&
+ "CXXBindTemporaryExprs are handled by processBindTemporary.");
const LocationContext *LCtx = Pred->getLocationContext();
PrettyStackTraceLocationContext StackCrashInfo(LCtx);
currBldrCtx = &BldCtx;
@@ -1601,10 +1665,29 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) {
builder.generateNode(I, state);
}
+#if 0
+static bool stackFrameDoesNotContainInitializedTemporaries(ExplodedNode &Pred) {
+ const StackFrameContext* Frame = Pred.getStackFrame();
+ const llvm::ImmutableSet<CXXBindTemporaryContext> &Set =
+ Pred.getState()->get<InitializedTemporariesSet>();
+ return std::find_if(Set.begin(), Set.end(),
+ [&](const CXXBindTemporaryContext &Ctx) {
+ if (Ctx.second == Frame) {
+ Ctx.first->dump();
+ llvm::errs() << "\n";
+ }
+ return Ctx.second == Frame;
+ }) == Set.end();
+}
+#endif
+
/// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path
/// nodes when the control reaches the end of a function.
void ExprEngine::processEndOfFunction(NodeBuilderContext& BC,
ExplodedNode *Pred) {
+ // FIXME: Assert that stackFrameDoesNotContainInitializedTemporaries(*Pred)).
+ // We currently cannot enable this assert, as lifetime extended temporaries
+ // are not modelled correctly.
PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext());
StateMgr.EndPath(Pred->getState());
OpenPOWER on IntegriCloud