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 5 extern bool clang_analyzer_eval(bool); 6 extern bool clang_analyzer_warnIfReached(); 7 8 struct Trivial { 9 Trivial(int x) : value(x) {} 10 int value; 11 }; 12 13 struct NonTrivial : public Trivial { 14 NonTrivial(int x) : Trivial(x) {} 15 ~NonTrivial(); 16 }; 17 18 19 Trivial getTrivial() { 20 return Trivial(42); // no-warning 21 } 22 23 const 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 28 NonTrivial getNonTrivial() { 29 return NonTrivial(42); // no-warning 30 } 31 32 const NonTrivial &getNonTrivialRef() { 33 return NonTrivial(42); // expected-warning {{Address of stack memory associated with temporary object of type 'NonTrivial' returned to caller}} 34 } 35 36 namespace 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 68 namespace 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 82 namespace 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 114 namespace 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 []() { check(NoReturnDtor()); } != nullptr || check(Dtor()); 303 } 304 305 void testGnuExpressionStatements(int v) { 306 ({ ++v; v == 10 || check(NoReturnDtor()); v == 42; }) || v == 23; 307 clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} 308 309 ({ ++v; check(NoReturnDtor()); v == 42; }) || v == 23; 310 clang_analyzer_warnIfReached(); // no warning, unreachable code 311 } 312 313 void testGnuExpressionStatementsDestructionPoint(int v) { 314 // In normal context, the temporary destructor runs at the end of the full 315 // statement, thus the last statement is reached. 316 (++v, check(NoReturnDtor()), v == 42), 317 clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} 318 319 // GNU expression statements execute temporary destructors within the 320 // blocks, thus the last statement is not reached. 321 ({ ++v; check(NoReturnDtor()); v == 42; }), 322 clang_analyzer_warnIfReached(); // no warning, unreachable code 323 } 324 325 void testMultipleTemporaries(bool value) { 326 if (value) { 327 // FIXME: Find a way to verify construction order. 328 // ~Dtor should run before ~NoReturnDtor() because construction order is 329 // guaranteed by comma operator. 330 if (!value || check((NoReturnDtor(), Dtor())) || value) { 331 clang_analyzer_eval(true); // no warning, unreachable code 332 } 333 } 334 } 335 336 void testBinaryOperatorShortcut(bool value) { 337 if (value) { 338 if (false && false && check(NoReturnDtor()) && true) { 339 clang_analyzer_eval(true); 340 } 341 } 342 } 343 344 void testIfAtEndOfLoop() { 345 int y = 0; 346 while (true) { 347 if (y > 0) { 348 clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} 349 } 350 ++y; 351 // Test that the CFG gets hooked up correctly when temporary destructors 352 // are handled after a statically known branch condition. 353 if (true) (void)0; else (void)check(NoReturnDtor()); 354 } 355 } 356 357 void testTernaryAtEndOfLoop() { 358 int y = 0; 359 while (true) { 360 if (y > 0) { 361 clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} 362 } 363 ++y; 364 // Test that the CFG gets hooked up correctly when temporary destructors 365 // are handled after a statically known branch condition. 366 true ? (void)0 : (void)check(NoReturnDtor()); 367 } 368 } 369 370 void testNoReturnInComplexCondition() { 371 check(Dtor()) && 372 (check(NoReturnDtor()) || check(NoReturnDtor())) && check(Dtor()); 373 clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} 374 } 375 376 void testSequencingOfConditionalTempDtors(bool b) { 377 b || (check(Dtor()), check(NoReturnDtor())); 378 clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} 379 } 380 381 void testSequencingOfConditionalTempDtors2(bool b) { 382 (b || check(Dtor())), check(NoReturnDtor()); 383 clang_analyzer_warnIfReached(); // no warning, unreachable code 384 } 385 386 void testSequencingOfConditionalTempDtorsWithinBinaryOperators(bool b) { 387 b || (check(Dtor()) + check(NoReturnDtor())); 388 clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} 389 } 390 391 void f(Dtor d = Dtor()); 392 void testDefaultParameters() { 393 f(); 394 } 395 396 struct DefaultParam { 397 DefaultParam(int, const Dtor& d = Dtor()); 398 ~DefaultParam(); 399 }; 400 void testDefaultParamConstructorsInLoops() { 401 while (true) { 402 // FIXME: This exact pattern triggers the temporary cleanup logic 403 // to fail when adding a 'clean' state. 404 DefaultParam(42); 405 DefaultParam(42); 406 } 407 } 408 void testDefaultParamConstructorsInTernariesInLoops(bool value) { 409 while (true) { 410 // FIXME: This exact pattern triggers the temporary cleanup logic 411 // to visit the bind-temporary logic with a state that already has that 412 // temporary marked as executed. 413 value ? DefaultParam(42) : DefaultParam(42); 414 } 415 } 416 #endif // TEMPORARY_DTORS 417 } 418 419 void testStaticMaterializeTemporaryExpr() { 420 static const Trivial &ref = getTrivial(); 421 clang_analyzer_eval(ref.value == 42); // expected-warning{{TRUE}} 422 423 static const Trivial &directRef = Trivial(42); 424 clang_analyzer_eval(directRef.value == 42); // expected-warning{{TRUE}} 425 426 #if __has_feature(cxx_thread_local) 427 thread_local static const Trivial &threadRef = getTrivial(); 428 clang_analyzer_eval(threadRef.value == 42); // expected-warning{{TRUE}} 429 430 thread_local static const Trivial &threadDirectRef = Trivial(42); 431 clang_analyzer_eval(threadDirectRef.value == 42); // expected-warning{{TRUE}} 432 #endif 433 } 434 435 namespace PR16629 { 436 struct A { 437 explicit A(int* p_) : p(p_) {} 438 int* p; 439 }; 440 441 extern void escape(const A*[]); 442 extern void check(int); 443 444 void callEscape(const A& a) { 445 const A* args[] = { &a }; 446 escape(args); 447 } 448 449 void testNoWarning() { 450 int x; 451 callEscape(A(&x)); 452 check(x); // Analyzer used to give a "x is uninitialized warning" here 453 } 454 455 void set(const A*a[]) { 456 *a[0]->p = 47; 457 } 458 459 void callSet(const A& a) { 460 const A* args[] = { &a }; 461 set(args); 462 } 463 464 void testConsistency() { 465 int x; 466 callSet(A(&x)); 467 clang_analyzer_eval(x == 47); // expected-warning{{TRUE}} 468 } 469 } 470