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