diff options
Diffstat (limited to 'test/Analysis/copy-elision.cpp')
| -rw-r--r-- | test/Analysis/copy-elision.cpp | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/test/Analysis/copy-elision.cpp b/test/Analysis/copy-elision.cpp new file mode 100644 index 000000000000..cf77912ea6c4 --- /dev/null +++ b/test/Analysis/copy-elision.cpp @@ -0,0 +1,304 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++11 -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++11 -analyzer-config elide-constructors=false -DNO_ELIDE_FLAG -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 -analyzer-config elide-constructors=false -DNO_ELIDE_FLAG -verify %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 <typename T> struct AddressVector { + T *buf[10]; + int len; + + AddressVector() : len(0) {} + + void push(T *t) { + buf[len] = t; + ++len; + } +}; + +class ClassWithoutDestructor { + AddressVector<ClassWithoutDestructor> &v; + +public: + ClassWithoutDestructor(AddressVector<ClassWithoutDestructor> &v) : v(v) { + v.push(this); + } + + ClassWithoutDestructor(ClassWithoutDestructor &&c) : v(c.v) { v.push(this); } + ClassWithoutDestructor(const ClassWithoutDestructor &c) : v(c.v) { + v.push(this); + } +}; + +ClassWithoutDestructor make1(AddressVector<ClassWithoutDestructor> &v) { + return ClassWithoutDestructor(v); +} +ClassWithoutDestructor make2(AddressVector<ClassWithoutDestructor> &v) { + return make1(v); +} +ClassWithoutDestructor make3(AddressVector<ClassWithoutDestructor> &v) { + return make2(v); +} + +void testMultipleReturns() { + AddressVector<ClassWithoutDestructor> 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 +} + +class ClassWithDestructor { + AddressVector<ClassWithDestructor> &v; + +public: + ClassWithDestructor(AddressVector<ClassWithDestructor> &v) : v(v) { + v.push(this); + } + + ClassWithDestructor(ClassWithDestructor &&c) : v(c.v) { v.push(this); } + ClassWithDestructor(const ClassWithDestructor &c) : v(c.v) { + v.push(this); + } + + ~ClassWithDestructor() { v.push(this); } +}; + +void testVariable() { + AddressVector<ClassWithDestructor> 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<ClassWithDestructor> &v) + : c(ClassWithDestructor(v)) {} +}; + +void testCtorInitializer() { + AddressVector<ClassWithDestructor> 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<ClassWithDestructor> &v) { + return ClassWithDestructor(v); +} +ClassWithDestructor make2(AddressVector<ClassWithDestructor> &v) { + return make1(v); +} +ClassWithDestructor make3(AddressVector<ClassWithDestructor> &v) { + return make2(v); +} + +void testMultipleReturnsWithDestructors() { + AddressVector<ClassWithDestructor> 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 +} +} // namespace address_vector_tests |
