Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit 3efe802

Browse files
committed
[analyzer] Add sink after construction of temporary with no-return destructor.
The analyzer's CFG currently doesn't have nodes for calls to temporary destructors. This causes the analyzer to explore infeasible paths in which a no-return destructor would have stopped exploration and so results in false positives when no-return destructors are used to implement assertions. To mitigate these false positives, this patch stops generates a sink after evaluating a constructor on a temporary object that has a no-return destructor. This results in a loss of coverage because the time at which the destructor is called may be after the time of construction (especially for lifetime-extended temporaries). This addresses PR15599. rdar://problem/29131566 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@290140 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 5cc8e8e commit 3efe802

File tree

2 files changed

+50
-0
lines changed

2 files changed

+50
-0
lines changed

Diff for: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp

+24
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,30 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
346346
defaultEvalCall(Bldr, *I, *Call);
347347
}
348348

349+
// If the CFG was contructed without elements for temporary destructors
350+
// and the just-called constructor created a temporary object then
351+
// stop exploration if the temporary object has a noreturn constructor.
352+
// This can lose coverage because the destructor, if it were present
353+
// in the CFG, would be called at the end of the full expression or
354+
// later (for life-time extended temporaries) -- but avoids infeasible
355+
// paths when no-return temporary destructors are used for assertions.
356+
const AnalysisDeclContext *ADC = LCtx->getAnalysisDeclContext();
357+
if (!ADC->getCFGBuildOptions().AddTemporaryDtors) {
358+
const MemRegion *Target = Call->getCXXThisVal().getAsRegion();
359+
if (Target && isa<CXXTempObjectRegion>(Target) &&
360+
Call->getDecl()->getParent()->isAnyDestructorNoReturn()) {
361+
362+
for (ExplodedNode *N : DstEvaluated) {
363+
Bldr.generateSink(CE, N, N->getState());
364+
}
365+
366+
// There is no need to run the PostCall and PostStmtchecker
367+
// callbacks because we just generated sinks on all nodes in th
368+
// frontier.
369+
return;
370+
}
371+
}
372+
349373
ExplodedNodeSet DstPostCall;
350374
getCheckerManager().runCheckersForPostCall(DstPostCall, DstEvaluated,
351375
*Call, *this);

Diff for: test/Analysis/temporaries.cpp

+26
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,32 @@ namespace destructors {
413413
value ? DefaultParam(42) : DefaultParam(42);
414414
}
415415
}
416+
#else // !TEMPORARY_DTORS
417+
418+
// Test for fallback logic that conservatively stops exploration after
419+
// executing a temporary constructor for a class with a no-return destructor
420+
// when temporary destructors are not enabled in the CFG.
421+
422+
struct CtorWithNoReturnDtor {
423+
CtorWithNoReturnDtor() = default;
424+
425+
~CtorWithNoReturnDtor() __attribute__((noreturn));
426+
};
427+
428+
void testDefaultContructorWithNoReturnDtor() {
429+
CtorWithNoReturnDtor();
430+
clang_analyzer_warnIfReached(); // no-warning
431+
}
432+
433+
void testLifeExtensionWithNoReturnDtor() {
434+
const CtorWithNoReturnDtor &c = CtorWithNoReturnDtor();
435+
436+
// This represents an (expected) loss of coverage, since the destructor
437+
// of the lifetime-exended temporary is executed at at the end of
438+
// scope.
439+
clang_analyzer_warnIfReached(); // no-warning
440+
}
441+
416442
#endif // TEMPORARY_DTORS
417443
}
418444

0 commit comments

Comments
 (0)