1 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify %s 2 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true -DTEMPORARIES -verify %s 3 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++17 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true -DTEMPORARIES %s 4 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -DMOVES -verify %s 5 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true -DTEMPORARIES -DMOVES -verify %s 6 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++17 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true -DTEMPORARIES -DMOVES %s 7 8 // Note: The C++17 run-lines don't -verify yet - it is a no-crash test. 9 10 void clang_analyzer_eval(bool); 11 void clang_analyzer_checkInlined(bool); 12 13 namespace pr17001_call_wrong_destructor { 14 bool x; 15 struct A { 16 int *a; 17 A() {} 18 ~A() {} 19 }; 20 struct B : public A { 21 B() {} 22 ~B() { x = true; } 23 }; 24 25 void f() { 26 { 27 const A &a = B(); 28 } 29 clang_analyzer_eval(x); // expected-warning{{TRUE}} 30 } 31 } // end namespace pr17001_call_wrong_destructor 32 33 namespace pr19539_crash_on_destroying_an_integer { 34 struct A { 35 int i; 36 int j[2]; 37 A() : i(1) { 38 j[0] = 2; 39 j[1] = 3; 40 } 41 ~A() {} 42 }; 43 44 void f() { 45 const int &x = A().i; // no-crash 46 const int &y = A().j[1]; // no-crash 47 const int &z = (A().j[1], A().j[0]); // no-crash 48 49 clang_analyzer_eval(x == 1); 50 clang_analyzer_eval(y == 3); 51 clang_analyzer_eval(z == 2); 52 #ifdef TEMPORARIES 53 // expected-warning@-4{{TRUE}} 54 // expected-warning@-4{{TRUE}} 55 // expected-warning@-4{{TRUE}} 56 #else 57 // expected-warning@-8{{UNKNOWN}} 58 // expected-warning@-8{{UNKNOWN}} 59 // expected-warning@-8{{UNKNOWN}} 60 #endif 61 } 62 } // end namespace pr19539_crash_on_destroying_an_integer 63 64 namespace maintain_original_object_address_on_lifetime_extension { 65 class C { 66 C **after, **before; 67 68 public: 69 bool x; 70 71 C(bool x, C **after, C **before) : x(x), after(after), before(before) { 72 *before = this; 73 } 74 75 // Don't track copies in our tests. 76 C(const C &c) : x(c.x), after(nullptr), before(nullptr) {} 77 78 ~C() { if (after) *after = this; } 79 80 operator bool() const { return x; } 81 82 static C make(C **after, C **before) { return C(false, after, before); } 83 }; 84 85 void f1() { 86 C *after, *before; 87 { 88 const C &c = C(true, &after, &before); 89 } 90 clang_analyzer_eval(after == before); 91 #ifdef TEMPORARIES 92 // expected-warning@-2{{TRUE}} 93 #else 94 // expected-warning@-4{{UNKNOWN}} 95 #endif 96 } 97 98 void f2() { 99 C *after, *before; 100 C c = C(1, &after, &before); 101 clang_analyzer_eval(after == before); 102 #ifdef TEMPORARIES 103 // expected-warning@-2{{TRUE}} 104 #else 105 // expected-warning@-4{{UNKNOWN}} 106 #endif 107 } 108 109 void f3(bool coin) { 110 C *after, *before; 111 { 112 const C &c = coin ? C(true, &after, &before) : C(false, &after, &before); 113 } 114 clang_analyzer_eval(after == before); 115 #ifdef TEMPORARIES 116 // expected-warning@-2{{TRUE}} 117 #else 118 // expected-warning@-4{{UNKNOWN}} 119 #endif 120 } 121 122 void f4(bool coin) { 123 C *after, *before; 124 { 125 // no-crash 126 const C &c = C(coin, &after, &before) ?: C(false, &after, &before); 127 } 128 // FIXME: Add support for lifetime extension through binary conditional 129 // operator. Ideally also add support for the binary conditional operator in 130 // C++. Because for now it calls the constructor for the condition twice. 131 if (coin) { 132 clang_analyzer_eval(after == before); 133 #ifdef TEMPORARIES 134 // expected-warning@-2{{The left operand of '==' is a garbage value}} 135 #else 136 // expected-warning@-4{{UNKNOWN}} 137 #endif 138 } else { 139 clang_analyzer_eval(after == before); 140 #ifdef TEMPORARIES 141 // Seems to work at the moment, but also seems accidental. 142 // Feel free to break. 143 // expected-warning@-4{{TRUE}} 144 #else 145 // expected-warning@-6{{UNKNOWN}} 146 #endif 147 } 148 } 149 150 void f5() { 151 C *after, *before; 152 { 153 const bool &x = C(true, &after, &before).x; // no-crash 154 } 155 clang_analyzer_eval(after == before); 156 #ifdef TEMPORARIES 157 // expected-warning@-2{{TRUE}} 158 #else 159 // expected-warning@-4{{UNKNOWN}} 160 #endif 161 } 162 163 struct A { // A is an aggregate. 164 const C &c; 165 }; 166 167 void f6() { 168 C *after, *before; 169 { 170 A a{C(true, &after, &before)}; 171 } 172 // FIXME: Should be TRUE. Should not warn about garbage value. 173 clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}} 174 } 175 176 void f7() { 177 C *after, *before; 178 { 179 A a = {C(true, &after, &before)}; 180 } 181 // FIXME: Should be TRUE. Should not warn about garbage value. 182 clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}} 183 } 184 185 void f8() { 186 C *after, *before; 187 { 188 A a[2] = {C(false, nullptr, nullptr), C(true, &after, &before)}; 189 } 190 // FIXME: Should be TRUE. Should not warn about garbage value. 191 clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}} 192 } 193 } // end namespace maintain_original_object_address_on_lifetime_extension 194 195 namespace maintain_original_object_address_on_move { 196 class C { 197 int *x; 198 199 public: 200 C() : x(nullptr) {} 201 C(int *x) : x(x) {} 202 C(const C &c) = delete; 203 C(C &&c) : x(c.x) { c.x = nullptr; } 204 C &operator=(C &&c) { 205 x = c.x; 206 c.x = nullptr; 207 return *this; 208 } 209 ~C() { 210 // This was triggering the division by zero warning in f1() and f2(): 211 // Because move-elision materialization was incorrectly causing the object 212 // to be relocated from one address to another before move, but destructor 213 // was operating on the old address, it was still thinking that 'x' is set. 214 if (x) 215 *x = 0; 216 } 217 }; 218 219 void f1() { 220 int x = 1; 221 // &x is replaced with nullptr in move-constructor before the temporary dies. 222 C c = C(&x); 223 // Hence x was not set to 0 yet. 224 1 / x; // no-warning 225 } 226 void f2() { 227 int x = 1; 228 C c; 229 // &x is replaced with nullptr in move-assignment before the temporary dies. 230 c = C(&x); 231 // Hence x was not set to 0 yet. 232 1 / x; // no-warning 233 } 234 } // end namespace maintain_original_object_address_on_move 235 236 namespace maintain_address_of_copies { 237 class C; 238 239 struct AddressVector { 240 C *buf[10]; 241 int len; 242 243 AddressVector() : len(0) {} 244 245 void push(C *c) { 246 buf[len] = c; 247 ++len; 248 } 249 }; 250 251 class C { 252 AddressVector &v; 253 254 public: 255 C(AddressVector &v) : v(v) { v.push(this); } 256 ~C() { v.push(this); } 257 258 #ifdef MOVES 259 C(C &&c) : v(c.v) { v.push(this); } 260 #endif 261 262 // Note how return-statements prefer move-constructors when available. 263 C(const C &c) : v(c.v) { 264 #ifdef MOVES 265 clang_analyzer_checkInlined(false); // no-warning 266 #else 267 v.push(this); 268 #endif 269 } // no-warning 270 271 static C make(AddressVector &v) { return C(v); } 272 }; 273 274 void f1() { 275 AddressVector v; 276 { 277 C c = C(v); 278 } 279 // 0. Create the original temporary and lifetime-extend it into variable 'c' 280 // construction argument. 281 // 1. Construct variable 'c' (elidable copy/move). 282 // 2. Destroy the temporary. 283 // 3. Destroy variable 'c'. 284 clang_analyzer_eval(v.len == 4); 285 clang_analyzer_eval(v.buf[0] == v.buf[2]); 286 clang_analyzer_eval(v.buf[1] == v.buf[3]); 287 #ifdef TEMPORARIES 288 // expected-warning@-4{{TRUE}} 289 // expected-warning@-4{{TRUE}} 290 // expected-warning@-4{{TRUE}} 291 #else 292 // expected-warning@-8{{UNKNOWN}} 293 // expected-warning@-8{{UNKNOWN}} 294 // expected-warning@-8{{UNKNOWN}} 295 #endif 296 } 297 298 void f2() { 299 AddressVector v; 300 { 301 const C &c = C::make(v); 302 } 303 // 0. Construct the original temporary within make(), 304 // 1. Construct the return value of make() (elidable copy/move) and 305 // lifetime-extend it via reference 'c', 306 // 2. Destroy the temporary within make(), 307 // 3. Destroy the temporary lifetime-extended by 'c'. 308 clang_analyzer_eval(v.len == 4); 309 clang_analyzer_eval(v.buf[0] == v.buf[2]); 310 clang_analyzer_eval(v.buf[1] == v.buf[3]); 311 #ifdef TEMPORARIES 312 // expected-warning@-4{{TRUE}} 313 // expected-warning@-4{{TRUE}} 314 // expected-warning@-4{{TRUE}} 315 #else 316 // expected-warning@-8{{UNKNOWN}} 317 // expected-warning@-8{{UNKNOWN}} 318 // expected-warning@-8{{UNKNOWN}} 319 #endif 320 } 321 322 void f3() { 323 AddressVector v; 324 { 325 C &&c = C::make(v); 326 } 327 // 0. Construct the original temporary within make(), 328 // 1. Construct the return value of make() (elidable copy/move) and 329 // lifetime-extend it via reference 'c', 330 // 2. Destroy the temporary within make(), 331 // 3. Destroy the temporary lifetime-extended by 'c'. 332 clang_analyzer_eval(v.len == 4); 333 clang_analyzer_eval(v.buf[0] == v.buf[2]); 334 clang_analyzer_eval(v.buf[1] == v.buf[3]); 335 #ifdef TEMPORARIES 336 // expected-warning@-4{{TRUE}} 337 // expected-warning@-4{{TRUE}} 338 // expected-warning@-4{{TRUE}} 339 #else 340 // expected-warning@-8{{UNKNOWN}} 341 // expected-warning@-8{{UNKNOWN}} 342 // expected-warning@-8{{UNKNOWN}} 343 #endif 344 } 345 346 C doubleMake(AddressVector &v) { 347 return C::make(v); 348 } 349 350 void f4() { 351 AddressVector v; 352 { 353 C c = doubleMake(v); 354 } 355 // 0. Construct the original temporary within make(), 356 // 1. Construct the return value of make() (elidable copy/move) and 357 // lifetime-extend it into the return value constructor argument within 358 // doubleMake(), 359 // 2. Destroy the temporary within make(), 360 // 3. Construct the return value of doubleMake() (elidable copy/move) and 361 // lifetime-extend it into the variable 'c' constructor argument, 362 // 4. Destroy the return value of make(), 363 // 5. Construct variable 'c' (elidable copy/move), 364 // 6. Destroy the return value of doubleMake(), 365 // 7. Destroy variable 'c'. 366 clang_analyzer_eval(v.len == 8); 367 clang_analyzer_eval(v.buf[0] == v.buf[2]); 368 clang_analyzer_eval(v.buf[1] == v.buf[4]); 369 clang_analyzer_eval(v.buf[3] == v.buf[6]); 370 clang_analyzer_eval(v.buf[5] == v.buf[7]); 371 #ifdef TEMPORARIES 372 // expected-warning@-6{{TRUE}} 373 // expected-warning@-6{{TRUE}} 374 // expected-warning@-6{{TRUE}} 375 // expected-warning@-6{{TRUE}} 376 // expected-warning@-6{{TRUE}} 377 #else 378 // expected-warning@-12{{UNKNOWN}} 379 // expected-warning@-12{{UNKNOWN}} 380 // expected-warning@-12{{UNKNOWN}} 381 // expected-warning@-12{{UNKNOWN}} 382 // expected-warning@-12{{UNKNOWN}} 383 #endif 384 } 385 } // end namespace maintain_address_of_copies 386