// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\
// RUN:  -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
// RUN:  -analyzer-config exploration_strategy=unexplored_first_queue\
// RUN:  -analyzer-checker debug.ExprInspection
// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\
// RUN:  -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
// RUN:  -analyzer-config exploration_strategy=dfs -DDFS=1\
// RUN:  -analyzer-checker debug.ExprInspection
// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\
// RUN:  -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
// RUN:  -analyzer-config exploration_strategy=unexplored_first_queue\
// RUN:  -analyzer-config cplusplus.Move:WarnOn=KnownsOnly -DPEACEFUL\
// RUN:  -analyzer-checker debug.ExprInspection
// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\
// RUN:  -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
// RUN:  -analyzer-config exploration_strategy=dfs -DDFS=1\
// RUN:  -analyzer-config cplusplus.Move:WarnOn=KnownsOnly -DPEACEFUL\
// RUN:  -analyzer-checker debug.ExprInspection
// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\
// RUN:  -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
// RUN:  -analyzer-config exploration_strategy=unexplored_first_queue\
// RUN:  -analyzer-config cplusplus.Move:WarnOn=All -DAGGRESSIVE\
// RUN:  -analyzer-checker debug.ExprInspection
// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\
// RUN:  -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
// RUN:  -analyzer-config exploration_strategy=dfs -DDFS=1\
// RUN:  -analyzer-config cplusplus.Move:WarnOn=All -DAGGRESSIVE\
// RUN:  -analyzer-checker debug.ExprInspection

#include "Inputs/system-header-simulator-cxx.h"

void clang_analyzer_warnIfReached();

class B {
public:
  B() = default;
  B(const B &) = default;
  B(B &&) = default;
  B& operator=(const B &q) = default;
  void operator=(B &&b) {
    return;
  }
  void foo() { return; }
};

class A {
  int i;
  double d;

public:
  B b;
  A(int ii = 42, double dd = 1.0) : d(dd), i(ii), b(B()) {}
  void moveconstruct(A &&other) {
    std::swap(b, other.b);
    std::swap(d, other.d);
    std::swap(i, other.i);
    return;
  }
  static A get() {
    A v(12, 13);
    return v;
  }
  A(A *a) {
    moveconstruct(std::move(*a));
  }
  A(const A &other) : i(other.i), d(other.d), b(other.b) {}
  A(A &&other) : i(other.i), d(other.d), b(std::move(other.b)) {
#ifdef AGGRESSIVE
    // expected-note@-2{{Object 'b' is moved}}
#endif
  }
  A(A &&other, char *k) {
    moveconstruct(std::move(other));
  }
  void operator=(const A &other) {
    i = other.i;
    d = other.d;
    b = other.b;
    return;
  }
  void operator=(A &&other) {
    moveconstruct(std::move(other));
    return;
  }
  int getI() { return i; }
  int foo() const;
  void bar() const;
  void reset();
  void destroy();
  void clear();
  void resize(std::size_t);
  bool empty() const;
  bool isEmpty() const;
  operator bool() const;

  void testUpdateField() {
    A a;
    A b = std::move(a);
    a.i = 1;
    a.foo(); // no-warning
  }
  void testUpdateFieldDouble() {
    A a;
    A b = std::move(a);
    a.d = 1.0;
    a.foo(); // no-warning
  }
};

int bignum();

void moveInsideFunctionCall(A a) {
  A b = std::move(a);
}
void leftRefCall(A &a) {
  a.foo();
}
void rightRefCall(A &&a) {
  a.foo();
}
void constCopyOrMoveCall(const A a) {
  a.foo();
}

void copyOrMoveCall(A a) {
  a.foo();
}

void simpleMoveCtorTest() {
  {
    A a;
    A b = std::move(a);
    a.foo();
#ifndef PEACEFUL
    // expected-note@-3 {{Object 'a' is moved}}
    // expected-warning@-3 {{Method called on moved-from object 'a'}}
    // expected-note@-4    {{Method called on moved-from object 'a'}}
#endif
  }
  {
    A a;
    A b = std::move(a);
    b = a;
#ifndef PEACEFUL
    // expected-note@-3 {{Object 'a' is moved}}
    // expected-warning@-3 {{Moved-from object 'a' is copied}}
    // expected-note@-4    {{Moved-from object 'a' is copied}}
#endif
  }
  {
    A a;
    A b = std::move(a);
    b = std::move(a);
#ifndef PEACEFUL
    // expected-note@-3 {{Object 'a' is moved}}
    // expected-warning@-3 {{Moved-from object 'a' is moved}}
    // expected-note@-4    {{Moved-from object 'a' is moved}}
#endif
  }
}

