diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index ede48161d933..5aace88cfa0f 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -581,23 +581,39 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, 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()) { + // 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); - // See if we need to conjure a heap pointer instead of - // a regular unknown pointer. - bool IsHeapPointer = false; - if (const auto *CNE = dyn_cast(E)) - if (CNE->getOperatorNew()->isReplaceableGlobalAllocationFunction()) { - // FIXME: Delegate this to evalCall in MallocChecker? - IsHeapPointer = true; - } + // 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); - SVal R = IsHeapPointer - ? SVB.getConjuredHeapSymbolVal(E, LCtx, Count) - : SVB.conjureSymbolVal(nullptr, E, LCtx, ResultTy, Count); + 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(E)) + if (CNE->getOperatorNew()->isReplaceableGlobalAllocationFunction()) { + // FIXME: Delegate this to evalCall in MallocChecker? + IsHeapPointer = true; + } + + R = IsHeapPointer ? svalBuilder.getConjuredHeapSymbolVal(E, LCtx, Count) + : svalBuilder.conjureSymbolVal(nullptr, E, LCtx, ResultTy, + Count); + } return State->BindExpr(E, LCtx, R); } diff --git a/clang/test/Analysis/explain-svals.cpp b/clang/test/Analysis/explain-svals.cpp index d4b56a348252..ecb5af1f8efb 100644 --- a/clang/test/Analysis/explain-svals.cpp +++ b/clang/test/Analysis/explain-svals.cpp @@ -94,5 +94,5 @@ public: void test_6() { clang_analyzer_explain(conjure_S()); // expected-warning-re{{{{^lazily frozen compound value of temporary object constructed at statement 'conjure_S\(\)'$}}}} - clang_analyzer_explain(conjure_S().z); // expected-warning-re{{{{^value derived from \(symbol of type 'struct S' conjured at statement 'conjure_S\(\)'\) for field 'z' of temporary object constructed at statement 'conjure_S\(\)'$}}}} + clang_analyzer_explain(conjure_S().z); // expected-warning-re{{{{^value derived from \(symbol of type 'int' conjured at statement 'conjure_S\(\)'\) for field 'z' of temporary object constructed at statement 'conjure_S\(\)'$}}}} } diff --git a/clang/test/Analysis/temporaries.cpp b/clang/test/Analysis/temporaries.cpp index 681ccc957e0d..0d2c610d6b8a 100644 --- a/clang/test/Analysis/temporaries.cpp +++ b/clang/test/Analysis/temporaries.cpp @@ -895,6 +895,45 @@ void test_ternary_temporary_with_copy(int coin) { } } // 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; @@ -922,8 +961,17 @@ void test(int coin) { // 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