1 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify -analyzer-config eagerly-assume=false %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 -analyzer-config eagerly-assume=false %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 -analyzer-config eagerly-assume=false %s 4 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -DMOVES -verify -analyzer-config eagerly-assume=false %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 -analyzer-config eagerly-assume=false %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 -analyzer-config eagerly-assume=false %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 { 101 C c = C(1, &after, &before); 102 } 103 clang_analyzer_eval(after == before); // expected-warning{{TRUE}} 104 } 105 106 void f3(bool coin) { 107 C *after, *before; 108 { 109 const C &c = coin ? C(true, &after, &before) : C(false, &after, &before); 110 } 111 clang_analyzer_eval(after == before); 112 #ifdef TEMPORARIES 113 // expected-warning@-2{{TRUE}} 114 #else 115 // expected-warning@-4{{UNKNOWN}} 116 #endif 117 } 118 119 void f4(bool coin) { 120 C *after, *before; 121 { 122 // no-crash 123 const C &c = C(coin, &after, &before) ?: C(false, &after, &before); 124 } 125 // FIXME: Add support for lifetime extension through binary conditional 126 // operator. Ideally also add support for the binary conditional operator in 127 // C++. Because for now it calls the constructor for the condition twice. 128 if (coin) { 129 // FIXME: Should not warn. 130 clang_analyzer_eval(after == before); 131 #ifdef TEMPORARIES 132 // expected-warning@-2{{The left operand of '==' is a garbage value}} 133 #else 134 // expected-warning@-4{{UNKNOWN}} 135 #endif 136 } else { 137 // FIXME: Should be TRUE. 138 clang_analyzer_eval(after == before); 139 #ifdef TEMPORARIES 140 // expected-warning@-2{{FALSE}} 141 #else 142 // expected-warning@-4{{UNKNOWN}} 143 #endif 144 } 145 } 146 147 void f5() { 148 C *after, *before; 149 { 150 const bool &x = C(true, &after, &before).x; // no-crash 151 } 152 clang_analyzer_eval(after == before); 153 #ifdef TEMPORARIES 154 // expected-warning@-2{{TRUE}} 155 #else 156 // expected-warning@-4{{UNKNOWN}} 157 #endif 158 } 159 160 struct A { // A is an aggregate. 161 const C &c; 162 }; 163 164 void f6() { 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 f7() { 174 C *after, *before; 175 { 176 A a = {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 182 void f8() { 183 C *after, *before; 184 { 185 A a[2] = {C(false, nullptr, nullptr), C(true, &after, &before)}; 186 } 187 // FIXME: Should be TRUE. Should not warn about garbage value. 188 clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}} 189 } 190 } // end namespace maintain_original_object_address_on_lifetime_extension 191 192 namespace maintain_original_object_address_on_move { 193 class C { 194 int *x; 195 196 public: 197 C() : x(nullptr) {} 198 C(int *x) : x(x) {} 199 C(const C &c) = delete; 200 C(C &&c) : x(c.x) { c.x = nullptr; } 201 C &operator=(C &&c) { 202 x = c.x; 203 c.x = nullptr; 204 return *this; 205 } 206 ~C() { 207 // This was triggering the division by zero warning in f1() and f2(): 208 // Because move-elision materialization was incorrectly causing the object 209 // to be relocated from one address to another before move, but destructor 210 // was operating on the old address, it was still thinking that 'x' is set. 211 if (x) 212 *x = 0; 213 } 214 }; 215 216 void f1() { 217 int x = 1; 218 // &x is replaced with nullptr in move-constructor before the temporary dies. 219 C c = C(&x); 220 // Hence x was not set to 0 yet. 221 1 / x; // no-warning 222 } 223 void f2() { 224 int x = 1; 225 C c; 226 // &x is replaced with nullptr in move-assignment before the temporary dies. 227 c = C(&x); 228 // Hence x was not set to 0 yet. 229 1 / x; // no-warning 230 } 231 } // end namespace maintain_original_object_address_on_move 232 233 namespace maintain_address_of_copies { 234 class C; 235 236 struct AddressVector { 237 C *buf[10]; 238 int len; 239 240 AddressVector() : len(0) {} 241 242 void push(C *c) { 243 buf[len] = c; 244 ++len; 245 } 246 }; 247 248 class C { 249 AddressVector &v; 250 251 public: 252 C(AddressVector &v) : v(v) { v.push(this); } 253 ~C() { v.push(this); } 254 255 #ifdef MOVES 256 C(C &&c) : v(c.v) { v.push(this); } 257 #endif 258 259 // Note how return-statements prefer move-constructors when available. 260 C(const C &c) : v(c.v) { 261 #ifdef MOVES 262 clang_analyzer_checkInlined(false); // no-warning 263 #else 264 v.push(this); 265 #endif 266 } // no-warning 267 268 static C make(AddressVector &v) { return C(v); } 269 }; 270 271 void f1() { 272 AddressVector v; 273 { 274 C c = C(v); 275 } 276 // 0. Construct variable 'c' (copy/move elided). 277 // 1. Destroy variable 'c'. 278 clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}} 279 clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}} 280 } 281 282 void f2() { 283 AddressVector v; 284 { 285 const C &c = C::make(v); 286 } 287 // 0. Construct the return value of make() (copy/move elided) and 288 // lifetime-extend it directly via reference 'c', 289 // 1. Destroy the temporary lifetime-extended by 'c'. 290 clang_analyzer_eval(v.len == 2); 291 clang_analyzer_eval(v.buf[0] == v.buf[1]); 292 #ifdef TEMPORARIES 293 // expected-warning@-3{{TRUE}} 294 // expected-warning@-3{{TRUE}} 295 #else 296 // expected-warning@-6{{UNKNOWN}} 297 // expected-warning@-6{{UNKNOWN}} 298 #endif 299 } 300 301 void f3() { 302 AddressVector v; 303 { 304 C &&c = C::make(v); 305 } 306 // 0. Construct the return value of make() (copy/move elided) and 307 // lifetime-extend it directly via reference 'c', 308 // 1. Destroy the temporary lifetime-extended by 'c'. 309 clang_analyzer_eval(v.len == 2); 310 clang_analyzer_eval(v.buf[0] == v.buf[1]); 311 #ifdef TEMPORARIES 312 // expected-warning@-3{{TRUE}} 313 // expected-warning@-3{{TRUE}} 314 #else 315 // expected-warning@-6{{UNKNOWN}} 316 // expected-warning@-6{{UNKNOWN}} 317 #endif 318 } 319 320 C doubleMake(AddressVector &v) { 321 return C::make(v); 322 } 323 324 void f4() { 325 AddressVector v; 326 { 327 C c = doubleMake(v); 328 } 329 // 0. Construct variable 'c' (all copies/moves elided), 330 // 1. Destroy variable 'c'. 331 clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}} 332 clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}} 333 } 334 } // end namespace maintain_address_of_copies 335