1018 lines · cpp
1// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move %s\2// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\3// RUN: -analyzer-config exploration_strategy=unexplored_first_queue\4// RUN: -analyzer-checker core,cplusplus.SmartPtrModeling,debug.ExprInspection\5// RUN: -verify=expected,peaceful,non-aggressive6// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move %s\7// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\8// RUN: -analyzer-config exploration_strategy=dfs -DDFS\9// RUN: -analyzer-checker core,cplusplus.SmartPtrModeling,debug.ExprInspection\10// RUN: -verify=expected,peaceful,non-aggressive11// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move %s\12// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\13// RUN: -analyzer-config exploration_strategy=unexplored_first_queue\14// RUN: -analyzer-config cplusplus.Move:WarnOn=KnownsOnly\15// RUN: -analyzer-checker core,cplusplus.SmartPtrModeling,debug.ExprInspection\16// RUN: -verify=expected,non-aggressive17// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\18// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\19// RUN: -analyzer-config exploration_strategy=dfs -DDFS\20// RUN: -analyzer-config cplusplus.Move:WarnOn=KnownsOnly\21// RUN: -analyzer-checker core,cplusplus.SmartPtrModeling,debug.ExprInspection\22// RUN: -verify=expected,non-aggressive23// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move %s\24// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\25// RUN: -analyzer-config exploration_strategy=unexplored_first_queue\26// RUN: -analyzer-config cplusplus.Move:WarnOn=All\27// RUN: -analyzer-checker core,cplusplus.SmartPtrModeling,debug.ExprInspection\28// RUN: -verify=expected,peaceful,aggressive29// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move %s\30// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\31// RUN: -analyzer-config exploration_strategy=dfs -DDFS\32// RUN: -analyzer-config cplusplus.Move:WarnOn=All\33// RUN: -analyzer-checker core,cplusplus.SmartPtrModeling,debug.ExprInspection\34// RUN: -verify=expected,peaceful,aggressive35 36// RUN: not %clang_analyze_cc1 -verify %s \37// RUN: -analyzer-checker=core \38// RUN: -analyzer-checker=cplusplus.Move \39// RUN: -analyzer-config cplusplus.Move:WarnOn="a bunch of things" \40// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-MOVE-INVALID-VALUE41 42// CHECK-MOVE-INVALID-VALUE: (frontend): invalid input for checker option43// CHECK-MOVE-INVALID-VALUE-SAME: 'cplusplus.Move:WarnOn', that expects either44// CHECK-MOVE-INVALID-VALUE-SAME: "KnownsOnly", "KnownsAndLocals" or "All"45// CHECK-MOVE-INVALID-VALUE-SAME: string value46 47// Tests checker-messages printing.48// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move %s\49// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\50// RUN: -analyzer-config exploration_strategy=dfs -DDFS\51// RUN: -analyzer-config cplusplus.Move:WarnOn=All -DAGGRESSIVE_DFS\52// RUN: -analyzer-checker core,cplusplus.SmartPtrModeling,debug.ExprInspection\53// RUN: -verify=expected,peaceful,aggressive %s 2>&1 | FileCheck %s54 55#include "Inputs/system-header-simulator-cxx.h"56 57void clang_analyzer_warnIfReached();58void clang_analyzer_printState();59 60class B {61public:62 B() = default;63 B(const B &) = default;64 B(B &&) = default;65 B& operator=(const B &q) = default;66 void operator=(B &&b) {67 return;68 }69 void foo() { return; }70};71 72class A {73 int i;74 double d;75 76public:77 B b;78 A(int ii = 42, double dd = 1.0) : d(dd), i(ii), b(B()) {}79 void moveconstruct(A &&other) {80 std::swap(b, other.b);81 std::swap(d, other.d);82 std::swap(i, other.i);83 return;84 }85 static A get() {86 A v(12, 13);87 return v;88 }89 A(A *a) {90 moveconstruct(std::move(*a));91 }92 A(const A &other) : i(other.i), d(other.d), b(other.b) {}93 A(A &&other) : i(other.i), d(other.d), b(std::move(other.b)) { // aggressive-note{{Object 'b' is moved}}94 }95 A(A &&other, char *k) {96 moveconstruct(std::move(other));97 }98 void operator=(const A &other) {99 i = other.i;100 d = other.d;101 b = other.b;102 return;103 }104 void operator=(A &&other) {105 moveconstruct(std::move(other));106 return;107 }108 int getI() { return i; }109 int foo() const;110 void bar() const;111 void reset();112 void destroy();113 void clear();114 void resize(std::size_t);115 void assign(const A &);116 bool empty() const;117 bool isEmpty() const;118 operator bool() const;119 120 void testUpdateField() {121 A a;122 A b = std::move(a);123 a.i = 1;124 a.foo(); // no-warning125 }126 void testUpdateFieldDouble() {127 A a;128 A b = std::move(a);129 a.d = 1.0;130 a.foo(); // no-warning131 }132};133 134int bignum();135 136void moveInsideFunctionCall(A a) {137 A b = std::move(a);138}139void leftRefCall(A &a) {140 a.foo();141}142void rightRefCall(A &&a) {143 a.foo();144}145void constCopyOrMoveCall(const A a) {146 a.foo();147}148 149void copyOrMoveCall(A a) {150 a.foo();151}152 153void simpleMoveCtorTest() {154 {155 A a;156 A b = std::move(a); // peaceful-note {{Object 'a' is moved}}157 158#ifdef AGGRESSIVE_DFS159 clang_analyzer_printState();160 161// CHECK: "checker_messages": [162// CHECK-NEXT: { "checker": "cplusplus.Move", "messages": [163// CHECK-NEXT: "Moved-from objects :",164// CHECK: "a: moved",165// CHECK: ""166// CHECK-NEXT: ]}167// CHECK-NEXT: ]168#endif169 170 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}171 // peaceful-note@-1 {{Method called on moved-from object 'a'}}172 }173 {174 A a;175 A b = std::move(a); // peaceful-note {{Object 'a' is moved}}176 b = a; // peaceful-warning {{Moved-from object 'a' is copied}}177 // peaceful-note@-1 {{Moved-from object 'a' is copied}}178 }179 {180 A a;181 A b = std::move(a); // peaceful-note {{Object 'a' is moved}}182 b = std::move(a); // peaceful-warning {{Moved-from object 'a' is moved}}183 // peaceful-note@-1 {{Moved-from object 'a' is moved}}184 }185}186 187void simpleMoveAssignementTest() {188 {189 A a;190 A b;191 b = std::move(a); // peaceful-note {{Object 'a' is moved}}192 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}193 // peaceful-note@-1 {{Method called on moved-from object 'a'}}194 }195 {196 A a;197 A b;198 b = std::move(a); // peaceful-note {{Object 'a' is moved}}199 A c(a); // peaceful-warning {{Moved-from object 'a' is copied}}200 // peaceful-note@-1 {{Moved-from object 'a' is copied}}201 }202 {203 A a;204 A b;205 b = std::move(a); // peaceful-note {{Object 'a' is moved}}206 A c(std::move(a)); // peaceful-warning {{Moved-from object 'a' is moved}}207 // peaceful-note@-1 {{Moved-from object 'a' is moved}}208 }209}210 211void moveInInitListTest() {212 struct S {213 A a;214 };215 A a;216 S s{std::move(a)}; // peaceful-note {{Object 'a' is moved}}217 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}218 // peaceful-note@-1 {{Method called on moved-from object 'a'}}219}220 221// Don't report a bug if the variable was assigned to in the meantime.222void reinitializationTest(int i) {223 {224 A a;225 A b;226 b = std::move(a);227 a = A();228 a.foo();229 }230 {231 A a;232 if (i == 1) { // peaceful-note 2 {{Assuming 'i' is not equal to 1}}233 // peaceful-note@-1 2 {{Taking false branch}}234 A b;235 b = std::move(a);236 a = A();237 }238 if (i == 2) { // peaceful-note 2 {{Assuming 'i' is not equal to 2}}239 // peaceful-note@-1 2 {{Taking false branch}}240 a.foo(); // no-warning241 }242 }243 {244 A a;245 if (i == 1) { // peaceful-note 2 {{'i' is not equal to 1}}246 // peaceful-note@-1 2 {{Taking false branch}}247 (void)std::move(a);248 }249 if (i == 2) { // peaceful-note 2 {{'i' is not equal to 2}}250 // peaceful-note@-1 2 {{Taking false branch}}251 a = A();252 a.foo();253 }254 }255 // The built-in assignment operator should also be recognized as a256 // reinitialization. (std::move() may be called on built-in types in template257 // code.)258 {259 int a1 = 1, a2 = 2;260 std::swap(a1, a2);261 }262 // A std::move() after the assignment makes the variable invalid again.263 {264 A a;265 A b;266 b = std::move(a);267 a = A();268 b = std::move(a); // peaceful-note {{Object 'a' is moved}}269 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}270 // peaceful-note@-1 {{Method called on moved-from object 'a'}}271 }272 // If a path exist where we not reinitialize the variable we report a bug.273 {274 A a;275 A b;276 b = std::move(a); // peaceful-note {{Object 'a' is moved}}277 if (i < 10) { // peaceful-note {{Assuming 'i' is >= 10}}278 // peaceful-note@-1 {{Taking false branch}}279 a = A();280 }281 if (i > 5) { // peaceful-note {{'i' is > 5}}282 // peaceful-note@-1 {{Taking true branch}}283 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}284 // peaceful-note@-1 {{Method called on moved-from object 'a'}}285 }286 }287}288 289// Using decltype on an expression is not a use.290void decltypeIsNotUseTest() {291 A a;292 // A b(std::move(a));293 decltype(a) other_a; // no-warning294}295 296void loopTest() {297 {298 A a;299 // FIXME: Execution doesn't jump to the end of the function yet.300 for (int i = 0; i < bignum(); i++) { // peaceful-note {{Loop condition is false. Execution jumps to the end of the function}}301 rightRefCall(std::move(a)); // no-warning302 }303 }304 {305 A a;306 for (int i = 0; i < 2; i++) { // peaceful-note {{Loop condition is true. Entering loop body}}307 // peaceful-note@-1 {{Loop condition is true. Entering loop body}}308 // peaceful-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}309 rightRefCall(std::move(a)); // no-warning310 }311 }312 {313 A a;314 for (int i = 0; i < bignum(); i++) { // peaceful-note {{Loop condition is false. Execution jumps to the end of the function}}315 leftRefCall(a); // no-warning316 }317 }318 {319 A a;320 for (int i = 0; i < 2; i++) { // peaceful-note {{Loop condition is true. Entering loop body}}321 // peaceful-note@-1 {{Loop condition is true. Entering loop body}}322 // peaceful-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}323 leftRefCall(a); // no-warning324 }325 }326 {327 A a;328 for (int i = 0; i < bignum(); i++) { // peaceful-note {{Loop condition is false. Execution jumps to the end of the function}}329 constCopyOrMoveCall(a); // no-warning330 }331 }332 {333 A a;334 for (int i = 0; i < 2; i++) { // peaceful-note {{Loop condition is true. Entering loop body}}335 // peaceful-note@-1 {{Loop condition is true. Entering loop body}}336 // peaceful-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}337 constCopyOrMoveCall(a); // no-warning338 }339 }340 {341 A a;342 for (int i = 0; i < bignum(); i++) { // peaceful-note {{Loop condition is false. Execution jumps to the end of the function}}343 moveInsideFunctionCall(a); // no-warning344 }345 }346 {347 A a;348 for (int i = 0; i < 2; i++) { // peaceful-note {{Loop condition is true. Entering loop body}}349 // peaceful-note@-1 {{Loop condition is true. Entering loop body}}350 // peaceful-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}351 moveInsideFunctionCall(a); // no-warning352 }353 }354 {355 A a;356 for (int i = 0; i < bignum(); i++) { // peaceful-note {{Loop condition is false. Execution jumps to the end of the function}}357 copyOrMoveCall(a); // no-warning358 }359 }360 {361 A a;362 for (int i = 0; i < 2; i++) { // peaceful-note {{Loop condition is true. Entering loop body}}363 // peaceful-note@-1 {{Loop condition is true. Entering loop body}}364 // peaceful-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}365 copyOrMoveCall(a); // no-warning366 }367 }368 {369 A a;370 for (int i = 0; i < bignum(); i++) { // peaceful-note {{Loop condition is true. Entering loop body}}371 // peaceful-note@-1 {{Loop condition is true. Entering loop body}}372 constCopyOrMoveCall(std::move(a)); // peaceful-note {{Object 'a' is moved}}373 // peaceful-warning@-1 {{Moved-from object 'a' is moved}}374 // peaceful-note@-2 {{Moved-from object 'a' is moved}}375 }376 }377 378 // Don't warn if we return after the move.379 {380 A a;381 for (int i = 0; i < 3; ++i) {382 a.bar();383 if (a.foo() > 0) {384 A b;385 b = std::move(a); // no-warning386 return;387 }388 }389 }390}391 392// Report a usage of a moved-from object only at the first use.393void uniqueTest(bool cond) {394 A a(42, 42.0);395 A b;396 b = std::move(a); // peaceful-note {{Object 'a' is moved}}397 398 if (cond) { // peaceful-note {{Assuming 'cond' is true}}399 // peaceful-note@-1 {{Taking true branch}}400 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}401 // peaceful-note@-1 {{Method called on moved-from object 'a'}}402 }403 if (cond) {404 a.bar(); // no-warning405 }406 407 a.bar(); // no-warning408}409 410void uniqueTest2() {411 A a;412 A a1 = std::move(a); // peaceful-note {{Object 'a' is moved}}413 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}414 // peaceful-note@-1 {{Method called on moved-from object 'a'}}415 416 A a2 = std::move(a); // no-warning417 a.foo(); // no-warning418}419 420// There are exceptions where we assume in general that the method works fine421//even on moved-from objects.422void moveSafeFunctionsTest() {423 A a;424 A b = std::move(a); // peaceful-note {{Object 'a' is moved}}425 a.empty(); // no-warning426 a.isEmpty(); // no-warning427 (void)a; // no-warning428 (bool)a; // expected-warning {{expression result unused}}429 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}430 // peaceful-note@-1 {{Method called on moved-from object 'a'}}431}432 433void moveStateResetFunctionsTest() {434 {435 A a;436 A b = std::move(a);437 a.reset(); // no-warning438 a.foo(); // no-warning439 // Test if resets the state of subregions as well.440 a.b.foo(); // no-warning441 }442 {443 A a;444 A b = std::move(a);445 a.destroy(); // no-warning446 a.foo(); // no-warning447 }448 {449 A a;450 A b = std::move(a);451 a.clear(); // no-warning452 a.foo(); // no-warning453 a.b.foo(); // no-warning454 }455 {456 A a;457 A b = std::move(a);458 a.resize(0); // no-warning459 a.foo(); // no-warning460 a.b.foo(); // no-warning461 }462 {463 A a;464 A b = std::move(a);465 a.assign(A()); // no-warning466 a.foo(); // no-warning467 a.b.foo(); // no-warning468 }469}470 471// Moves or uses that occur as part of template arguments.472template <int>473class ClassTemplate {474public:475 void foo(A a);476};477 478template <int>479void functionTemplate(A a);480 481void templateArgIsNotUseTest() {482 {483 // A pattern like this occurs in the EXPECT_EQ and ASSERT_EQ macros in484 // Google Test.485 A a;486 ClassTemplate<sizeof(A(std::move(a)))>().foo(std::move(a)); // no-warning487 }488 {489 A a;490 functionTemplate<sizeof(A(std::move(a)))>(std::move(a)); // no-warning491 }492}493 494// Moves of global variables are not reported.495A global_a;496void globalVariablesTest() {497 (void)std::move(global_a);498 global_a.foo(); // no-warning499}500 501// Moves of member variables.502class memberVariablesTest {503 A a;504 static A static_a;505 506 void f() {507 A b;508 b = std::move(a); // aggressive-note {{Object 'a' is moved}}509 510 a.foo(); // aggressive-warning {{Method called on moved-from object 'a'}}511 // aggressive-note@-1 {{Method called on moved-from object 'a'}}512 513 b = std::move(static_a); // aggressive-note {{Object 'static_a' is moved}}514 static_a.foo(); // aggressive-warning {{Method called on moved-from object 'static_a'}}515 // aggressive-note@-1 {{Method called on moved-from object 'static_a'}}516 }517};518 519void PtrAndArrayTest() {520 A *Ptr = new A(1, 1.5);521 A Arr[10];522 Arr[2] = std::move(*Ptr); // aggressive-note{{Object is moved}}523 (*Ptr).foo(); // aggressive-warning{{Method called on moved-from object}}524 // aggressive-note@-1{{Method called on moved-from object}}525 526 Ptr = &Arr[1];527 Arr[3] = std::move(Arr[1]); // aggressive-note {{Object is moved}}528 Ptr->foo(); // aggressive-warning {{Method called on moved-from object}}529 // aggressive-note@-1 {{Method called on moved-from object}}530 531 Arr[3] = std::move(Arr[2]); // aggressive-note{{Object is moved}}532 Arr[2].foo(); // aggressive-warning{{Method called on moved-from object}}533 // aggressive-note@-1{{Method called on moved-from object}}534 535 Arr[2] = std::move(Arr[3]); // reinitialization536 Arr[2].foo(); // no-warning537}538 539void exclusiveConditionsTest(bool cond) {540 A a;541 if (cond) {542 A b;543 b = std::move(a);544 }545 if (!cond) {546 a.bar(); // no-warning547 }548}549 550void differentBranchesTest(int i) {551 // Don't warn if the use is in a different branch from the move.552 {553 A a;554 if (i > 0) { // peaceful-note {{Assuming 'i' is > 0}}555 // peaceful-note@-1 {{Taking true branch}}556 A b;557 b = std::move(a);558 } else {559 a.foo(); // no-warning560 }561 }562 // Same thing, but with a ternary operator.563 {564 A a, b;565 i > 0 ? (void)(b = std::move(a)) : a.bar(); // no-warning566 // peaceful-note@-1 {{'i' is > 0}}567 // peaceful-note@-2 {{'?' condition is true}}568 }569 // A variation on the theme above.570 {571 A a;572 a.foo() > 0 ? a.foo() : A(std::move(a)).foo();573 // peaceful-note@-1 {{Assuming the condition is true}}574 // peaceful-note@-2 {{'?' condition is true}}575 }576 // Same thing, but with a switch statement.577 {578 A a, b;579 switch (i) { // peaceful-note {{Control jumps to 'case 1:'}}580 case 1:581 b = std::move(a); // no-warning582 // FIXME: Execution doesn't jump to the end of the function yet.583 break; // peaceful-note {{Execution jumps to the end of the function}}584 case 2:585 a.foo(); // no-warning586 break;587 }588 }589 // However, if there's a fallthrough, we do warn.590 {591 A a, b;592 switch (i) { // peaceful-note {{Control jumps to 'case 1:'}}593 case 1:594 b = std::move(a); // peaceful-note {{Object 'a' is moved}}595 case 2:596 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}597 // peaceful-note@-1 {{Method called on moved-from object 'a'}}598 break;599 }600 }601}602 603void tempTest() {604 A a = A::get();605 A::get().foo(); // no-warning606 for (int i = 0; i < bignum(); i++) {607 A::get().foo(); // no-warning608 }609}610 611void lifeTimeExtendTest() {612 A&& a = A{};613 A b = std::move(a); // peaceful-note {{Object is moved}}614 615 a.foo(); // peaceful-warning {{Method called on moved-from object}}616 // peaceful-note@-1 {{Method called on moved-from object}}617}618 619void interFunTest1(A &a) {620 a.bar(); // peaceful-warning {{Method called on moved-from object 'a'}}621 // peaceful-note@-1 {{Method called on moved-from object 'a'}}622}623 624void interFunTest2() {625 A a;626 A b;627 b = std::move(a); // peaceful-note {{Object 'a' is moved}}628 interFunTest1(a); // peaceful-note {{Calling 'interFunTest1'}}629}630 631void foobar(A a, int i);632void foobar(int i, A a);633 634void paramEvaluateOrderTest() {635 A a;636 foobar(std::move(a), a.getI()); // peaceful-note {{Object 'a' is moved}}637 // peaceful-warning@-1 {{Method called on moved-from object 'a'}}638 // peaceful-note@-2 {{Method called on moved-from object 'a'}}639 640 //FALSE NEGATIVE since parameters evaluate order is undefined641 foobar(a.getI(), std::move(a)); //no-warning642}643 644void not_known_pass_by_ref(A &a);645void not_known_pass_by_const_ref(const A &a);646void not_known_pass_by_rvalue_ref(A &&a);647void not_known_pass_by_ptr(A *a);648void not_known_pass_by_const_ptr(const A *a);649 650void regionAndPointerEscapeTest() {651 {652 A a;653 A b;654 b = std::move(a);655 not_known_pass_by_ref(a);656 a.foo(); // no-warning657 }658 {659 A a;660 A b;661 b = std::move(a); // peaceful-note{{Object 'a' is moved}}662 not_known_pass_by_const_ref(a);663 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}664 // peaceful-note@-1 {{Method called on moved-from object 'a'}}665 }666 {667 A a;668 A b;669 b = std::move(a);670 not_known_pass_by_rvalue_ref(std::move(a));671 a.foo(); // no-warning672 }673 {674 A a;675 A b;676 b = std::move(a);677 not_known_pass_by_ptr(&a);678 a.foo(); // no-warning679 }680 {681 A a;682 A b;683 b = std::move(a); // peaceful-note {{Object 'a' is moved}}684 not_known_pass_by_const_ptr(&a);685 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}686 // peaceful-note@-1 {{Method called on moved-from object 'a'}}687 }688}689 690// A declaration statement containing multiple declarations sequences the691// initializer expressions.692void declarationSequenceTest() {693 {694 A a;695 A a1 = a, a2 = std::move(a); // no-warning696 }697 {698 A a;699 A a1 = std::move(a), a2 = a; // peaceful-note {{Object 'a' is moved}}700 // peaceful-warning@-1 {{Moved-from object 'a' is copied}}701 // peaceful-note@-2 {{Moved-from object 'a' is copied}}702 }703}704 705// The logical operators && and || sequence their operands.706void logicalOperatorsSequenceTest() {707 {708 A a;709 if (a.foo() > 0 && A(std::move(a)).foo() > 0) { // peaceful-note {{Assuming the condition is false}}710 // peaceful-note@-1 {{Left side of '&&' is false}}711 // peaceful-note@-2 {{Taking false branch}}712 // And the other report:713 // peaceful-note@-4 {{Assuming the condition is false}}714 // peaceful-note@-5 {{Left side of '&&' is false}}715 // peaceful-note@-6 {{Taking false branch}}716 A().bar();717 }718 }719 // A variation: Negate the result of the && (which pushes the && further down720 // into the AST).721 {722 A a;723 if (!(a.foo() > 0 && A(std::move(a)).foo() > 0)) { // peaceful-note {{Assuming the condition is false}}724 // peaceful-note@-1 {{Left side of '&&' is false}}725 // peaceful-note@-2 {{Taking true branch}}726 // And the other report:727 // peaceful-note@-4 {{Assuming the condition is false}}728 // peaceful-note@-5 {{Left side of '&&' is false}}729 // peaceful-note@-6 {{Taking true branch}}730 A().bar();731 }732 }733 {734 A a;735 if (A(std::move(a)).foo() > 0 && a.foo() > 0) { // peaceful-note {{Object 'a' is moved}}736 // peaceful-note@-1 {{Assuming the condition is true}}737 // peaceful-note@-2 {{Left side of '&&' is true}}738 // peaceful-warning@-3 {{Method called on moved-from object 'a'}}739 // peaceful-note@-4 {{Method called on moved-from object 'a'}}740 // And the other report:741 // peaceful-note@-6 {{Assuming the condition is false}}742 // peaceful-note@-7 {{Left side of '&&' is false}}743 // peaceful-note@-8 {{Taking false branch}}744 A().bar();745 }746 }747 {748 A a;749 if (a.foo() > 0 || A(std::move(a)).foo() > 0) { // peaceful-note {{Assuming the condition is true}}750 // peaceful-note@-1 {{Left side of '||' is true}}751 // peaceful-note@-2 {{Taking true branch}}752 A().bar();753 }754 }755 {756 A a;757 if (A(std::move(a)).foo() > 0 || a.foo() > 0) { // peaceful-note {{Object 'a' is moved}}758 // peaceful-note@-1 {{Assuming the condition is false}}759 // peaceful-note@-2 {{Left side of '||' is false}}760 // peaceful-warning@-3 {{Method called on moved-from object 'a'}}761 // peaceful-note@-4 {{Method called on moved-from object 'a'}}762 A().bar();763 }764 }765}766 767// A range-based for sequences the loop variable declaration before the body.768void forRangeSequencesTest() {769 A v[2] = {A(), A()};770 for (A &a : v) {771 A b;772 b = std::move(a); // no-warning773 }774}775 776// If a variable is declared in an if statement, the declaration of the variable777// (which is treated like a reinitialization by the check) is sequenced before778// the evaluation of the condition (which constitutes a use).779void ifStmtSequencesDeclAndConditionTest() {780 for (int i = 0; i < 3; ++i) {781 if (A a = A()) {782 A b;783 b = std::move(a); // no-warning784 }785 }786}787 788struct C : public A {789 [[clang::reinitializes]] void reinit();790};791 792void subRegionMoveTest() {793 {794 A a;795 B b = std::move(a.b); // aggressive-note {{Object 'b' is moved}}796 a.b.foo(); // aggressive-warning {{Method called on moved-from object 'b'}}797 // aggressive-note@-1 {{Method called on moved-from object 'b'}}798 }799 {800 A a;801 A a1 = std::move(a); // aggressive-note {{Calling move constructor for 'A'}}802 // aggressive-note@-1 {{Returning from move constructor for 'A'}}803 a.b.foo(); // aggressive-warning{{Method called on moved-from object 'b'}}804 // aggressive-note@-1{{Method called on moved-from object 'b'}}805 }806 // Don't report a misuse if any SuperRegion is already reported.807 {808 A a;809 A a1 = std::move(a); // peaceful-note {{Object 'a' is moved}}810 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}811 // peaceful-note@-1 {{Method called on moved-from object 'a'}}812 a.b.foo(); // no-warning813 }814 {815 C c;816 C c1 = std::move(c); // peaceful-note {{Object 'c' is moved}}817 c.foo(); // peaceful-warning {{Method called on moved-from object 'c'}}818 // peaceful-note@-1 {{Method called on moved-from object 'c'}}819 c.b.foo(); // no-warning820 }821}822 823void resetSuperClass() {824 C c;825 C c1 = std::move(c);826 c.clear();827 C c2 = c; // no-warning828}829 830void resetSuperClass2() {831 C c;832 C c1 = std::move(c);833 c.reinit();834 C c2 = c; // no-warning835}836 837void reportSuperClass() {838 C c;839 C c1 = std::move(c); // peaceful-note {{Object 'c' is moved}}840 c.foo(); // peaceful-warning {{Method called on moved-from object 'c'}}841 // peaceful-note@-1 {{Method called on moved-from object 'c'}}842 C c2 = c; // no-warning843}844 845struct Empty {};846 847Empty inlinedCall() {848 // Used to warn because region 'e' failed to be cleaned up because no symbols849 // have ever died during the analysis and the checkDeadSymbols callback850 // was skipped entirely.851 Empty e{};852 return e; // no-warning853}854 855void checkInlinedCallZombies() {856 while (true)857 inlinedCall();858}859 860void checkLoopZombies() {861 while (true) {862 Empty e{};863 Empty f = std::move(e); // no-warning864 }865}866 867void checkMoreLoopZombies1(bool flag) {868 while (flag) {869 Empty e{};870 if (true)871 e; // expected-warning {{expression result unused}}872 Empty f = std::move(e); // no-warning873 }874}875 876bool coin();877 878void checkMoreLoopZombies2(bool flag) {879 while (flag) {880 Empty e{};881 while (coin())882 e; // expected-warning {{expression result unused}}883 Empty f = std::move(e); // no-warning884 }885}886 887void checkMoreLoopZombies3(bool flag) {888 while (flag) {889 Empty e{};890 do891 e; // expected-warning {{expression result unused}}892 while (coin());893 Empty f = std::move(e); // no-warning894 }895}896 897void checkMoreLoopZombies4(bool flag) {898 while (flag) {899 Empty e{};900 for (; coin();)901 e; // expected-warning {{expression result unused}}902 Empty f = std::move(e); // no-warning903 }904}905 906void checkExplicitDestructorCalls() {907 // The below code segments invoke the destructor twice (explicit and 908 // implicit). While this is not a desired code behavior, it is 909 // not the use-after-move checker's responsibility to issue such a warning.910 {911 B* b = new B;912 B a = std::move(*b);913 b->~B(); // no-warning 914 delete b;915 }916 {917 B a, b;918 new (&a) B(reinterpret_cast<B &&>(b));919 (&b)->~B(); // no-warning920 }921 {922 B b;923 B a = std::move(b);924 b.~B(); // no-warning 925 }926}927 928struct MoveOnlyWithDestructor {929 MoveOnlyWithDestructor();930 ~MoveOnlyWithDestructor();931 MoveOnlyWithDestructor(const MoveOnlyWithDestructor &m) = delete;932 MoveOnlyWithDestructor(MoveOnlyWithDestructor &&m);933};934 935MoveOnlyWithDestructor foo() {936 MoveOnlyWithDestructor m;937 return m;938}939 940class HasSTLField {941 std::vector<int> V;942 void testVector() {943 // Warn even in non-aggressive mode when it comes to STL, because944 // in STL the object is left in "valid but unspecified state" after move.945 std::vector<int> W = std::move(V); // expected-note {{Object 'V' of type 'std::vector' is left in a valid but unspecified state after move}}946 V.push_back(123); // expected-warning {{Method called on moved-from object 'V'}}947 // expected-note@-1 {{Method called on moved-from object 'V'}}948 }949 950 std::unique_ptr<int> P;951 void testUniquePtr() {952 // unique_ptr remains in a well-defined state after move.953 std::unique_ptr<int> Q = std::move(P); // aggressive-note {{Object 'P' is moved}}954 // non-aggressive-note@-1 {{Smart pointer 'P' of type 'std::unique_ptr' is reset to null when moved from}}955 P.get(); // aggressive-warning{{Method called on moved-from object 'P'}}956 // aggressive-note@-1{{Method called on moved-from object 'P'}}957 958 // Because that well-defined state is null, dereference is still UB.959 // Note that in aggressive mode we already warned about 'P',960 // so no extra warning is generated.961 *P += 1; // non-aggressive-warning{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}}962 // non-aggressive-note@-1{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}}963 964 // The program should have crashed by now.965 clang_analyzer_warnIfReached(); // no-warning966 }967};968 969void localRValueMove(A &&a) {970 A b = std::move(a); // peaceful-note {{Object 'a' is moved}}971 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}972 // peaceful-note@-1 {{Method called on moved-from object 'a'}}973}974 975void localUniquePtr(std::unique_ptr<int> P) {976 // Even though unique_ptr is safe to use after move,977 // reusing a local variable this way usually indicates a bug.978 std::unique_ptr<int> Q = std::move(P); // peaceful-note {{Object 'P' is moved}}979 P.get(); // peaceful-warning {{Method called on moved-from object 'P'}}980 // peaceful-note@-1 {{Method called on moved-from object 'P'}}981}982 983void localUniquePtrWithArrow(std::unique_ptr<A> P) {984 std::unique_ptr<A> Q = std::move(P); // expected-note{{Smart pointer 'P' of type 'std::unique_ptr' is reset to null when moved from}}985 P->foo(); // expected-warning{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}}986 // expected-note@-1{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}}987}988 989void getAfterMove(std::unique_ptr<A> P) {990 std::unique_ptr<A> Q = std::move(P); // peaceful-note {{Object 'P' is moved}}991 992 // TODO: Explain why (bool)P is false.993 if (P) // peaceful-note{{Taking false branch}}994 clang_analyzer_warnIfReached(); // no-warning995 996 A *a = P.get(); // peaceful-warning {{Method called on moved-from object 'P'}}997 // peaceful-note@-1 {{Method called on moved-from object 'P'}}998 999 // TODO: Warn on a null dereference here.1000 a->foo();1001}1002 1003struct OtherMoveSafeClasses {1004 std::packaged_task<int(void)> Task;1005 1006 void test() {1007 // Test the suppression caused by use-after-move semantics of1008 // std::package_task being different from other standard classes.1009 // Only warn in aggressive mode. Don't say that the object1010 // is left in unspecified state after move.1011 std::packaged_task<int(void)> Task2 = std::move(Task);1012 // aggressive-note@-1 {{Object 'Task' is moved}}1013 std::packaged_task<int(void)> Task3 = std::move(Task);1014 // aggressive-warning@-1{{Moved-from object 'Task' is moved}}1015 // aggressive-note@-2 {{Moved-from object 'Task' is moved}}1016 }1017};1018