brintos

brintos / llvm-project-archived public Read only

0
0
Text · 32.7 KiB · 24d5dd8 Raw
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