Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -199,12 +199,20 @@ cast(SFC->getCallSite()), State, CallerLCtx, RTC->getConstructionContext(), CallOpts); } else { - // We are on the top frame of the analysis. - // TODO: What exactly happens when we are? Does the temporary object - // live long enough in the region store in this case? Would checkers - // think that this object immediately goes out of scope? - CallOpts.IsTemporaryCtorOrDtor = true; + // We are on the top frame of the analysis. We do not know where is the + // object returned to. + // In C++17, do not inline the constructor, because the object + // represented by the returned expression will not live long enough + // according to liveness analysis. + // TODO: Does it live long enough in all other cases? + // It definitely does in most cases. + if (CC->getKind() == + ConstructionContext::CXX17ElidedCopyReturnedValueKind) { + CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true; + } + // TODO: We probably need a new MemRegion kind to represent such object. SVal V = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx)); + CallOpts.IsTemporaryCtorOrDtor = true; return std::make_pair(State, V); } llvm_unreachable("Unhandled return value construction context!"); Index: test/Analysis/temporaries.cpp =================================================================== --- test/Analysis/temporaries.cpp +++ test/Analysis/temporaries.cpp @@ -1,9 +1,25 @@ -// RUN: %clang_analyze_cc1 -Wno-non-pod-varargs -analyzer-checker=core,cplusplus,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify -w -std=c++03 -analyzer-config eagerly-assume=false %s -// RUN: %clang_analyze_cc1 -Wno-non-pod-varargs -analyzer-checker=core,cplusplus,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify -w -std=c++11 -analyzer-config eagerly-assume=false %s -// RUN: %clang_analyze_cc1 -Wno-non-pod-varargs -analyzer-checker=core,cplusplus,debug.ExprInspection -DTEMPORARY_DTORS -verify -w -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true -analyzer-config eagerly-assume=false %s -std=c++11 -// RUN: %clang_analyze_cc1 -Wno-non-pod-varargs -analyzer-checker=core,cplusplus,debug.ExprInspection -DTEMPORARY_DTORS -w -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true -analyzer-config eagerly-assume=false %s -std=c++17 +// RUN: %clang_analyze_cc1 -w -analyzer-checker=core,cplusplus\ +// RUN: -analyzer-checker debug.ExprInspection -Wno-non-pod-varargs\ +// RUN: -analyzer-config eagerly-assume=false -verify %s\ +// RUN: -std=c++03 -analyzer-config cfg-temporary-dtors=false + +// RUN: %clang_analyze_cc1 -w -analyzer-checker=core,cplusplus\ +// RUN: -analyzer-checker debug.ExprInspection -Wno-non-pod-varargs\ +// RUN: -analyzer-config eagerly-assume=false -verify %s\ +// RUN: -std=c++11 -analyzer-config cfg-temporary-dtors=false + +// RUN: %clang_analyze_cc1 -w -analyzer-checker=core,cplusplus\ +// RUN: -analyzer-checker debug.ExprInspection -Wno-non-pod-varargs\ +// RUN: -analyzer-config eagerly-assume=false -verify %s\ +// RUN: -std=c++11 -analyzer-config cfg-temporary-dtors=true\ +// RUN: -DTEMPORARY_DTORS + +// RUN: %clang_analyze_cc1 -w -analyzer-checker=core,cplusplus\ +// RUN: -analyzer-checker debug.ExprInspection -Wno-non-pod-varargs\ +// RUN: -analyzer-config eagerly-assume=false -verify %s\ +// RUN: -std=c++17 -analyzer-config cfg-temporary-dtors=true\ +// RUN: -DTEMPORARY_DTORS -// Note: The C++17 run-line doesn't -verify yet - it is a no-crash test. extern bool clang_analyzer_eval(bool); extern bool clang_analyzer_warnIfReached(); @@ -805,7 +821,12 @@ // On each branch the variable is constructed directly. if (coin) { clang_analyzer_eval(x == 1); // expected-warning{{TRUE}} +#if __cplusplus < 201703L clang_analyzer_eval(y == 1); // expected-warning{{TRUE}} +#else + // FIXME: Destructor called twice in C++17? + clang_analyzer_eval(y == 2); // expected-warning{{TRUE}} +#endif clang_analyzer_eval(z == 0); // expected-warning{{TRUE}} clang_analyzer_eval(w == 0); // expected-warning{{TRUE}} @@ -813,7 +834,12 @@ clang_analyzer_eval(x == 0); // expected-warning{{TRUE}} clang_analyzer_eval(y == 0); // expected-warning{{TRUE}} clang_analyzer_eval(z == 1); // expected-warning{{TRUE}} +#if __cplusplus < 201703L clang_analyzer_eval(w == 1); // expected-warning{{TRUE}} +#else + // FIXME: Destructor called twice in C++17? + clang_analyzer_eval(w == 2); // expected-warning{{TRUE}} +#endif } } } // namespace test_match_constructors_and_destructors @@ -865,9 +891,12 @@ public: ~C() { glob = 1; + // FIXME: Why is destructor not inlined in C++17 clang_analyzer_checkInlined(true); #ifdef TEMPORARY_DTORS - // expected-warning@-2{{TRUE}} +#if __cplusplus < 201703L + // expected-warning@-3{{TRUE}} +#endif #endif } }; @@ -886,11 +915,16 @@ // temporaries returned from functions, so we took the wrong branch. coin && is(get()); // no-crash if (coin) { + // FIXME: Why is destructor not inlined in C++17 clang_analyzer_eval(glob); #ifdef TEMPORARY_DTORS - // expected-warning@-2{{TRUE}} +#if __cplusplus < 201703L + // expected-warning@-3{{TRUE}} #else - // expected-warning@-4{{UNKNOWN}} + // expected-warning@-5{{UNKNOWN}} +#endif +#else + // expected-warning@-8{{UNKNOWN}} #endif } else { // The destructor is not called on this branch. @@ -1012,11 +1046,16 @@ #endif bar2(S(2)); + // FIXME: Why are we losing information in C++17? clang_analyzer_eval(glob == 2); #ifdef TEMPORARY_DTORS - // expected-warning@-2{{TRUE}} +#if __cplusplus < 201703L + // expected-warning@-3{{TRUE}} #else - // expected-warning@-4{{UNKNOWN}} + // expected-warning@-5{{UNKNOWN}} +#endif +#else + // expected-warning@-8{{UNKNOWN}} #endif C *c = new D(); @@ -1172,3 +1211,21 @@ c.foo(); } } // namespace union_indirect_field_crash + +namespace return_from_top_frame { +struct S { + int *p; + S() { p = new int; } + S(S &&s) : p(s.p) { s.p = 0; } + ~S(); // Presumably releases 'p'. +}; + +S foo() { + S s; + return s; +} + +S bar() { + return foo(); // no-warning +} +} // namespace return_from_top_frame