void simpleMoveAssignementTest() {
  {
    A a;
    A b;
    b = std::move(a);
    a.foo();
#ifndef PEACEFUL
    // expected-note@-3 {{Object 'a' is moved}}
    // expected-warning@-3 {{Method called on moved-from object 'a'}}
    // expected-note@-4    {{Method called on moved-from object 'a'}}
#endif
  }
  {
    A a;
    A b;
    b = std::move(a);
    A c(a);
#ifndef PEACEFUL
    // expected-note@-3 {{Object 'a' is moved}}
    // expected-warning@-3 {{Moved-from object 'a' is copied}}
    // expected-note@-4    {{Moved-from object 'a' is copied}}
#endif
  }
  {
    A a;
    A b;
    b = std::move(a);
    A c(std::move(a));
#ifndef PEACEFUL
    // expected-note@-3 {{Object 'a' is moved}}
    // expected-warning@-3 {{Moved-from object 'a' is moved}}
    // expected-note@-4    {{Moved-from object 'a' is moved}}
#endif
  }
}

void moveInInitListTest() {
  struct S {
    A a;
  };
  A a;
  S s{std::move(a)};
  a.foo();
#ifndef PEACEFUL
  // expected-note@-3 {{Object 'a' is moved}}
  // expected-warning@-3 {{Method called on moved-from object 'a'}}
  // expected-note@-4 {{Method called on moved-from object 'a'}}
#endif
}

// Don't report a bug if the variable was assigned to in the meantime.
void reinitializationTest(int i) {
  {
    A a;
    A b;
    b = std::move(a);
    a = A();
    a.foo();
  }
  {
    A a;
    if (i == 1) {
#ifndef PEACEFUL
      // expected-note@-2 {{Assuming 'i' is not equal to 1}}
      // expected-note@-3 {{Taking false branch}}
      // And the other report:
      // expected-note@-5 {{Assuming 'i' is not equal to 1}}
      // expected-note@-6 {{Taking false branch}}
#endif
      A b;
      b = std::move(a);
      a = A();
    }
    if (i == 2) {
#ifndef PEACEFUL
      // expected-note@-2 {{Assuming 'i' is not equal to 2}}
      // expected-note@-3 {{Taking false branch}}
      // And the other report:
      // expected-note@-5 {{Assuming 'i' is not equal to 2}}
      // expected-note@-6 {{Taking false branch}}
#endif
      a.foo();    // no-warning
    }
  }
  {
    A a;
    if (i == 1) {
#ifndef PEACEFUL
      // expected-note@-2 {{Taking false branch}}
      // expected-note@-3 {{Taking false branch}}
#endif
      std::move(a);
    }
    if (i == 2) {
#ifndef PEACEFUL
      // expected-note@-2 {{Taking false branch}}
      // expected-note@-3 {{Taking false branch}}
#endif
      a = A();
      a.foo();
    }
  }
  // The built-in assignment operator should also be recognized as a
  // reinitialization. (std::move() may be called on built-in types in template
  // code.)
  {
    int a1 = 1, a2 = 2;
    std::swap(a1, a2);
  }
  // A std::move() after the assignment makes the variable invalid again.
  {
    A a;
    A b;
    b = std::move(a);
    a = A();
    b = std::move(a);
    a.foo();
#ifndef PEACEFUL
    // expected-note@-3 {{Object 'a' is moved}}
    // expected-warning@-3 {{Method called on moved-from object 'a'}}
    // expected-note@-4    {{Method called on moved-from object 'a'}}
#endif
  }
  // If a path exist where we not reinitialize the variable we report a bug.
  {
    A a;
    A b;
    b = std::move(a);
#ifndef PEACEFUL
    // expected-note@-2 {{Object 'a' is moved}}
#endif
    if (i < 10) {
#ifndef PEACEFUL
      // expected-note@-2 {{Assuming 'i' is >= 10}}
      // expected-note@-3 {{Taking false branch}}
#endif
      a = A();
    }
    if (i > 5) {
      a.foo();
#ifndef PEACEFUL
      // expected-note@-3 {{Taking true branch}}
      // expected-warning@-3 {{Method called on moved-from object 'a'}}
      // expected-note@-4    {{Method called on moved-from object 'a'}}
#endif
    }
  }
}

