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