461 lines · cpp
1// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++11 \2// RUN: -analyzer-config eagerly-assume=false -verify %s3// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 \4// RUN: -analyzer-config eagerly-assume=false -verify %s5// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++11 \6// RUN: -analyzer-config elide-constructors=false -DNO_ELIDE_FLAG \7// RUN: -analyzer-config eagerly-assume=false -verify=expected,no-elide %s8// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 \9// RUN: -analyzer-config elide-constructors=false \10// RUN: -analyzer-config eagerly-assume=false -verify %s11 12// Copy elision always occurs in C++17, otherwise it's under13// an on-by-default flag.14#if __cplusplus >= 201703L15 #define ELIDE 116#else17 #ifndef NO_ELIDE_FLAG18 #define ELIDE 119 #endif20#endif21 22void clang_analyzer_eval(bool);23void clang_analyzer_dump(int);24 25namespace variable_functional_cast_crash {26 27struct A {28 A(int) {}29};30 31void foo() {32 A a = A(0);33}34 35struct B {36 A a;37 B(): a(A(0)) {}38};39 40} // namespace variable_functional_cast_crash41 42 43namespace ctor_initializer {44 45struct S {46 int x, y, z;47};48 49struct T {50 S s;51 int w;52 T(int w): s(), w(w) {}53};54 55class C {56 T t;57public:58 C() : t(T(4)) {59 S s = {1, 2, 3};60 t.s = s;61 // FIXME: Should be TRUE regardless of copy elision.62 clang_analyzer_eval(t.w == 4);63#ifdef ELIDE64 // expected-warning@-2{{TRUE}}65#else66 // expected-warning@-4{{UNKNOWN}}67#endif68 }69};70 71 72struct A {73 int x;74 A(): x(0) {}75 ~A() {}76};77 78struct B {79 A a;80 B() : a(A()) {}81};82 83void foo() {84 B b;85 clang_analyzer_eval(b.a.x == 0); // expected-warning{{TRUE}}86}87 88} // namespace ctor_initializer89 90 91namespace elision_on_ternary_op_branches {92class C1 {93 int x;94public:95 C1(int x): x(x) {}96 int getX() const { return x; }97 ~C1();98};99 100class C2 {101 int x;102 int y;103public:104 C2(int x, int y): x(x), y(y) {}105 int getX() const { return x; }106 int getY() const { return y; }107 ~C2();108};109 110void foo(int coin) {111 C1 c1 = coin ? C1(1) : C1(2);112 if (coin) {113 clang_analyzer_eval(c1.getX() == 1); // expected-warning{{TRUE}}114 } else {115 clang_analyzer_eval(c1.getX() == 2); // expected-warning{{TRUE}}116 }117 C2 c2 = coin ? C2(3, 4) : C2(5, 6);118 if (coin) {119 clang_analyzer_eval(c2.getX() == 3); // expected-warning{{TRUE}}120 clang_analyzer_eval(c2.getY() == 4); // expected-warning{{TRUE}}121 } else {122 clang_analyzer_eval(c2.getX() == 5); // expected-warning{{TRUE}}123 clang_analyzer_eval(c2.getY() == 6); // expected-warning{{TRUE}}124 }125}126} // namespace elision_on_ternary_op_branches127 128 129namespace address_vector_tests {130 131template <typename T> struct AddressVector {132 T *buf[20];133 int len;134 135 AddressVector() : len(0) {}136 137 void push(T *t) {138 buf[len] = t;139 ++len;140 }141};142 143class ClassWithoutDestructor {144 AddressVector<ClassWithoutDestructor> &v;145 146public:147 ClassWithoutDestructor(AddressVector<ClassWithoutDestructor> &v) : v(v) {148 push();149 }150 151 ClassWithoutDestructor(ClassWithoutDestructor &&c) : v(c.v) { push(); }152 ClassWithoutDestructor(const ClassWithoutDestructor &c) : v(c.v) { push(); }153 154 void push() { v.push(this); }155};156 157// Two warnings on no-elide: arg v holds the address of the temporary, and we158// are returning an object which holds v which holds the address of the temporary 159ClassWithoutDestructor make1(AddressVector<ClassWithoutDestructor> &v) {160 return ClassWithoutDestructor(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithoutDestructor' returned to caller}}161 // no-elide-warning@-1 {{Address of stack memory associated with temporary \162object of type 'ClassWithoutDestructor' is still \163referred to by the caller variable 'v' upon returning to the caller}}164}165// Two warnings on no-elide: arg v holds the address of the temporary, and we166// are returning an object which holds v which holds the address of the temporary 167ClassWithoutDestructor make2(AddressVector<ClassWithoutDestructor> &v) {168 return make1(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithoutDestructor' returned to caller}}169 // no-elide-warning@-1 {{Address of stack memory associated with temporary \170object of type 'ClassWithoutDestructor' is still \171referred to by the caller variable 'v' upon returning to the caller}}172}173// Two warnings on no-elide: arg v holds the address of the temporary, and we174// are returning an object which holds v which holds the address of the temporary 175ClassWithoutDestructor make3(AddressVector<ClassWithoutDestructor> &v) {176 return make2(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithoutDestructor' returned to caller}}177 // no-elide-warning@-1 {{Address of stack memory associated with temporary \178object of type 'ClassWithoutDestructor' is still \179referred to by the caller variable 'v' upon returning to the caller}}180}181 182void testMultipleReturns() {183 AddressVector<ClassWithoutDestructor> v;184 ClassWithoutDestructor c = make3(v);185 186#if ELIDE187 clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}188 clang_analyzer_eval(v.buf[0] == &c); // expected-warning{{TRUE}}189#else190 clang_analyzer_eval(v.len == 5); // expected-warning{{TRUE}}191 clang_analyzer_eval(v.buf[0] != v.buf[1]); // expected-warning{{TRUE}}192 clang_analyzer_eval(v.buf[1] != v.buf[2]); // expected-warning{{TRUE}}193 clang_analyzer_eval(v.buf[2] != v.buf[3]); // expected-warning{{TRUE}}194 clang_analyzer_eval(v.buf[3] != v.buf[4]); // expected-warning{{TRUE}}195 clang_analyzer_eval(v.buf[4] == &c); // expected-warning{{TRUE}}196#endif197}198 199void consume(ClassWithoutDestructor c) {200 c.push();201 // expected-warning@-1 {{Address of stack memory associated with local \202variable 'c' is still referred to by the caller variable 'v' upon returning \203to the caller}}204}205 206void testArgumentConstructorWithoutDestructor() {207 AddressVector<ClassWithoutDestructor> v;208 209 consume(make3(v));210 211#if ELIDE212 clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}213 clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}214#else215 clang_analyzer_eval(v.len == 6); // expected-warning{{TRUE}}216 clang_analyzer_eval(v.buf[0] != v.buf[1]); // expected-warning{{TRUE}}217 clang_analyzer_eval(v.buf[1] != v.buf[2]); // expected-warning{{TRUE}}218 clang_analyzer_eval(v.buf[2] != v.buf[3]); // expected-warning{{TRUE}}219 clang_analyzer_eval(v.buf[3] != v.buf[4]); // expected-warning{{TRUE}}220 // We forced a push() in consume(), let's see if the address here matches221 // the address during construction.222 clang_analyzer_eval(v.buf[4] == v.buf[5]); // expected-warning{{TRUE}}223#endif224}225 226class ClassWithDestructor {227 AddressVector<ClassWithDestructor> &v;228 229public:230 ClassWithDestructor(AddressVector<ClassWithDestructor> &v) : v(v) {231 push();232 }233 234 ClassWithDestructor(ClassWithDestructor &&c) : v(c.v) { push(); }235 ClassWithDestructor(const ClassWithDestructor &c) : v(c.v) { push(); }236 237 ~ClassWithDestructor() { push(); }238 239 void push() { v.push(this); }240};241 242void testVariable() {243 AddressVector<ClassWithDestructor> v;244 {245 ClassWithDestructor c = ClassWithDestructor(v);246 // Check if the last destructor is an automatic destructor.247 // A temporary destructor would have fired by now.248#if ELIDE249 clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}250#else251 clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}252#endif253 }254#if ELIDE255 // 0. Construct the variable.256 // 1. Destroy the variable.257 clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}258 clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}259#else260 // 0. Construct the temporary.261 // 1. Construct the variable.262 // 2. Destroy the temporary.263 // 3. Destroy the variable.264 clang_analyzer_eval(v.len == 4); // expected-warning{{TRUE}}265 clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}266 clang_analyzer_eval(v.buf[1] == v.buf[3]); // expected-warning{{TRUE}}267#endif268}269 270struct TestCtorInitializer {271 ClassWithDestructor c;272 TestCtorInitializer(AddressVector<ClassWithDestructor> &refParam)273 : c(ClassWithDestructor(refParam)) {}274};275 276void testCtorInitializer() {277 AddressVector<ClassWithDestructor> v;278 {279 TestCtorInitializer t(v);280 // no-elide-warning@-1 {{Address of stack memory associated with temporary \281object of type 'ClassWithDestructor' is still referred \282to by the caller variable 'v' upon returning to the caller}}283 // Check if the last destructor is an automatic destructor.284 // A temporary destructor would have fired by now.285#if ELIDE286 clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}287#else288 clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}289#endif290 }291#if ELIDE292 // 0. Construct the member variable.293 // 1. Destroy the member variable.294 clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}295 clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}296#else297 // 0. Construct the temporary.298 // 1. Construct the member variable.299 // 2. Destroy the temporary.300 // 3. Destroy the member variable.301 clang_analyzer_eval(v.len == 4); // expected-warning{{TRUE}}302 clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}303 clang_analyzer_eval(v.buf[1] == v.buf[3]); // expected-warning{{TRUE}}304#endif305}306 307// Two warnings on no-elide: arg v holds the address of the temporary, and we308// are returning an object which holds v which holds the address of the temporary309ClassWithDestructor make1(AddressVector<ClassWithDestructor> &v) {310 return ClassWithDestructor(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithDestructor' returned to caller}}311 // no-elide-warning@-1 {{Address of stack memory associated with temporary \312object of type 'ClassWithDestructor' is still referred \313to by the caller variable 'v' upon returning to the caller}}314}315// Two warnings on no-elide: arg v holds the address of the temporary, and we316// are returning an object which holds v which holds the address of the temporary317ClassWithDestructor make2(AddressVector<ClassWithDestructor> &v) {318 return make1(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithDestructor' returned to caller}}319 // no-elide-warning@-1 {{Address of stack memory associated with temporary \320object of type 'ClassWithDestructor' is still referred \321to by the caller variable 'v' upon returning to the caller}}322}323// Two warnings on no-elide: arg v holds the address of the temporary, and we324// are returning an object which holds v which holds the address of the temporary325ClassWithDestructor make3(AddressVector<ClassWithDestructor> &v) {326 return make2(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithDestructor' returned to caller}}327 // no-elide-warning@-1 {{Address of stack memory associated with temporary \328object of type 'ClassWithDestructor' is still referred \329to by the caller variable 'v' upon returning to the caller}}330}331 332void testMultipleReturnsWithDestructors() {333 AddressVector<ClassWithDestructor> v;334 {335 ClassWithDestructor c = make3(v);336 // Check if the last destructor is an automatic destructor.337 // A temporary destructor would have fired by now.338#if ELIDE339 clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}340#else341 clang_analyzer_eval(v.len == 9); // expected-warning{{TRUE}}342#endif343 }344 345#if ELIDE346 // 0. Construct the variable. Yes, constructor in make1() constructs347 // the variable 'c'.348 // 1. Destroy the variable.349 clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}350 clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}351#else352 // 0. Construct the temporary in make1().353 // 1. Construct the temporary in make2().354 // 2. Destroy the temporary in make1().355 // 3. Construct the temporary in make3().356 // 4. Destroy the temporary in make2().357 // 5. Construct the temporary here.358 // 6. Destroy the temporary in make3().359 // 7. Construct the variable.360 // 8. Destroy the temporary here.361 // 9. Destroy the variable.362 clang_analyzer_eval(v.len == 10); // expected-warning{{TRUE}}363 clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}364 clang_analyzer_eval(v.buf[1] == v.buf[4]); // expected-warning{{TRUE}}365 clang_analyzer_eval(v.buf[3] == v.buf[6]); // expected-warning{{TRUE}}366 clang_analyzer_eval(v.buf[5] == v.buf[8]); // expected-warning{{TRUE}}367 clang_analyzer_eval(v.buf[7] == v.buf[9]); // expected-warning{{TRUE}}368#endif369}370 371void consume(ClassWithDestructor c) {372 c.push();373 // expected-warning@-1 {{Address of stack memory associated with local \374variable 'c' is still referred to by the caller variable 'v' upon returning \375to the caller}}376}377 378void testArgumentConstructorWithDestructor() {379 AddressVector<ClassWithDestructor> v;380 381 consume(make3(v));382 383#if ELIDE384 // 0. Construct the argument.385 // 1. Forced push() in consume().386 // 2. Destroy the argument.387 clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}388 clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}389 clang_analyzer_eval(v.buf[1] == v.buf[2]); // expected-warning{{TRUE}}390#else391 // 0. Construct the temporary in make1().392 // 1. Construct the temporary in make2().393 // 2. Destroy the temporary in make1().394 // 3. Construct the temporary in make3().395 // 4. Destroy the temporary in make2().396 // 5. Construct the temporary here.397 // 6. Destroy the temporary in make3().398 // 7. Construct the argument.399 // 8. Forced push() in consume().400 // 9. Destroy the argument. Notice the reverse order!401 // 10. Destroy the temporary here.402 clang_analyzer_eval(v.len == 11); // expected-warning{{TRUE}}403 clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}404 clang_analyzer_eval(v.buf[1] == v.buf[4]); // expected-warning{{TRUE}}405 clang_analyzer_eval(v.buf[3] == v.buf[6]); // expected-warning{{TRUE}}406 clang_analyzer_eval(v.buf[5] == v.buf[10]); // expected-warning{{TRUE}}407 clang_analyzer_eval(v.buf[7] == v.buf[8]); // expected-warning{{TRUE}}408 clang_analyzer_eval(v.buf[8] == v.buf[9]); // expected-warning{{TRUE}}409#endif410}411 412struct Foo {413 Foo(Foo **q) {414 *q = this;415 }416};417 418Foo make1(Foo **r) {419 return Foo(r);420 // no-elide-warning@-1 {{Address of stack memory associated with temporary \421object of type 'Foo' is still referred to by the caller \422variable 'z' upon returning to the caller}}423}424 425void test_copy_elision() {426 Foo *z;427 // If the copy elided, 'z' points to 'tmp', otherwise it's a dangling pointer.428 Foo tmp = make1(&z);429 (void)tmp;430}431 432} // namespace address_vector_tests433 434namespace arg_directly_from_return_in_loop {435 436struct Result {437 int value;438};439 440Result create() {441 return Result{10};442}443 444int accessValue(Result r) {445 return r.value;446}447 448void test() {449 for (int i = 0; i < 3; ++i) {450 int v = accessValue(create());451 if (i == 0) {452 clang_analyzer_dump(v); // expected-warning {{10 S32b}}453 } else {454 clang_analyzer_dump(v); // expected-warning {{10 S32b}}455 // was {{reg_${{[0-9]+}}<int r.value> }} for C++11456 }457 }458}459 460} // namespace arg_directly_from_return_in_loop461