// Using decltype on an expression is not a use.
void decltypeIsNotUseTest() {
  A a;
  // A b(std::move(a));
  decltype(a) other_a; // no-warning
}

void loopTest() {
  {
    A a;
    for (int i = 0; i < bignum(); i++) {
#ifndef PEACEFUL
      // expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
#endif
      rightRefCall(std::move(a));        // no-warning
    }
  }
  {
    A a;
    for (int i = 0; i < 2; i++) {
#ifndef PEACEFUL
      // expected-note@-2 {{Loop condition is true.  Entering loop body}}
      // expected-note@-3 {{Loop condition is true.  Entering loop body}}
      // expected-note@-4 {{Loop condition is false. Execution jumps to the end of the function}}
#endif
      rightRefCall(std::move(a)); // no-warning
    }
  }
  {
    A a;
    for (int i = 0; i < bignum(); i++) {
#ifndef PEACEFUL
      // expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
#endif
      leftRefCall(a);                    // no-warning
    }
  }
  {
    A a;
    for (int i = 0; i < 2; i++) {
#ifndef PEACEFUL
      // expected-note@-2 {{Loop condition is true.  Entering loop body}}
      // expected-note@-3 {{Loop condition is true.  Entering loop body}}
      // expected-note@-4 {{Loop condition is false. Execution jumps to the end of the function}}
#endif
      leftRefCall(a);             // no-warning
    }
  }
  {
    A a;
    for (int i = 0; i < bignum(); i++) {
#ifndef PEACEFUL
      // expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
#endif
      constCopyOrMoveCall(a);            // no-warning
    }
  }
  {
    A a;
    for (int i = 0; i < 2; i++) {
#ifndef PEACEFUL
      // expected-note@-2 {{Loop condition is true.  Entering loop body}}
      // expected-note@-3 {{Loop condition is true.  Entering loop body}}
      // expected-note@-4 {{Loop condition is false. Execution jumps to the end of the function}}
#endif
      constCopyOrMoveCall(a);     // no-warning
    }
  }
  {
    A a;
    for (int i = 0; i < bignum(); i++) {
#ifndef PEACEFUL
      // expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
#endif
      moveInsideFunctionCall(a);         // no-warning
    }
  }
  {
    A a;
    for (int i = 0; i < 2; i++) {
#ifndef PEACEFUL
      // expected-note@-2 {{Loop condition is true.  Entering loop body}}
      // expected-note@-3 {{Loop condition is true.  Entering loop body}}
      // expected-note@-4 {{Loop condition is false. Execution jumps to the end of the function}}
#endif
      moveInsideFunctionCall(a);  // no-warning
    }
  }
  {
    A a;
    for (int i = 0; i < bignum(); i++) {
#ifndef PEACEFUL
      // expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
#endif
      copyOrMoveCall(a);                 // no-warning
    }
  }
  {
    A a;
    for (int i = 0; i < 2; i++) {
#ifndef PEACEFUL
      // expected-note@-2 {{Loop condition is true.  Entering loop body}}
      // expected-note@-3 {{Loop condition is true.  Entering loop body}}
      // expected-note@-4 {{Loop condition is false. Execution jumps to the end of the function}}
#endif
      copyOrMoveCall(a);          // no-warning
    }
  }
  {
    A a;
    for (int i = 0; i < bignum(); i++) {
#ifndef PEACEFUL
      // expected-note@-2 {{Loop condition is true.  Entering loop body}}
      // expected-note@-3 {{Loop condition is true.  Entering loop body}}
#endif
      constCopyOrMoveCall(std::move(a));
#ifndef PEACEFUL
      // expected-note@-2    {{Object 'a' is moved}}
      // expected-warning@-3 {{Moved-from object 'a' is moved}}
      // expected-note@-4 {{Moved-from object 'a' is moved}}
#endif
    }
  }

  // Don't warn if we return after the move.
  {
    A a;
    for (int i = 0; i < 3; ++i) {
      a.bar();
      if (a.foo() > 0) {
        A b;
        b = std::move(a); // no-warning
        return;
      }
    }
  }
}

