forked from OSchip/llvm-project
[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 llvm-svn: 290140
This commit is contained in:
parent
d9430944f4
commit
5b1ee2fad9
|
@ -346,6 +346,30 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
|
||||||
defaultEvalCall(Bldr, *I, *Call);
|
defaultEvalCall(Bldr, *I, *Call);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the CFG was contructed without elements for temporary destructors
|
||||||
|
// and the just-called constructor created a temporary object then
|
||||||
|
// stop exploration if the temporary object has a noreturn constructor.
|
||||||
|
// This can lose coverage because the destructor, if it were present
|
||||||
|
// in the CFG, would be called at the end of the full expression or
|
||||||
|
// later (for life-time extended temporaries) -- but avoids infeasible
|
||||||
|
// paths when no-return temporary destructors are used for assertions.
|
||||||
|
const AnalysisDeclContext *ADC = LCtx->getAnalysisDeclContext();
|
||||||
|
if (!ADC->getCFGBuildOptions().AddTemporaryDtors) {
|
||||||
|
const MemRegion *Target = Call->getCXXThisVal().getAsRegion();
|
||||||
|
if (Target && isa<CXXTempObjectRegion>(Target) &&
|
||||||
|
Call->getDecl()->getParent()->isAnyDestructorNoReturn()) {
|
||||||
|
|
||||||
|
for (ExplodedNode *N : DstEvaluated) {
|
||||||
|
Bldr.generateSink(CE, N, N->getState());
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is no need to run the PostCall and PostStmtchecker
|
||||||
|
// callbacks because we just generated sinks on all nodes in th
|
||||||
|
// frontier.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ExplodedNodeSet DstPostCall;
|
ExplodedNodeSet DstPostCall;
|
||||||
getCheckerManager().runCheckersForPostCall(DstPostCall, DstEvaluated,
|
getCheckerManager().runCheckersForPostCall(DstPostCall, DstEvaluated,
|
||||||
*Call, *this);
|
*Call, *this);
|
||||||
|
|
|
@ -413,6 +413,32 @@ namespace destructors {
|
||||||
value ? DefaultParam(42) : DefaultParam(42);
|
value ? DefaultParam(42) : DefaultParam(42);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else // !TEMPORARY_DTORS
|
||||||
|
|
||||||
|
// Test for fallback logic that conservatively stops exploration after
|
||||||
|
// executing a temporary constructor for a class with a no-return destructor
|
||||||
|
// when temporary destructors are not enabled in the CFG.
|
||||||
|
|
||||||
|
struct CtorWithNoReturnDtor {
|
||||||
|
CtorWithNoReturnDtor() = default;
|
||||||
|
|
||||||
|
~CtorWithNoReturnDtor() __attribute__((noreturn));
|
||||||
|
};
|
||||||
|
|
||||||
|
void testDefaultContructorWithNoReturnDtor() {
|
||||||
|
CtorWithNoReturnDtor();
|
||||||
|
clang_analyzer_warnIfReached(); // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
|
void testLifeExtensionWithNoReturnDtor() {
|
||||||
|
const CtorWithNoReturnDtor &c = CtorWithNoReturnDtor();
|
||||||
|
|
||||||
|
// This represents an (expected) loss of coverage, since the destructor
|
||||||
|
// of the lifetime-exended temporary is executed at at the end of
|
||||||
|
// scope.
|
||||||
|
clang_analyzer_warnIfReached(); // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
#endif // TEMPORARY_DTORS
|
#endif // TEMPORARY_DTORS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue