temporaries.cpp revision 176edba5311f6eff0cad2631449885ddf4fbc9ea
1// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -w -std=c++03 %s 2// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -w -std=c++11 %s 3// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -DTEMPORARY_DTORS -verify -w -analyzer-config cfg-temporary-dtors=true %s -std=c++11 4 5extern bool clang_analyzer_eval(bool); 6extern bool clang_analyzer_warnIfReached(); 7 8struct Trivial { 9 Trivial(int x) : value(x) {} 10 int value; 11}; 12 13struct NonTrivial : public Trivial { 14 NonTrivial(int x) : Trivial(x) {} 15 ~NonTrivial(); 16}; 17 18 19Trivial getTrivial() { 20 return Trivial(42); // no-warning 21} 22 23const Trivial &getTrivialRef() { 24 return Trivial(42); // expected-warning {{Address of stack memory associated with temporary object of type 'Trivial' returned to caller}} 25} 26 27 28NonTrivial getNonTrivial() { 29 return NonTrivial(42); // no-warning 30} 31 32const NonTrivial &getNonTrivialRef() { 33 return NonTrivial(42); // expected-warning {{Address of stack memory associated with temporary object of type 'NonTrivial' returned to caller}} 34} 35 36namespace rdar13265460 { 37 struct TrivialSubclass : public Trivial { 38 TrivialSubclass(int x) : Trivial(x), anotherValue(-x) {} 39 int anotherValue; 40 }; 41 42 TrivialSubclass getTrivialSub() { 43 TrivialSubclass obj(1); 44 obj.value = 42; 45 obj.anotherValue = -42; 46 return obj; 47 } 48 49 void testImmediate() { 50 TrivialSubclass obj = getTrivialSub(); 51 52 clang_analyzer_eval(obj.value == 42); // expected-warning{{TRUE}} 53 clang_analyzer_eval(obj.anotherValue == -42); // expected-warning{{TRUE}} 54 55 clang_analyzer_eval(getTrivialSub().value == 42); // expected-warning{{TRUE}} 56 clang_analyzer_eval(getTrivialSub().anotherValue == -42); // expected-warning{{TRUE}} 57 } 58 59 void testMaterializeTemporaryExpr() { 60 const TrivialSubclass &ref = getTrivialSub(); 61 clang_analyzer_eval(ref.value == 42); // expected-warning{{TRUE}} 62 63 const Trivial &baseRef = getTrivialSub(); 64 clang_analyzer_eval(baseRef.value == 42); // expected-warning{{TRUE}} 65 } 66} 67 68namespace rdar13281951 { 69 struct Derived : public Trivial { 70 Derived(int value) : Trivial(value), value2(-value) {} 71 int value2; 72 }; 73 74 void test() { 75 Derived obj(1); 76 obj.value = 42; 77 const Trivial * const &pointerRef = &obj; 78 clang_analyzer_eval(pointerRef->value == 42); // expected-warning{{TRUE}} 79 } 80} 81 82namespace compound_literals { 83 struct POD { 84 int x, y; 85 }; 86 struct HasCtor { 87 HasCtor(int x, int y) : x(x), y(y) {} 88 int x, y; 89 }; 90 struct HasDtor { 91 int x, y; 92 ~HasDtor(); 93 }; 94 struct HasCtorDtor { 95 HasCtorDtor(int x, int y) : x(x), y(y) {} 96 ~HasCtorDtor(); 97 int x, y; 98 }; 99 100 void test() { 101 clang_analyzer_eval(((POD){1, 42}).y == 42); // expected-warning{{TRUE}} 102 clang_analyzer_eval(((HasDtor){1, 42}).y == 42); // expected-warning{{TRUE}} 103 104#if __cplusplus >= 201103L 105 clang_analyzer_eval(((HasCtor){1, 42}).y == 42); // expected-warning{{TRUE}} 106 107 // FIXME: should be TRUE, but we don't inline the constructors of 108 // temporaries because we can't model their destructors yet. 109 clang_analyzer_eval(((HasCtorDtor){1, 42}).y == 42); // expected-warning{{UNKNOWN}} 110#endif 111 } 112} 113 114namespace destructors { 115 struct Dtor { 116 ~Dtor(); 117 }; 118 extern bool coin(); 119 extern bool check(const Dtor &); 120 121 void testPR16664andPR18159Crash() { 122 // Regression test: we used to assert here when tmp dtors are enabled. 123 // PR16664 and PR18159 124 if (coin() && (coin() || coin() || check(Dtor()))) { 125 Dtor(); 126 } 127 } 128 129#ifdef TEMPORARY_DTORS 130 struct NoReturnDtor { 131 ~NoReturnDtor() __attribute__((noreturn)); 132 }; 133 134 void noReturnTemp(int *x) { 135 if (! x) NoReturnDtor(); 136 *x = 47; // no warning 137 } 138 139 void noReturnInline(int **x) { 140 NoReturnDtor(); 141 } 142 143 void callNoReturn() { 144 int *x; 145 noReturnInline(&x); 146 *x = 47; // no warning 147 } 148 149 extern bool check(const NoReturnDtor &); 150 151 void testConsistencyIf(int i) { 152 if (i != 5) 153 return; 154 if (i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) { 155 clang_analyzer_eval(true); // no warning, unreachable code 156 } 157 } 158 159 void testConsistencyTernary(int i) { 160 (i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) ? 1 : 0; 161 162 clang_analyzer_eval(true); // expected-warning{{TRUE}} 163 164 if (i != 5) 165 return; 166 167 (i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) ? 1 : 0; 168 169 clang_analyzer_eval(true); // no warning, unreachable code 170 } 171 172 // Regression test: we used to assert here. 173 // PR16664 and PR18159 174 void testConsistencyNested(int i) { 175 extern bool compute(bool); 176 177 if (i == 5 && (i == 4 || i == 5 || check(NoReturnDtor()))) 178 clang_analyzer_eval(true); // expected-warning{{TRUE}} 179 180 if (i == 5 && (i == 4 || i == 5 || check(NoReturnDtor()))) 181 clang_analyzer_eval(true); // expected-warning{{TRUE}} 182 183 if (i != 5) 184 return; 185 186 if (compute(i == 5 && 187 (i == 4 || compute(true) || 188 compute(i == 5 && (i == 4 || check(NoReturnDtor()))))) || 189 i != 4) { 190 clang_analyzer_eval(true); // expected-warning{{TRUE}} 191 } 192 193 if (compute(i == 5 && 194 (i == 4 || i == 4 || 195 compute(i == 5 && (i == 4 || check(NoReturnDtor()))))) || 196 i != 4) { 197 clang_analyzer_eval(true); // no warning, unreachable code 198 } 199 } 200 201 // PR16664 and PR18159 202 void testConsistencyNestedSimple(bool value) { 203 if (value) { 204 if (!value || check(NoReturnDtor())) { 205 clang_analyzer_eval(true); // no warning, unreachable code 206 } 207 } 208 } 209 210 // PR16664 and PR18159 211 void testConsistencyNestedComplex(bool value) { 212 if (value) { 213 if (!value || !value || check(NoReturnDtor())) { 214 clang_analyzer_eval(true); // no warning, unreachable code 215 } 216 } 217 } 218 219 // PR16664 and PR18159 220 void testConsistencyNestedWarning(bool value) { 221 if (value) { 222 if (!value || value || check(NoReturnDtor())) { 223 clang_analyzer_eval(true); // expected-warning{{TRUE}} 224 } 225 } 226 } 227 // PR16664 and PR18159 228 void testConsistencyNestedComplexMidBranch(bool value) { 229 if (value) { 230 if (!value || !value || check(NoReturnDtor()) || value) { 231 clang_analyzer_eval(true); // no warning, unreachable code 232 } 233 } 234 } 235 236 // PR16664 and PR18159 237 void testConsistencyNestedComplexNestedBranch(bool value) { 238 if (value) { 239 if (!value || (!value || check(NoReturnDtor()) || value)) { 240 clang_analyzer_eval(true); // no warning, unreachable code 241 } 242 } 243 } 244 245 // PR16664 and PR18159 246 void testConsistencyNestedVariableModification(bool value) { 247 bool other = true; 248 if (value) { 249 if (!other || !value || (other = false) || check(NoReturnDtor()) || 250 !other) { 251 clang_analyzer_eval(true); // no warning, unreachable code 252 } 253 } 254 } 255 256 void testTernaryNoReturnTrueBranch(bool value) { 257 if (value) { 258 bool b = value && (value ? check(NoReturnDtor()) : true); 259 clang_analyzer_eval(true); // no warning, unreachable code 260 } 261 } 262 void testTernaryNoReturnFalseBranch(bool value) { 263 if (value) { 264 bool b = !value && !value ? true : check(NoReturnDtor()); 265 clang_analyzer_eval(true); // no warning, unreachable code 266 } 267 } 268 void testTernaryIgnoreNoreturnBranch(bool value) { 269 if (value) { 270 bool b = !value && !value ? check(NoReturnDtor()) : true; 271 clang_analyzer_eval(true); // expected-warning{{TRUE}} 272 } 273 } 274 void testTernaryTrueBranchReached(bool value) { 275 value ? clang_analyzer_warnIfReached() : // expected-warning{{REACHABLE}} 276 check(NoReturnDtor()); 277 } 278 void testTernaryFalseBranchReached(bool value) { 279 value ? check(NoReturnDtor()) : 280 clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} 281 } 282 283 void testLoop() { 284 for (int i = 0; i < 10; ++i) { 285 if (i < 3 && (i >= 2 || check(NoReturnDtor()))) { 286 clang_analyzer_eval(true); // no warning, unreachable code 287 } 288 } 289 } 290 291 bool testRecursiveFrames(bool isInner) { 292 if (isInner || 293 (clang_analyzer_warnIfReached(), false) || // expected-warning{{REACHABLE}} 294 check(NoReturnDtor()) || 295 testRecursiveFrames(true)) { 296 clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} 297 } 298 } 299 void testRecursiveFramesStart() { testRecursiveFrames(false); } 300 301 void testLambdas() { 302 // This is the test we would like to write: 303 // []() { check(NoReturnDtor()); } != nullptr || check(Dtor()); 304 // But currently the analyzer stops when it encounters a lambda: 305 [] {}; 306 // The CFG for this now looks correct, but we still do not reach the line 307 // below. 308 clang_analyzer_warnIfReached(); // FIXME: Should warn. 309 } 310 311 void testGnuExpressionStatements(int v) { 312 ({ ++v; v == 10 || check(NoReturnDtor()); v == 42; }) || v == 23; 313 clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} 314 315 ({ ++v; check(NoReturnDtor()); v == 42; }) || v == 23; 316 clang_analyzer_warnIfReached(); // no warning, unreachable code 317 } 318 319 void testGnuExpressionStatementsDestructionPoint(int v) { 320 // In normal context, the temporary destructor runs at the end of the full 321 // statement, thus the last statement is reached. 322 (++v, check(NoReturnDtor()), v == 42), 323 clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} 324 325 // GNU expression statements execute temporary destructors within the 326 // blocks, thus the last statement is not reached. 327 ({ ++v; check(NoReturnDtor()); v == 42; }), 328 clang_analyzer_warnIfReached(); // no warning, unreachable code 329 } 330 331 void testMultipleTemporaries(bool value) { 332 if (value) { 333 // FIXME: Find a way to verify construction order. 334 // ~Dtor should run before ~NoReturnDtor() because construction order is 335 // guaranteed by comma operator. 336 if (!value || check((NoReturnDtor(), Dtor())) || value) { 337 clang_analyzer_eval(true); // no warning, unreachable code 338 } 339 } 340 } 341 342 void testBinaryOperatorShortcut(bool value) { 343 if (value) { 344 if (false && false && check(NoReturnDtor()) && true) { 345 clang_analyzer_eval(true); 346 } 347 } 348 } 349 350 void testIfAtEndOfLoop() { 351 int y = 0; 352 while (true) { 353 if (y > 0) { 354 clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} 355 } 356 ++y; 357 // Test that the CFG gets hooked up correctly when temporary destructors 358 // are handled after a statically known branch condition. 359 if (true) (void)0; else (void)check(NoReturnDtor()); 360 } 361 } 362 363 void testTernaryAtEndOfLoop() { 364 int y = 0; 365 while (true) { 366 if (y > 0) { 367 clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} 368 } 369 ++y; 370 // Test that the CFG gets hooked up correctly when temporary destructors 371 // are handled after a statically known branch condition. 372 true ? (void)0 : (void)check(NoReturnDtor()); 373 } 374 } 375 376 void testNoReturnInComplexCondition() { 377 check(Dtor()) && 378 (check(NoReturnDtor()) || check(NoReturnDtor())) && check(Dtor()); 379 clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} 380 } 381 382 void testSequencingOfConditionalTempDtors(bool b) { 383 b || (check(Dtor()), check(NoReturnDtor())); 384 clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} 385 } 386 387 void testSequencingOfConditionalTempDtors2(bool b) { 388 (b || check(Dtor())), check(NoReturnDtor()); 389 clang_analyzer_warnIfReached(); // no warning, unreachable code 390 } 391 392 void testSequencingOfConditionalTempDtorsWithinBinaryOperators(bool b) { 393 b || (check(Dtor()) + check(NoReturnDtor())); 394 clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} 395 } 396 397 void f(Dtor d = Dtor()); 398 void testDefaultParameters() { 399 f(); 400 } 401 402 struct DefaultParam { 403 DefaultParam(int, const Dtor& d = Dtor()); 404 ~DefaultParam(); 405 }; 406 void testDefaultParamConstructorsInLoops() { 407 while (true) { 408 // FIXME: This exact pattern triggers the temporary cleanup logic 409 // to fail when adding a 'clean' state. 410 DefaultParam(42); 411 DefaultParam(42); 412 } 413 } 414 void testDefaultParamConstructorsInTernariesInLoops(bool value) { 415 while (true) { 416 // FIXME: This exact pattern triggers the temporary cleanup logic 417 // to visit the bind-temporary logic with a state that already has that 418 // temporary marked as executed. 419 value ? DefaultParam(42) : DefaultParam(42); 420 } 421 } 422#endif // TEMPORARY_DTORS 423} 424 425void testStaticMaterializeTemporaryExpr() { 426 static const Trivial &ref = getTrivial(); 427 clang_analyzer_eval(ref.value == 42); // expected-warning{{TRUE}} 428 429 static const Trivial &directRef = Trivial(42); 430 clang_analyzer_eval(directRef.value == 42); // expected-warning{{TRUE}} 431 432#if __has_feature(cxx_thread_local) 433 thread_local static const Trivial &threadRef = getTrivial(); 434 clang_analyzer_eval(threadRef.value == 42); // expected-warning{{TRUE}} 435 436 thread_local static const Trivial &threadDirectRef = Trivial(42); 437 clang_analyzer_eval(threadDirectRef.value == 42); // expected-warning{{TRUE}} 438#endif 439} 440 441namespace PR16629 { 442 struct A { 443 explicit A(int* p_) : p(p_) {} 444 int* p; 445 }; 446 447 extern void escape(const A*[]); 448 extern void check(int); 449 450 void callEscape(const A& a) { 451 const A* args[] = { &a }; 452 escape(args); 453 } 454 455 void testNoWarning() { 456 int x; 457 callEscape(A(&x)); 458 check(x); // Analyzer used to give a "x is uninitialized warning" here 459 } 460 461 void set(const A*a[]) { 462 *a[0]->p = 47; 463 } 464 465 void callSet(const A& a) { 466 const A* args[] = { &a }; 467 set(args); 468 } 469 470 void testConsistency() { 471 int x; 472 callSet(A(&x)); 473 clang_analyzer_eval(x == 47); // expected-warning{{TRUE}} 474 } 475} 476