// Report a usage of a moved-from object only at the first use.
void uniqueTest(bool cond) {
  A a(42, 42.0);
  A b;
  b = std::move(a);

  if (cond) {
    a.foo();
#ifndef PEACEFUL
  // expected-note@-5 {{Object 'a' is moved}}
  // expected-note@-4 {{Assuming 'cond' is not equal to 0}}
  // expected-note@-5 {{Taking true branch}}
  // expected-warning@-5 {{Method called on moved-from object 'a'}}
  // expected-note@-6    {{Method called on moved-from object 'a'}}
#endif
  }
  if (cond) {
    a.bar(); // no-warning
  }

  a.bar(); // no-warning
}

void uniqueTest2() {
  A a;
  A a1 = std::move(a);
  a.foo();
#ifndef PEACEFUL
  // expected-note@-3 {{Object 'a' is moved}}
  // expected-warning@-3 {{Method called on moved-from object 'a'}}
  // expected-note@-4    {{Method called on moved-from object 'a'}}
#endif

  A a2 = std::move(a); // no-warning
  a.foo();             // no-warning
}

// There are exceptions where we assume in general that the method works fine
//even on moved-from objects.
void moveSafeFunctionsTest() {
  A a;
  A b = std::move(a);
#ifndef PEACEFUL
  // expected-note@-2 {{Object 'a' is moved}}
#endif
  a.empty();          // no-warning
  a.isEmpty();        // no-warning
  (void)a;            // no-warning
  (bool)a;            // expected-warning {{expression result unused}}
  a.foo();
#ifndef PEACEFUL
  // expected-warning@-2 {{Method called on moved-from object 'a'}}
  // expected-note@-3    {{Method called on moved-from object 'a'}}
#endif
}

void moveStateResetFunctionsTest() {
  {
    A a;
    A b = std::move(a);
    a.reset(); // no-warning
    a.foo();   // no-warning
    // Test if resets the state of subregions as well.
    a.b.foo(); // no-warning
  }
  {
    A a;
    A b = std::move(a);
    a.destroy(); // no-warning
    a.foo();     // no-warning
  }
  {
    A a;
    A b = std::move(a);
    a.clear(); // no-warning
    a.foo();   // no-warning
    a.b.foo(); // no-warning
  }
  {
    A a;
    A b = std::move(a);
    a.resize(0); // no-warning
    a.foo();   // no-warning
    a.b.foo(); // no-warning
  }
}

// Moves or uses that occur as part of template arguments.
template <int>
class ClassTemplate {
public:
  void foo(A a);
};

template <int>
void functionTemplate(A a);

void templateArgIsNotUseTest() {
  {
    // A pattern like this occurs in the EXPECT_EQ and ASSERT_EQ macros in
    // Google Test.
    A a;
    ClassTemplate<sizeof(A(std::move(a)))>().foo(std::move(a)); // no-warning
  }
  {
    A a;
    functionTemplate<sizeof(A(std::move(a)))>(std::move(a)); // no-warning
  }
}

// Moves of global variables are not reported.
A global_a;
void globalVariablesTest() {
  std::move(global_a);
  global_a.foo(); // no-warning
}

// Moves of member variables.
class memberVariablesTest {
  A a;
  static A static_a;

  void f() {
    A b;
    b = std::move(a);
    a.foo();
#ifdef AGGRESSIVE
    // expected-note@-3{{Object 'a' is moved}}
    // expected-warning@-3 {{Method called on moved-from object 'a'}}
    // expected-note@-4{{Method called on moved-from object 'a'}}
#endif

    b = std::move(static_a);
    static_a.foo();
#ifdef AGGRESSIVE
    // expected-note@-3{{Object 'static_a' is moved}}
    // expected-warning@-3{{Method called on moved-from object 'static_a'}}
    // expected-note@-4{{Method called on moved-from object 'static_a'}}
#endif
  }
};

