return State->BindExpr(E, LCtx, ThisV);
}
- // Conjure a symbol if the return value is unknown.
+ SVal R;
QualType ResultTy = Call.getResultType();
- SValBuilder &SVB = getSValBuilder();
unsigned Count = currBldrCtx->blockCount();
+ if (auto RTC = getCurrentCFGElement().getAs<CFGCXXRecordTypedCall>()) {
+ // Conjure a temporary if the function returns an object by value.
+ MemRegionManager &MRMgr = svalBuilder.getRegionManager();
+ const CXXTempObjectRegion *TR = MRMgr.getCXXTempObjectRegion(E, LCtx);
+ State = addAllNecessaryTemporaryInfo(State, RTC->getConstructionContext(),
+ LCtx, TR);
+
+ // Invalidate the region so that it didn't look uninitialized. Don't notify
+ // the checkers.
+ State = State->invalidateRegions(TR, E, Count, LCtx,
+ /* CausedByPointerEscape=*/false, nullptr,
+ &Call, nullptr);
+
+ R = State->getSVal(TR, E->getType());
+ } else {
+ // Conjure a symbol if the return value is unknown.
+
+ // See if we need to conjure a heap pointer instead of
+ // a regular unknown pointer.
+ bool IsHeapPointer = false;
+ if (const auto *CNE = dyn_cast<CXXNewExpr>(E))
+ if (CNE->getOperatorNew()->isReplaceableGlobalAllocationFunction()) {
+ // FIXME: Delegate this to evalCall in MallocChecker?
+ IsHeapPointer = true;
+ }
- // See if we need to conjure a heap pointer instead of
- // a regular unknown pointer.
- bool IsHeapPointer = false;
- if (const auto *CNE = dyn_cast<CXXNewExpr>(E))
- if (CNE->getOperatorNew()->isReplaceableGlobalAllocationFunction()) {
- // FIXME: Delegate this to evalCall in MallocChecker?
- IsHeapPointer = true;
- }
-
- SVal R = IsHeapPointer
- ? SVB.getConjuredHeapSymbolVal(E, LCtx, Count)
- : SVB.conjureSymbolVal(nullptr, E, LCtx, ResultTy, Count);
+ R = IsHeapPointer ? svalBuilder.getConjuredHeapSymbolVal(E, LCtx, Count)
+ : svalBuilder.conjureSymbolVal(nullptr, E, LCtx, ResultTy,
+ Count);
+ }
return State->BindExpr(E, LCtx, R);
}
}
} // namespace test_match_constructors_and_destructors
+namespace destructors_for_return_values {
+
+class C {
+public:
+ ~C() {
+ 1 / 0; // expected-warning{{Division by zero}}
+ }
+};
+
+C make();
+
+void testFloatingCall() {
+ make();
+ // Should have divided by zero in the destructor.
+ clang_analyzer_warnIfReached();
+#ifndef TEMPORARY_DTORS
+ // expected-warning@-2{{REACHABLE}}
+#endif
+}
+
+void testLifetimeExtendedCall() {
+ {
+ const C &c = make();
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+ // Should have divided by zero in the destructor.
+ clang_analyzer_warnIfReached(); // no-warning
+}
+
+void testCopiedCall() {
+ C c = make();
+ // Should have divided by zero in the temporary destructor.
+ clang_analyzer_warnIfReached();
+#ifndef TEMPORARY_DTORS
+ // expected-warning@-2{{REACHABLE}}
+#endif
+}
+} // namespace destructors_for_return_values
+
namespace dont_forget_destructor_around_logical_op {
int glob;
// return value of get() was initialized. However, we didn't track
// temporaries returned from functions, so we took the wrong branch.
coin && is(get()); // no-crash
- // FIXME: Should be true once we inline all destructors.
- clang_analyzer_eval(glob); // expected-warning{{UNKNOWN}}
+ if (coin) {
+ clang_analyzer_eval(glob);
+#ifdef TEMPORARY_DTORS
+ // expected-warning@-2{{TRUE}}
+#else
+ // expected-warning@-4{{UNKNOWN}}
+#endif
+ } else {
+ // The destructor is not called on this branch.
+ clang_analyzer_eval(glob); // expected-warning{{UNKNOWN}}
+ }
}
} // namespace dont_forget_destructor_around_logical_op