// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify -w -std=c++03 %s // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify -w -std=c++11 %s // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -DTEMPORARY_DTORS -verify -w -analyzer-config cfg-temporary-dtors=true %s -std=c++11 extern bool clang_analyzer_eval(bool); extern bool clang_analyzer_warnIfReached(); struct Trivial { Trivial(int x) : value(x) {} int value; }; struct NonTrivial : public Trivial { NonTrivial(int x) : Trivial(x) {} ~NonTrivial(); }; Trivial getTrivial() { return Trivial(42); // no-warning } const Trivial &getTrivialRef() { return Trivial(42); // expected-warning {{Address of stack memory associated with temporary object of type 'Trivial' returned to caller}} } NonTrivial getNonTrivial() { return NonTrivial(42); // no-warning } const NonTrivial &getNonTrivialRef() { return NonTrivial(42); // expected-warning {{Address of stack memory associated with temporary object of type 'NonTrivial' returned to caller}} } namespace rdar13265460 { struct TrivialSubclass : public Trivial { TrivialSubclass(int x) : Trivial(x), anotherValue(-x) {} int anotherValue; }; TrivialSubclass getTrivialSub() { TrivialSubclass obj(1); obj.value = 42; obj.anotherValue = -42; return obj; } void testImmediate() { TrivialSubclass obj = getTrivialSub(); clang_analyzer_eval(obj.value == 42); // expected-warning{{TRUE}} clang_analyzer_eval(obj.anotherValue == -42); // expected-warning{{TRUE}} clang_analyzer_eval(getTrivialSub().value == 42); // expected-warning{{TRUE}} clang_analyzer_eval(getTrivialSub().anotherValue == -42); // expected-warning{{TRUE}} } void testMaterializeTemporaryExpr() { const TrivialSubclass &ref = getTrivialSub(); clang_analyzer_eval(ref.value == 42); // expected-warning{{TRUE}} const Trivial &baseRef = getTrivialSub(); clang_analyzer_eval(baseRef.value == 42); // expected-warning{{TRUE}} } } namespace rdar13281951 { struct Derived : public Trivial { Derived(int value) : Trivial(value), value2(-value) {} int value2; }; void test() { Derived obj(1); obj.value = 42; const Trivial * const &pointerRef = &obj; clang_analyzer_eval(pointerRef->value == 42); // expected-warning{{TRUE}} } } namespace compound_literals { struct POD { int x, y; }; struct HasCtor { HasCtor(int x, int y) : x(x), y(y) {} int x, y; }; struct HasDtor { int x, y; ~HasDtor(); }; struct HasCtorDtor { HasCtorDtor(int x, int y) : x(x), y(y) {} ~HasCtorDtor(); int x, y; }; void test() { clang_analyzer_eval(((POD){1, 42}).y == 42); // expected-warning{{TRUE}} clang_analyzer_eval(((HasDtor){1, 42}).y == 42); // expected-warning{{TRUE}} #if __cplusplus >= 201103L clang_analyzer_eval(((HasCtor){1, 42}).y == 42); // expected-warning{{TRUE}} // FIXME: should be TRUE, but we don't inline the constructors of // temporaries because we can't model their destructors yet. clang_analyzer_eval(((HasCtorDtor){1, 42}).y == 42); // expected-warning{{UNKNOWN}} #endif } } namespace destructors { struct Dtor { ~Dtor(); }; extern bool coin(); extern bool check(const Dtor &); void testPR16664andPR18159Crash() { // Regression test: we used to assert here when tmp dtors are enabled. // PR16664 and PR18159 if (coin() && (coin() || coin() || check(Dtor()))) { Dtor(); } } #ifdef TEMPORARY_DTORS struct NoReturnDtor { ~NoReturnDtor() __attribute__((noreturn)); }; void noReturnTemp(int *x) { if (! x) NoReturnDtor(); *x = 47; // no warning } void noReturnInline(int **x) { NoReturnDtor(); } void callNoReturn() { int *x; noReturnInline(&x); *x = 47; // no warning } extern bool check(const NoReturnDtor &); void testConsistencyIf(int i) { if (i != 5) return; if (i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) { clang_analyzer_eval(true); // no warning, unreachable code } } void testConsistencyTernary(int i) { (i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) ? 1 : 0; clang_analyzer_eval(true); // expected-warning{{TRUE}} if (i != 5) return; (i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) ? 1 : 0; clang_analyzer_eval(true); // no warning, unreachable code } // Regression test: we used to assert here. // PR16664 and PR18159 void testConsistencyNested(int i) { extern bool compute(bool); if (i == 5 && (i == 4 || i == 5 || check(NoReturnDtor()))) clang_analyzer_eval(true); // expected-warning{{TRUE}} if (i == 5 && (i == 4 || i == 5 || check(NoReturnDtor()))) clang_analyzer_eval(true); // expected-warning{{TRUE}} if (i != 5) return; if (compute(i == 5 && (i == 4 || compute(true) || compute(i == 5 && (i == 4 || check(NoReturnDtor()))))) || i != 4) { clang_analyzer_eval(true); // expected-warning{{TRUE}} } if (compute(i == 5 && (i == 4 || i == 4 || compute(i == 5 && (i == 4 || check(NoReturnDtor()))))) || i != 4) { clang_analyzer_eval(true); // no warning, unreachable code } } // PR16664 and PR18159 void testConsistencyNestedSimple(bool value) { if (value) { if (!value || check(NoReturnDtor())) { clang_analyzer_eval(true); // no warning, unreachable code } } } // PR16664 and PR18159 void testConsistencyNestedComplex(bool value) { if (value) { if (!value || !value || check(NoReturnDtor())) { clang_analyzer_eval(true); // no warning, unreachable code } } } // PR16664 and PR18159 void testConsistencyNestedWarning(bool value) { if (value) { if (!value || value || check(NoReturnDtor())) { clang_analyzer_eval(true); // expected-warning{{TRUE}} } } } // PR16664 and PR18159 void testConsistencyNestedComplexMidBranch(bool value) { if (value) { if (!value || !value || check(NoReturnDtor()) || value) { clang_analyzer_eval(true); // no warning, unreachable code } } } // PR16664 and PR18159 void testConsistencyNestedComplexNestedBranch(bool value) { if (value) { if (!value || (!value || check(NoReturnDtor()) || value)) { clang_analyzer_eval(true); // no warning, unreachable code } } } // PR16664 and PR18159 void testConsistencyNestedVariableModification(bool value) { bool other = true; if (value) { if (!other || !value || (other = false) || check(NoReturnDtor()) || !other) { clang_analyzer_eval(true); // no warning, unreachable code } } } void testTernaryNoReturnTrueBranch(bool value) { if (value) { bool b = value && (value ? check(NoReturnDtor()) : true); clang_analyzer_eval(true); // no warning, unreachable code } } void testTernaryNoReturnFalseBranch(bool value) { if (value) { bool b = !value && !value ? true : check(NoReturnDtor()); clang_analyzer_eval(true); // no warning, unreachable code } } void testTernaryIgnoreNoreturnBranch(bool value) { if (value) { bool b = !value && !value ? check(NoReturnDtor()) : true; clang_analyzer_eval(true); // expected-warning{{TRUE}} } } void testTernaryTrueBranchReached(bool value) { value ? clang_analyzer_warnIfReached() : // expected-warning{{REACHABLE}} check(NoReturnDtor()); } void testTernaryFalseBranchReached(bool value) { value ? check(NoReturnDtor()) : clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} } void testLoop() { for (int i = 0; i < 10; ++i) { if (i < 3 && (i >= 2 || check(NoReturnDtor()))) { clang_analyzer_eval(true); // no warning, unreachable code } } } bool testRecursiveFrames(bool isInner) { if (isInner || (clang_analyzer_warnIfReached(), false) || // expected-warning{{REACHABLE}} check(NoReturnDtor()) || testRecursiveFrames(true)) { clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} } } void testRecursiveFramesStart() { testRecursiveFrames(false); } void testLambdas() { []() { check(NoReturnDtor()); } != nullptr || check(Dtor()); } void testGnuExpressionStatements(int v) { ({ ++v; v == 10 || check(NoReturnDtor()); v == 42; }) || v == 23; clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} ({ ++v; check(NoReturnDtor()); v == 42; }) || v == 23; clang_analyzer_warnIfReached(); // no warning, unreachable code } void testGnuExpressionStatementsDestructionPoint(int v) { // In normal context, the temporary destructor runs at the end of the full // statement, thus the last statement is reached. (++v, check(NoReturnDtor()), v == 42), clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} // GNU expression statements execute temporary destructors within the // blocks, thus the last statement is not reached. ({ ++v; check(NoReturnDtor()); v == 42; }), clang_analyzer_warnIfReached(); // no warning, unreachable code } void testMultipleTemporaries(bool value) { if (value) { // FIXME: Find a way to verify construction order. // ~Dtor should run before ~NoReturnDtor() because construction order is // guaranteed by comma operator. if (!value || check((NoReturnDtor(), Dtor())) || value) { clang_analyzer_eval(true); // no warning, unreachable code } } } void testBinaryOperatorShortcut(bool value) { if (value) { if (false && false && check(NoReturnDtor()) && true) { clang_analyzer_eval(true); } } } void testIfAtEndOfLoop() { int y = 0; while (true) { if (y > 0) { clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} } ++y; // Test that the CFG gets hooked up correctly when temporary destructors // are handled after a statically known branch condition. if (true) (void)0; else (void)check(NoReturnDtor()); } } void testTernaryAtEndOfLoop() { int y = 0; while (true) { if (y > 0) { clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} } ++y; // Test that the CFG gets hooked up correctly when temporary destructors // are handled after a statically known branch condition. true ? (void)0 : (void)check(NoReturnDtor()); } } void testNoReturnInComplexCondition() { check(Dtor()) && (check(NoReturnDtor()) || check(NoReturnDtor())) && check(Dtor()); clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} } void testSequencingOfConditionalTempDtors(bool b) { b || (check(Dtor()), check(NoReturnDtor())); clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} } void testSequencingOfConditionalTempDtors2(bool b) { (b || check(Dtor())), check(NoReturnDtor()); clang_analyzer_warnIfReached(); // no warning, unreachable code } void testSequencingOfConditionalTempDtorsWithinBinaryOperators(bool b) { b || (check(Dtor()) + check(NoReturnDtor())); clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} } void f(Dtor d = Dtor()); void testDefaultParameters() { f(); } struct DefaultParam { DefaultParam(int, const Dtor& d = Dtor()); ~DefaultParam(); }; void testDefaultParamConstructorsInLoops() { while (true) { // FIXME: This exact pattern triggers the temporary cleanup logic // to fail when adding a 'clean' state. DefaultParam(42); DefaultParam(42); } } void testDefaultParamConstructorsInTernariesInLoops(bool value) { while (true) { // FIXME: This exact pattern triggers the temporary cleanup logic // to visit the bind-temporary logic with a state that already has that // temporary marked as executed. 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 } void testStaticMaterializeTemporaryExpr() { static const Trivial &ref = getTrivial(); clang_analyzer_eval(ref.value == 42); // expected-warning{{TRUE}} static const Trivial &directRef = Trivial(42); clang_analyzer_eval(directRef.value == 42); // expected-warning{{TRUE}} #if __has_feature(cxx_thread_local) thread_local static const Trivial &threadRef = getTrivial(); clang_analyzer_eval(threadRef.value == 42); // expected-warning{{TRUE}} thread_local static const Trivial &threadDirectRef = Trivial(42); clang_analyzer_eval(threadDirectRef.value == 42); // expected-warning{{TRUE}} #endif } namespace PR16629 { struct A { explicit A(int* p_) : p(p_) {} int* p; }; extern void escape(const A*[]); extern void check(int); void callEscape(const A& a) { const A* args[] = { &a }; escape(args); } void testNoWarning() { int x; callEscape(A(&x)); check(x); // Analyzer used to give a "x is uninitialized warning" here } void set(const A*a[]) { *a[0]->p = 47; } void callSet(const A& a) { const A* args[] = { &a }; set(args); } void testConsistency() { int x; callSet(A(&x)); clang_analyzer_eval(x == 47); // expected-warning{{TRUE}} } } namespace PR32088 { void testReturnFromStmtExprInitializer() { // We shouldn't try to destroy the object pointed to by `obj' upon return. const NonTrivial &obj = ({ return; // no-crash NonTrivial(42); }); } } namespace CopyToTemporaryCorrectly { class Super { public: void m() { mImpl(); } virtual void mImpl() = 0; }; class Sub : public Super { public: Sub(const int &p) : j(p) {} virtual void mImpl() override { // Used to be undefined pointer dereference because we didn't copy // the subclass data (j) to the temporary object properly. (void)(j + 1); // no-warning if (j != 22) { clang_analyzer_warnIfReached(); // no-warning } } const int &j; }; void run() { int i = 22; Sub(i).m(); } }