void PtrAndArrayTest() {
  A *Ptr = new A(1, 1.5);
  A Arr[10];
  Arr[2] = std::move(*Ptr);
  (*Ptr).foo();
#ifdef AGGRESSIVE
  // expected-note@-3{{Object is moved}}
  // expected-warning@-3{{Method called on moved-from object}}
  // expected-note@-4{{Method called on moved-from object}}
#endif

  Ptr = &Arr[1];
  Arr[3] = std::move(Arr[1]);
  Ptr->foo();
#ifdef AGGRESSIVE
  // expected-note@-3{{Object is moved}}
  // expected-warning@-3{{Method called on moved-from object}}
  // expected-note@-4{{Method called on moved-from object}}
#endif

  Arr[3] = std::move(Arr[2]);
  Arr[2].foo();
#ifdef AGGRESSIVE
  // expected-note@-3{{Object is moved}}
  // expected-warning@-3{{Method called on moved-from object}}
  // expected-note@-4{{Method called on moved-from object}}
#endif

  Arr[2] = std::move(Arr[3]); // reinitialization
  Arr[2].foo();               // no-warning
}

void exclusiveConditionsTest(bool cond) {
  A a;
  if (cond) {
    A b;
    b = std::move(a);
  }
  if (!cond) {
    a.bar(); // no-warning
  }
}

void differentBranchesTest(int i) {
  // Don't warn if the use is in a different branch from the move.
  {
    A a;
    if (i > 0) {
#ifndef PEACEFUL
    // expected-note@-2 {{Assuming 'i' is > 0}}
    // expected-note@-3 {{Taking true branch}}
#endif
      A b;
      b = std::move(a);
    } else {
      a.foo(); // no-warning
    }
  }
  // Same thing, but with a ternary operator.
  {
    A a, b;
    i > 0 ? (void)(b = std::move(a)) : a.bar(); // no-warning
#ifndef PEACEFUL
    // expected-note@-2 {{'?' condition is true}}
#endif
  }
  // A variation on the theme above.
  {
    A a;
    a.foo() > 0 ? a.foo() : A(std::move(a)).foo();
#ifdef DFS
  #ifndef PEACEFUL
    // expected-note@-3 {{Assuming the condition is false}}
    // expected-note@-4 {{'?' condition is false}}
  #endif
#else
  #ifndef PEACEFUL
    // expected-note@-8 {{Assuming the condition is true}}
    // expected-note@-9 {{'?' condition is true}}
  #endif
#endif
  }
  // Same thing, but with a switch statement.
  {
    A a, b;
    switch (i) {
#ifndef PEACEFUL
    // expected-note@-2 {{Control jumps to 'case 1:'}}
#endif
    case 1:
      b = std::move(a); // no-warning
      break;
#ifndef PEACEFUL
      // expected-note@-2 {{Execution jumps to the end of the function}}
#endif
    case 2:
      a.foo(); // no-warning
      break;
    }
  }
  // However, if there's a fallthrough, we do warn.
  {
    A a, b;
    switch (i) {
#ifndef PEACEFUL
    // expected-note@-2 {{Control jumps to 'case 1:'}}
#endif
    case 1:
      b = std::move(a);
#ifndef PEACEFUL
      // expected-note@-2 {{Object 'a' is moved}}
#endif
    case 2:
      a.foo();
#ifndef PEACEFUL
      // expected-warning@-2 {{Method called on moved-from object}}
      // expected-note@-3    {{Method called on moved-from object 'a'}}
#endif
      break;
    }
  }
}

void tempTest() {
  A a = A::get();
  A::get().foo(); // no-warning
  for (int i = 0; i < bignum(); i++) {
    A::get().foo(); // no-warning
  }
}

void interFunTest1(A &a) {
  a.bar();
#ifndef PEACEFUL
  // expected-warning@-2 {{Method called on moved-from object 'a'}}
  // expected-note@-3    {{Method called on moved-from object 'a'}}
#endif
}

