std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
const ConstructionContext *CC, EvalCallOptions &CallOpts) {
- MemRegionManager &MRMgr = getSValBuilder().getRegionManager();
+ SValBuilder &SVB = getSValBuilder();
+ MemRegionManager &MRMgr = SVB.getRegionManager();
+ ASTContext &ACtx = SVB.getContext();
// See if we're constructing an existing region by looking at the
// current construction context.
assert(Init->isAnyMemberInitializer());
const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
Loc ThisPtr =
- getSValBuilder().getCXXThis(CurCtor, LCtx->getStackFrame());
+ SVB.getCXXThis(CurCtor, LCtx->getStackFrame());
SVal ThisVal = State->getSVal(ThisPtr);
const ValueDecl *Field;
cast<Expr>(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;
- SVal V = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
+ // We are on the top frame of the analysis. We do not know where is the
+ // object returned to. Conjure a symbolic region for the return value.
+ // TODO: We probably need a new MemRegion kind to represent the storage
+ // of that SymbolicRegion, so that we cound produce a fancy symbol
+ // instead of an anonymous conjured symbol.
+ // TODO: Do we need to track the region to avoid having it dead
+ // too early? It does die too early, at least in C++17, but because
+ // putting anything into a SymbolicRegion causes an immediate escape,
+ // it doesn't cause any leak false positives.
+ const auto *RCC = cast<ReturnedValueConstructionContext>(CC);
+ // Make sure that this doesn't coincide with any other symbol
+ // conjured for the returned expression.
+ static const int TopLevelSymRegionTag = 0;
+ const Expr *RetE = RCC->getReturnStmt()->getRetValue();
+ assert(RetE && "Void returns should not have a construction context");
+ QualType ReturnTy = RetE->getType();
+ QualType RegionTy = ACtx.getPointerType(ReturnTy);
+ SVal V = SVB.conjureSymbolVal(&TopLevelSymRegionTag, RetE, SFC,
+ RegionTy, currBldrCtx->blockCount());
return std::make_pair(State, V);
}
llvm_unreachable("Unhandled return value construction context!");
-// 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();
}
#if __cplusplus >= 201103L
- CtorWithNoReturnDtor returnNoReturnDtor() {
+ struct CtorWithNoReturnDtor2 {
+ CtorWithNoReturnDtor2() = default;
+
+ CtorWithNoReturnDtor2(int x) {
+ clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
+ }
+
+ ~CtorWithNoReturnDtor2() __attribute__((noreturn));
+ };
+ CtorWithNoReturnDtor2 returnNoReturnDtor() {
return {1}; // no-crash
}
#endif
// 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}}
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
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
}
};
// 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@-5{{UNKNOWN}}
+#endif
#else
- // expected-warning@-4{{UNKNOWN}}
+ // expected-warning@-8{{UNKNOWN}}
#endif
} else {
// The destructor is not called on this branch.
#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();
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 bar1() {
+ return foo(); // no-warning
+}
+
+S bar2() {
+ return S();
+}
+
+S bar3(int coin) {
+ return coin ? S() : foo(); // no-warning
+}
+} // namespace return_from_top_frame