// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++11 -verify -analyzer-config eagerly-assume=false %s // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 -verify -analyzer-config eagerly-assume=false %s // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++11 -analyzer-config elide-constructors=false -DNO_ELIDE_FLAG -verify -analyzer-config eagerly-assume=false %s // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 -analyzer-config elide-constructors=false -DNO_ELIDE_FLAG -verify -analyzer-config eagerly-assume=false %s // Copy elision always occurs in C++17, otherwise it's under // an on-by-default flag. #if __cplusplus >= 201703L #define ELIDE 1 #else #ifndef NO_ELIDE_FLAG #define ELIDE 1 #endif #endif void clang_analyzer_eval(bool); namespace variable_functional_cast_crash { struct A { A(int) {} }; void foo() { A a = A(0); } struct B { A a; B(): a(A(0)) {} }; } // namespace variable_functional_cast_crash namespace ctor_initializer { struct S { int x, y, z; }; struct T { S s; int w; T(int w): s(), w(w) {} }; class C { T t; public: C() : t(T(4)) { S s = {1, 2, 3}; t.s = s; // FIXME: Should be TRUE regardless of copy elision. clang_analyzer_eval(t.w == 4); #ifdef ELIDE // expected-warning@-2{{TRUE}} #else // expected-warning@-4{{UNKNOWN}} #endif } }; struct A { int x; A(): x(0) {} ~A() {} }; struct B { A a; B() : a(A()) {} }; void foo() { B b; clang_analyzer_eval(b.a.x == 0); // expected-warning{{TRUE}} } } // namespace ctor_initializer namespace elision_on_ternary_op_branches { class C1 { int x; public: C1(int x): x(x) {} int getX() const { return x; } ~C1(); }; class C2 { int x; int y; public: C2(int x, int y): x(x), y(y) {} int getX() const { return x; } int getY() const { return y; } ~C2(); }; void foo(int coin) { C1 c1 = coin ? C1(1) : C1(2); if (coin) { clang_analyzer_eval(c1.getX() == 1); // expected-warning{{TRUE}} } else { clang_analyzer_eval(c1.getX() == 2); // expected-warning{{TRUE}} } C2 c2 = coin ? C2(3, 4) : C2(5, 6); if (coin) { clang_analyzer_eval(c2.getX() == 3); // expected-warning{{TRUE}} clang_analyzer_eval(c2.getY() == 4); // expected-warning{{TRUE}} } else { clang_analyzer_eval(c2.getX() == 5); // expected-warning{{TRUE}} clang_analyzer_eval(c2.getY() == 6); // expected-warning{{TRUE}} } } } // namespace elision_on_ternary_op_branches namespace address_vector_tests { template struct AddressVector { T *buf[20]; int len; AddressVector() : len(0) {} void push(T *t) { buf[len] = t; ++len; } }; class ClassWithoutDestructor { AddressVector &v; public: ClassWithoutDestructor(AddressVector &v) : v(v) { push(); } ClassWithoutDestructor(ClassWithoutDestructor &&c) : v(c.v) { push(); } ClassWithoutDestructor(const ClassWithoutDestructor &c) : v(c.v) { push(); } void push() { v.push(this); } }; ClassWithoutDestructor make1(AddressVector &v) { return ClassWithoutDestructor(v); } ClassWithoutDestructor make2(AddressVector &v) { return make1(v); } ClassWithoutDestructor make3(AddressVector &v) { return make2(v); } void testMultipleReturns() { AddressVector v; ClassWithoutDestructor c = make3(v); #if ELIDE clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[0] == &c); // expected-warning{{TRUE}} #else clang_analyzer_eval(v.len == 5); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[0] != v.buf[1]); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[1] != v.buf[2]); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[2] != v.buf[3]); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[3] != v.buf[4]); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[4] == &c); // expected-warning{{TRUE}} #endif } void consume(ClassWithoutDestructor c) { c.push(); } void testArgumentConstructorWithoutDestructor() { AddressVector v; consume(make3(v)); #if ELIDE clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}} #else clang_analyzer_eval(v.len == 6); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[0] != v.buf[1]); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[1] != v.buf[2]); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[2] != v.buf[3]); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[3] != v.buf[4]); // expected-warning{{TRUE}} // We forced a push() in consume(), let's see if the address here matches // the address during construction. clang_analyzer_eval(v.buf[4] == v.buf[5]); // expected-warning{{TRUE}} #endif } class ClassWithDestructor { AddressVector &v; public: ClassWithDestructor(AddressVector &v) : v(v) { push(); } ClassWithDestructor(ClassWithDestructor &&c) : v(c.v) { push(); } ClassWithDestructor(const ClassWithDestructor &c) : v(c.v) { push(); } ~ClassWithDestructor() { push(); } void push() { v.push(this); } }; void testVariable() { AddressVector v; { ClassWithDestructor c = ClassWithDestructor(v); // Check if the last destructor is an automatic destructor. // A temporary destructor would have fired by now. #if ELIDE clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}} #else clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}} #endif } #if ELIDE // 0. Construct the variable. // 1. Destroy the variable. clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}} #else // 0. Construct the temporary. // 1. Construct the variable. // 2. Destroy the temporary. // 3. Destroy the variable. clang_analyzer_eval(v.len == 4); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[1] == v.buf[3]); // expected-warning{{TRUE}} #endif } struct TestCtorInitializer { ClassWithDestructor c; TestCtorInitializer(AddressVector &v) : c(ClassWithDestructor(v)) {} }; void testCtorInitializer() { AddressVector v; { TestCtorInitializer t(v); // Check if the last destructor is an automatic destructor. // A temporary destructor would have fired by now. #if ELIDE clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}} #else clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}} #endif } #if ELIDE // 0. Construct the member variable. // 1. Destroy the member variable. clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}} #else // 0. Construct the temporary. // 1. Construct the member variable. // 2. Destroy the temporary. // 3. Destroy the member variable. clang_analyzer_eval(v.len == 4); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[1] == v.buf[3]); // expected-warning{{TRUE}} #endif } ClassWithDestructor make1(AddressVector &v) { return ClassWithDestructor(v); } ClassWithDestructor make2(AddressVector &v) { return make1(v); } ClassWithDestructor make3(AddressVector &v) { return make2(v); } void testMultipleReturnsWithDestructors() { AddressVector v; { ClassWithDestructor c = make3(v); // Check if the last destructor is an automatic destructor. // A temporary destructor would have fired by now. #if ELIDE clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}} #else clang_analyzer_eval(v.len == 9); // expected-warning{{TRUE}} #endif } #if ELIDE // 0. Construct the variable. Yes, constructor in make1() constructs // the variable 'c'. // 1. Destroy the variable. clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}} #else // 0. Construct the temporary in make1(). // 1. Construct the temporary in make2(). // 2. Destroy the temporary in make1(). // 3. Construct the temporary in make3(). // 4. Destroy the temporary in make2(). // 5. Construct the temporary here. // 6. Destroy the temporary in make3(). // 7. Construct the variable. // 8. Destroy the temporary here. // 9. Destroy the variable. clang_analyzer_eval(v.len == 10); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[1] == v.buf[4]); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[3] == v.buf[6]); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[5] == v.buf[8]); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[7] == v.buf[9]); // expected-warning{{TRUE}} #endif } void consume(ClassWithDestructor c) { c.push(); } void testArgumentConstructorWithDestructor() { AddressVector v; consume(make3(v)); #if ELIDE // 0. Construct the argument. // 1. Forced push() in consume(). // 2. Destroy the argument. clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[1] == v.buf[2]); // expected-warning{{TRUE}} #else // 0. Construct the temporary in make1(). // 1. Construct the temporary in make2(). // 2. Destroy the temporary in make1(). // 3. Construct the temporary in make3(). // 4. Destroy the temporary in make2(). // 5. Construct the temporary here. // 6. Destroy the temporary in make3(). // 7. Construct the argument. // 8. Forced push() in consume(). // 9. Destroy the argument. Notice the reverse order! // 10. Destroy the temporary here. clang_analyzer_eval(v.len == 11); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[1] == v.buf[4]); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[3] == v.buf[6]); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[5] == v.buf[10]); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[7] == v.buf[8]); // expected-warning{{TRUE}} clang_analyzer_eval(v.buf[8] == v.buf[9]); // expected-warning{{TRUE}} #endif } } // namespace address_vector_tests