void interFunTest2() {
  A a;
  A b;
  b = std::move(a);
  interFunTest1(a);
#ifndef PEACEFUL
  // expected-note@-3 {{Object 'a' is moved}}
  // expected-note@-3 {{Calling 'interFunTest1'}}
#endif
}

void foobar(A a, int i);
void foobar(int i, A a);

void paramEvaluateOrderTest() {
  A a;
  foobar(std::move(a), a.getI());
#ifndef PEACEFUL
  // expected-note@-2 {{Object 'a' is moved}}
  // expected-warning@-3 {{Method called on moved-from object 'a'}}
  // expected-note@-4    {{Method called on moved-from object 'a'}}
#endif

  //FALSE NEGATIVE since parameters evaluate order is undefined
  foobar(a.getI(), std::move(a)); //no-warning
}

void not_known_pass_by_ref(A &a);
void not_known_pass_by_const_ref(const A &a);
void not_known_pass_by_rvalue_ref(A &&a);
void not_known_pass_by_ptr(A *a);
void not_known_pass_by_const_ptr(const A *a);

void regionAndPointerEscapeTest() {
  {
    A a;
    A b;
    b = std::move(a);
    not_known_pass_by_ref(a);
    a.foo(); // no-warning
  }
  {
    A a;
    A b;
    b = std::move(a);
    not_known_pass_by_const_ref(a);
    a.foo();
#ifndef PEACEFUL
    // expected-note@-4{{Object 'a' is moved}}
    // expected-warning@-3{{Method called on moved-from object 'a'}}
    // expected-note@-4   {{Method called on moved-from object 'a'}}
#endif
  }
  {
    A a;
    A b;
    b = std::move(a);
    not_known_pass_by_rvalue_ref(std::move(a));
    a.foo(); // no-warning
  }
  {
    A a;
    A b;
    b = std::move(a);
    not_known_pass_by_ptr(&a);
    a.foo(); // no-warning
  }
  {
    A a;
    A b;
    b = std::move(a);
    not_known_pass_by_const_ptr(&a);
    a.foo();
#ifndef PEACEFUL
    // expected-note@-4{{Object 'a' is moved}}
    // expected-warning@-3{{Method called on moved-from object 'a'}}
    // expected-note@-4   {{Method called on moved-from object 'a'}}
#endif
  }
}

// A declaration statement containing multiple declarations sequences the
// initializer expressions.
void declarationSequenceTest() {
  {
    A a;
    A a1 = a, a2 = std::move(a); // no-warning
  }
  {
    A a;
    A a1 = std::move(a), a2 = a;
#ifndef PEACEFUL
    // expected-note@-2 {{Object 'a' is moved}}
    // expected-warning@-3 {{Moved-from object 'a' is copied}}
    // expected-note@-4    {{Moved-from object 'a' is copied}}
#endif
  }
}

