// 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 extern bool clang_analyzer_eval(bool); extern bool clang_analyzer_warnIfReached(); void clang_analyzer_checkInlined(bool); #include "Inputs/system-header-simulator-cxx.h" 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(int x) { clang_analyzer_checkInlined(false); // no-warning } ~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 the end of // scope. clang_analyzer_warnIfReached(); // no-warning } #if __cplusplus >= 201103L struct CtorWithNoReturnDtor2 { CtorWithNoReturnDtor2() = default; CtorWithNoReturnDtor2(int x) { clang_analyzer_checkInlined(true); // expected-warning{{TRUE}} } ~CtorWithNoReturnDtor2() __attribute__((noreturn)); }; CtorWithNoReturnDtor2 returnNoReturnDtor() { return {1}; // no-crash } #endif #endif // TEMPORARY_DTORS } namespace default_param_elided_destructors { struct a { ~a(); }; struct F { a d; F(char *, a = a()); }; void g() { char h[1]; for (int i = 0;;) F j(i ? j : h); } } // namespace default_param_elided_destructors 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(); } } namespace test_return_temporary { class C { int x, y; public: C(int x, int y) : x(x), y(y) {} int getX() const { return x; } int getY() const { return y; } ~C() {} }; class D: public C { public: D() : C(1, 2) {} D(const D &d): C(d.getX(), d.getY()) {} }; C returnTemporaryWithVariable() { C c(1, 2); return c; } C returnTemporaryWithAnotherFunctionWithVariable() { return returnTemporaryWithVariable(); } C returnTemporaryWithCopyConstructionWithVariable() { return C(returnTemporaryWithVariable()); } C returnTemporaryWithConstruction() { return C(1, 2); } C returnTemporaryWithAnotherFunctionWithConstruction() { return returnTemporaryWithConstruction(); } C returnTemporaryWithCopyConstructionWithConstruction() { return C(returnTemporaryWithConstruction()); } D returnTemporaryWithVariableAndNonTrivialCopy() { D d; return d; } D returnTemporaryWithAnotherFunctionWithVariableAndNonTrivialCopy() { return returnTemporaryWithVariableAndNonTrivialCopy(); } D returnTemporaryWithCopyConstructionWithVariableAndNonTrivialCopy() { return D(returnTemporaryWithVariableAndNonTrivialCopy()); } #if __cplusplus >= 201103L C returnTemporaryWithBraces() { return {1, 2}; } C returnTemporaryWithAnotherFunctionWithBraces() { return returnTemporaryWithBraces(); } C returnTemporaryWithCopyConstructionWithBraces() { return C(returnTemporaryWithBraces()); } #endif // C++11 void test() { C c1 = returnTemporaryWithVariable(); clang_analyzer_eval(c1.getX() == 1); // expected-warning{{TRUE}} clang_analyzer_eval(c1.getY() == 2); // expected-warning{{TRUE}} C c2 = returnTemporaryWithAnotherFunctionWithVariable(); clang_analyzer_eval(c2.getX() == 1); // expected-warning{{TRUE}} clang_analyzer_eval(c2.getY() == 2); // expected-warning{{TRUE}} C c3 = returnTemporaryWithCopyConstructionWithVariable(); clang_analyzer_eval(c3.getX() == 1); // expected-warning{{TRUE}} clang_analyzer_eval(c3.getY() == 2); // expected-warning{{TRUE}} C c4 = returnTemporaryWithConstruction(); clang_analyzer_eval(c4.getX() == 1); // expected-warning{{TRUE}} clang_analyzer_eval(c4.getY() == 2); // expected-warning{{TRUE}} C c5 = returnTemporaryWithAnotherFunctionWithConstruction(); clang_analyzer_eval(c5.getX() == 1); // expected-warning{{TRUE}} clang_analyzer_eval(c5.getY() == 2); // expected-warning{{TRUE}} C c6 = returnTemporaryWithCopyConstructionWithConstruction(); clang_analyzer_eval(c5.getX() == 1); // expected-warning{{TRUE}} clang_analyzer_eval(c5.getY() == 2); // expected-warning{{TRUE}} #if __cplusplus >= 201103L C c7 = returnTemporaryWithBraces(); clang_analyzer_eval(c7.getX() == 1); // expected-warning{{TRUE}} clang_analyzer_eval(c7.getY() == 2); // expected-warning{{TRUE}} C c8 = returnTemporaryWithAnotherFunctionWithBraces(); clang_analyzer_eval(c8.getX() == 1); // expected-warning{{TRUE}} clang_analyzer_eval(c8.getY() == 2); // expected-warning{{TRUE}} C c9 = returnTemporaryWithCopyConstructionWithBraces(); clang_analyzer_eval(c9.getX() == 1); // expected-warning{{TRUE}} clang_analyzer_eval(c9.getY() == 2); // expected-warning{{TRUE}} #endif // C++11 D d1 = returnTemporaryWithVariableAndNonTrivialCopy(); clang_analyzer_eval(d1.getX() == 1); // expected-warning{{TRUE}} clang_analyzer_eval(d1.getY() == 2); // expected-warning{{TRUE}} D d2 = returnTemporaryWithAnotherFunctionWithVariableAndNonTrivialCopy(); clang_analyzer_eval(d2.getX() == 1); // expected-warning{{TRUE}} clang_analyzer_eval(d2.getY() == 2); // expected-warning{{TRUE}} D d3 = returnTemporaryWithCopyConstructionWithVariableAndNonTrivialCopy(); clang_analyzer_eval(d3.getX() == 1); // expected-warning{{TRUE}} clang_analyzer_eval(d3.getY() == 2); // expected-warning{{TRUE}} } } // namespace test_return_temporary namespace test_temporary_object_expr_without_dtor { class C { int x; public: C(int x) : x(x) {} int getX() const { return x; } }; void test() { clang_analyzer_eval(C(3).getX() == 3); // expected-warning{{TRUE}} }; } namespace test_temporary_object_expr_with_dtor { class C { int x; public: C(int x) : x(x) {} ~C() {} int getX() const { return x; } }; void test(int coin) { clang_analyzer_eval(C(3).getX() == 3); #ifdef TEMPORARY_DTORS // expected-warning@-2{{TRUE}} #else // expected-warning@-4{{UNKNOWN}} #endif const C &c1 = coin ? C(1) : C(2); if (coin) { clang_analyzer_eval(c1.getX() == 1); #ifdef TEMPORARY_DTORS // expected-warning@-2{{TRUE}} #else // expected-warning@-4{{UNKNOWN}} #endif } else { clang_analyzer_eval(c1.getX() == 2); #ifdef TEMPORARY_DTORS // expected-warning@-2{{TRUE}} #else // expected-warning@-4{{UNKNOWN}} #endif } C c2 = coin ? C(1) : C(2); if (coin) { clang_analyzer_eval(c2.getX() == 1); // expected-warning{{TRUE}} } else { clang_analyzer_eval(c2.getX() == 2); // expected-warning{{TRUE}} } } } // namespace test_temporary_object_expr namespace test_match_constructors_and_destructors { class C { public: int &x, &y; C(int &_x, int &_y) : x(_x), y(_y) { ++x; } C(const C &c): x(c.x), y(c.y) { ++x; } ~C() { ++y; } }; void test_simple_temporary() { int x = 0, y = 0; { const C &c = C(x, y); } // One constructor and one destructor. clang_analyzer_eval(x == 1); clang_analyzer_eval(y == 1); #ifdef TEMPORARY_DTORS // expected-warning@-3{{TRUE}} // expected-warning@-3{{TRUE}} #else // expected-warning@-6{{UNKNOWN}} // expected-warning@-6{{UNKNOWN}} #endif } void test_simple_temporary_with_copy() { int x = 0, y = 0; { C c = C(x, y); } // Only one constructor directly into the variable, and one destructor. clang_analyzer_eval(x == 1); // expected-warning{{TRUE}} clang_analyzer_eval(y == 1); // expected-warning{{TRUE}} } void test_ternary_temporary(int coin) { int x = 0, y = 0, z = 0, w = 0; { const C &c = coin ? C(x, y) : C(z, w); } // Only one constructor on every branch, and one automatic destructor. if (coin) { clang_analyzer_eval(x == 1); clang_analyzer_eval(y == 1); #ifdef TEMPORARY_DTORS // expected-warning@-3{{TRUE}} // expected-warning@-3{{TRUE}} #else // expected-warning@-6{{UNKNOWN}} // expected-warning@-6{{UNKNOWN}} #endif clang_analyzer_eval(z == 0); // expected-warning{{TRUE}} clang_analyzer_eval(w == 0); // expected-warning{{TRUE}} } else { clang_analyzer_eval(x == 0); // expected-warning{{TRUE}} clang_analyzer_eval(y == 0); // expected-warning{{TRUE}} clang_analyzer_eval(z == 1); clang_analyzer_eval(w == 1); #ifdef TEMPORARY_DTORS // expected-warning@-3{{TRUE}} // expected-warning@-3{{TRUE}} #else // expected-warning@-6{{UNKNOWN}} // expected-warning@-6{{UNKNOWN}} #endif } } void test_ternary_temporary_with_copy(int coin) { int x = 0, y = 0, z = 0, w = 0; { C c = coin ? C(x, y) : C(z, w); } // On each branch the variable is constructed directly. if (coin) { clang_analyzer_eval(x == 1); // expected-warning{{TRUE}} clang_analyzer_eval(y == 1); // expected-warning{{TRUE}} clang_analyzer_eval(z == 0); // expected-warning{{TRUE}} clang_analyzer_eval(w == 0); // expected-warning{{TRUE}} } else { clang_analyzer_eval(x == 0); // expected-warning{{TRUE}} clang_analyzer_eval(y == 0); // expected-warning{{TRUE}} clang_analyzer_eval(z == 1); // expected-warning{{TRUE}} clang_analyzer_eval(w == 1); // expected-warning{{TRUE}} } } } // 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 elided the constructor/destructor for the temporary clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} } // Should have divided by zero in the destructor. clang_analyzer_warnIfReached(); // no-warning } } // namespace destructors_for_return_values namespace dont_forget_destructor_around_logical_op { int glob; class C { public: ~C() { glob = 1; // FIXME: Why is destructor not inlined in C++17 clang_analyzer_checkInlined(true); #ifdef TEMPORARY_DTORS #if __cplusplus < 201703L // expected-warning@-3{{TRUE}} #endif #endif } }; C get(); bool is(C); void test(int coin) { // Here temporaries are being cleaned up after && is evaluated. There are two // temporaries: the return value of get() and the elidable copy constructor // of that return value into is(). According to the CFG, we need to cleanup // both of them depending on whether the temporary corresponding to the // 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 if (coin) { // FIXME: Why is destructor not inlined in C++17 clang_analyzer_eval(glob); #ifdef TEMPORARY_DTORS #if __cplusplus < 201703L // expected-warning@-3{{TRUE}} #else // expected-warning@-5{{UNKNOWN}} #endif #else // expected-warning@-8{{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 #if __cplusplus >= 201103L namespace temporary_list_crash { class C { public: C() {} ~C() {} }; void test() { std::initializer_list{C(), C()}; // no-crash } } // namespace temporary_list_crash #endif // C++11 namespace implicit_constructor_conversion { struct S { int x; S(int x) : x(x) {} ~S() {} }; class C { int x; public: C(const S &s) : x(s.x) {} ~C() {} int getX() const { return x; } }; void test() { const C &c1 = S(10); clang_analyzer_eval(c1.getX() == 10); #ifdef TEMPORARY_DTORS // expected-warning@-2{{TRUE}} #else // expected-warning@-4{{UNKNOWN}} #endif S s = 20; clang_analyzer_eval(s.x == 20); // expected-warning{{TRUE}} C c2 = s; clang_analyzer_eval(c2.getX() == 20); // expected-warning{{TRUE}} } } // end namespace implicit_constructor_conversion namespace pass_references_through { class C { public: ~C() {} }; const C &foo1(); C &&foo2(); // In these examples the foo() expression has record type, not reference type. // Don't try to figure out how to perform construction of the record here. const C &bar1() { return foo1(); } // no-crash C &&bar2() { return foo2(); } // no-crash } // end namespace pass_references_through namespace arguments { int glob; struct S { int x; S(int x): x(x) {} S(const S &s) : x(s.x) {} ~S() {} S &operator+(S s) { glob = s.x; x += s.x; return *this; } }; class C { public: virtual void bar3(S s) {} }; class D: public C { public: D() {} virtual void bar3(S s) override { glob = s.x; } }; void bar1(S s) { glob = s.x; } // Record-typed calls are a different CFGStmt, let's see if we handle that // as well. S bar2(S s) { glob = s.x; return S(3); } void bar5(int, ...); void foo(void (*bar4)(S)) { bar1(S(1)); clang_analyzer_eval(glob == 1); #ifdef TEMPORARY_DTORS // expected-warning@-2{{TRUE}} #else // expected-warning@-4{{UNKNOWN}} #endif bar2(S(2)); clang_analyzer_eval(glob == 2); #ifdef TEMPORARY_DTORS // expected-warning@-2{{TRUE}} #else // expected-warning@-4{{UNKNOWN}} #endif C *c = new D(); c->bar3(S(3)); // FIXME: Should be TRUE. clang_analyzer_eval(glob == 3); // expected-warning{{UNKNOWN}} delete c; // What if we've no idea what we're calling? bar4(S(4)); // no-crash S(5) + S(6); clang_analyzer_eval(glob == 6); #ifdef TEMPORARY_DTORS // expected-warning@-2{{TRUE}} #else // expected-warning@-4{{UNKNOWN}} #endif // Variadic functions. This will __builtin_trap() because you cannot pass // an object as a variadic argument. bar5(7, S(7)); // no-crash clang_analyzer_warnIfReached(); // no-warning } } // namespace arguments namespace ctor_argument { // Stripped down unique_ptr struct IntPtr { IntPtr(): i(new int) {} IntPtr(IntPtr &&o): i(o.i) { o.i = 0; } ~IntPtr() { delete i; } int *i; }; struct Foo { Foo(IntPtr); void bar(); IntPtr i; }; void bar() { IntPtr ptr; int *i = ptr.i; Foo f(static_cast(ptr)); *i = 99; // no-warning } } // namespace ctor_argument namespace operator_implicit_argument { struct S { bool x; S(bool x): x(x) {} operator bool() const { return x; } }; void foo() { if (S(false)) { clang_analyzer_warnIfReached(); // no-warning } if (S(true)) { clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} } } } // namespace operator_implicit_argument #if __cplusplus >= 201103L namespace argument_lazy_bindings { int glob; struct S { int x, y, z; }; struct T { S s; int w; T(int w): s{5, 6, 7}, w(w) {} }; void foo(T t) { t.s = {1, 2, 3}; glob = t.w; } void bar() { foo(T(4)); clang_analyzer_eval(glob == 4); // expected-warning{{TRUE}} } } // namespace argument_lazy_bindings #endif namespace operator_argument_cleanup { struct S { S(); }; class C { public: void operator=(S); }; void foo() { C c; c = S(); // no-crash } } // namespace operator_argument_cleanup namespace argument_decl_lookup { class C {}; int foo(C); int bar(C c) { foo(c); } int foo(C c) {} } // namespace argument_decl_lookup namespace argument_virtual_decl_lookup { class C {}; struct T { virtual void foo(C); }; void run() { T *t; t->foo(C()); // no-crash // expected-warning{{Called C++ object pointer is uninitialized}} } // This is after run() because the test is about picking the correct decl // for the parameter region, which should belong to the correct function decl, // and the non-definition decl should be found by direct lookup. void T::foo(C) {} } // namespace argument_virtual_decl_lookup namespace union_indirect_field_crash { union U { struct { int x; }; }; template class C { public: void foo() const { (void)(true ? U().x : 0); } }; void test() { C c; 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