// The logical operators && and || sequence their operands.
void logicalOperatorsSequenceTest() {
  {
    A a;
    if (a.foo() > 0 && A(std::move(a)).foo() > 0) {
#ifndef PEACEFUL
      // expected-note@-2 {{Assuming the condition is false}}
      // expected-note@-3 {{Left side of '&&' is false}}
      // expected-note@-4 {{Taking false branch}}
      // And the other report:
      // expected-note@-6 {{Assuming the condition is false}}
      // expected-note@-7 {{Left side of '&&' is false}}
      // expected-note@-8 {{Taking false branch}}
      A().bar();
#endif
    }
  }
  // A variation: Negate the result of the && (which pushes the && further down
  // into the AST).
  {
    A a;
    if (!(a.foo() > 0 && A(std::move(a)).foo() > 0)) {
#ifndef PEACEFUL
      // expected-note@-2 {{Assuming the condition is false}}
      // expected-note@-3 {{Left side of '&&' is false}}
      // expected-note@-4 {{Taking true branch}}
      // And the other report:
      // expected-note@-6 {{Assuming the condition is false}}
      // expected-note@-7 {{Left side of '&&' is false}}
      // expected-note@-8 {{Taking true branch}}
#endif
      A().bar();
    }
  }
  {
    A a;
    if (A(std::move(a)).foo() > 0 && a.foo() > 0) {
#ifndef PEACEFUL
      // expected-note@-2 {{Object 'a' is moved}}
      // expected-note@-3 {{Assuming the condition is true}}
      // expected-note@-4 {{Left side of '&&' is true}}
      // expected-warning@-5 {{Method called on moved-from object 'a'}}
      // expected-note@-6    {{Method called on moved-from object 'a'}}
      // And the other report:
      // expected-note@-8 {{Assuming the condition is false}}
      // expected-note@-9 {{Left side of '&&' is false}}
      // expected-note@-10{{Taking false branch}}
#endif
      A().bar();
    }
  }
  {
    A a;
    if (a.foo() > 0 || A(std::move(a)).foo() > 0) {
#ifndef PEACEFUL
      // expected-note@-2 {{Assuming the condition is true}}
      // expected-note@-3 {{Left side of '||' is true}}
      // expected-note@-4 {{Taking true branch}}
#endif
      A().bar();
    }
  }
  {
    A a;
    if (A(std::move(a)).foo() > 0 || a.foo() > 0) {
#ifndef PEACEFUL
      // expected-note@-2 {{Object 'a' is moved}}
      // expected-note@-3 {{Assuming the condition is false}}
      // expected-note@-4 {{Left side of '||' is false}}
      // expected-warning@-5 {{Method called on moved-from object 'a'}}
      // expected-note@-6    {{Method called on moved-from object 'a'}}
#endif
      A().bar();
    }
  }
}

// A range-based for sequences the loop variable declaration before the body.
void forRangeSequencesTest() {
  A v[2] = {A(), A()};
  for (A &a : v) {
    A b;
    b = std::move(a); // no-warning
  }
}

// If a variable is declared in an if statement, the declaration of the variable
// (which is treated like a reinitialization by the check) is sequenced before
// the evaluation of the condition (which constitutes a use).
void ifStmtSequencesDeclAndConditionTest() {
  for (int i = 0; i < 3; ++i) {
    if (A a = A()) {
      A b;
      b = std::move(a); // no-warning
    }
  }
}

struct C : public A {
  [[clang::reinitializes]] void reinit();
};

void subRegionMoveTest() {
  {
    A a;
    B b = std::move(a.b);
    a.b.foo();
#ifdef AGGRESSIVE
    // expected-note@-3{{Object 'b' is moved}}
    // expected-warning@-3{{Method called on moved-from object 'b'}}
    // expected-note@-4 {{Method called on moved-from object 'b'}}
#endif
  }
  {
    A a;
    A a1 = std::move(a);
    a.b.foo();
#ifdef AGGRESSIVE
    // expected-note@-3{{Calling move constructor for 'A'}}
    // expected-note@-4{{Returning from move constructor for 'A'}}
    // expected-warning@-4{{Method called on moved-from object 'b'}}
    // expected-note@-5{{Method called on moved-from object 'b'}}
#endif
  }
  // Don't report a misuse if any SuperRegion is already reported.
  {
    A a;
    A a1 = std::move(a);
    a.foo();
#ifndef PEACEFUL
    // expected-note@-3 {{Object 'a' is moved}}
    // expected-warning@-3 {{Method called on moved-from object 'a'}}
    // expected-note@-4    {{Method called on moved-from object 'a'}}
#endif
    a.b.foo();           // no-warning
  }
  {
    C c;
    C c1 = std::move(c);
    c.foo();
#ifndef PEACEFUL
    // expected-note@-3 {{Object 'c' is moved}}
    // expected-warning@-3 {{Method called on moved-from object 'c'}}
    // expected-note@-4    {{Method called on moved-from object 'c'}}
#endif
    c.b.foo();           // no-warning
  }
}

void resetSuperClass() {
  C c;
  C c1 = std::move(c);
  c.clear();
  C c2 = c; // no-warning
}

void resetSuperClass2() {
  C c;
  C c1 = std::move(c);
  c.reinit();
  C c2 = c; // no-warning
}

void reportSuperClass() {
  C c;
  C c1 = std::move(c);
  c.foo();
#ifndef PEACEFUL
  // expected-note@-3 {{Object 'c' is moved}}
  // expected-warning@-3 {{Method called on moved-from object 'c'}}
  // expected-note@-4    {{Method called on moved-from object 'c'}}
#endif
  C c2 = c;            // no-warning
}

struct Empty {};

Empty inlinedCall() {
  // Used to warn because region 'e' failed to be cleaned up because no symbols
  // have ever died during the analysis and the checkDeadSymbols callback
  // was skipped entirely.
  Empty e{};
  return e; // no-warning
}

void checkInlinedCallZombies() {
  while (true)
    inlinedCall();
}

void checkLoopZombies() {
  while (true) {
    Empty e{};
    Empty f = std::move(e); // no-warning
  }
}

void checkMoreLoopZombies1(bool flag) {
  while (flag) {
    Empty e{};
    if (true)
      e; // expected-warning {{expression result unused}}
    Empty f = std::move(e); // no-warning
  }
}

bool coin();

void checkMoreLoopZombies2(bool flag) {
  while (flag) {
    Empty e{};
    while (coin())
      e; // expected-warning {{expression result unused}}
    Empty f = std::move(e); // no-warning
  }
}

void checkMoreLoopZombies3(bool flag) {
  while (flag) {
    Empty e{};
    do
      e; // expected-warning {{expression result unused}}
    while (coin());
    Empty f = std::move(e); // no-warning
  }
}

void checkMoreLoopZombies4(bool flag) {
  while (flag) {
    Empty e{};
    for (; coin();)
      e; // expected-warning {{expression result unused}}
    Empty f = std::move(e); // no-warning
  }
}

struct MoveOnlyWithDestructor {
  MoveOnlyWithDestructor();
  ~MoveOnlyWithDestructor();
  MoveOnlyWithDestructor(const MoveOnlyWithDestructor &m) = delete;
  MoveOnlyWithDestructor(MoveOnlyWithDestructor &&m);
};

MoveOnlyWithDestructor foo() {
  MoveOnlyWithDestructor m;
  return m;
}

class HasSTLField {
  std::vector<int> V;
  void testVector() {
    // Warn even in non-aggressive mode when it comes to STL, because
    // in STL the object is left in "valid but unspecified state" after move.
    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}}
    V.push_back(123); // expected-warning{{Method called on moved-from object 'V'}}
                      // expected-note@-1{{Method called on moved-from object 'V'}}
  }

  std::unique_ptr<int> P;
  void testUniquePtr() {
    // unique_ptr remains in a well-defined state after move.
    std::unique_ptr<int> Q = std::move(P);
    P.get();
#ifdef AGGRESSIVE
    // expected-warning@-2{{Method called on moved-from object 'P'}}
    // expected-note@-4{{Object 'P' is moved}}
    // expected-note@-4{{Method called on moved-from object 'P'}}
#endif

    // Because that well-defined state is null, dereference is still UB.
    // Note that in aggressive mode we already warned about 'P',
    // so no extra warning is generated.
    *P += 1;
#ifndef AGGRESSIVE
    // expected-warning@-2{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}}
    // expected-note@-14{{Smart pointer 'P' of type 'std::unique_ptr' is reset to null when moved from}}
    // expected-note@-4{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}}
#endif

    // The program should have crashed by now.
    clang_analyzer_warnIfReached(); // no-warning
  }
};

void localRValueMove(A &&a) {
  A b = std::move(a);
  a.foo();
#ifndef PEACEFUL
  // expected-note@-3 {{Object 'a' is moved}}
  // expected-warning@-3 {{Method called on moved-from object 'a'}}
  // expected-note@-4    {{Method called on moved-from object 'a'}}
#endif
}

void localUniquePtr(std::unique_ptr<int> P) {
  // Even though unique_ptr is safe to use after move,
  // reusing a local variable this way usually indicates a bug.
  std::unique_ptr<int> Q = std::move(P);
  P.get();
#ifndef PEACEFUL
  // expected-note@-3 {{Object 'P' is moved}}
  // expected-warning@-3 {{Method called on moved-from object 'P'}}
  // expected-note@-4    {{Method called on moved-from object 'P'}}
#endif
}

void localUniquePtrWithArrow(std::unique_ptr<A> P) {
  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}}
  P->foo(); // expected-warning{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}}
            // expected-note@-1{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}}
}
