// RUN: %clang_analyze_cc1 -w -analyzer-checker=core,cplusplus\
// RUN:     -analyzer-checker debug.ExprInspection -Wno-non-pod-varargs\
// RUN:     -analyzer-config eagerly-assume=false -verify %s\
// RUN:     -std=c++03 -analyzer-config cfg-temporary-dtors=false

// RUN: %clang_analyze_cc1 -w -analyzer-checker=core,cplusplus\
// RUN:     -analyzer-checker debug.ExprInspection -Wno-non-pod-varargs\
// RUN:     -analyzer-config eagerly-assume=false -verify %s\
// RUN:     -std=c++11 -analyzer-config cfg-temporary-dtors=false

// RUN: %clang_analyze_cc1 -w -analyzer-checker=core,cplusplus\
// RUN:     -analyzer-checker debug.ExprInspection -Wno-non-pod-varargs\
// RUN:     -analyzer-config eagerly-assume=false -verify %s\
// RUN:     -std=c++11 -analyzer-config cfg-temporary-dtors=true\
// RUN:     -DTEMPORARY_DTORS

// RUN: %clang_analyze_cc1 -w -analyzer-checker=core,cplusplus\
// RUN:     -analyzer-checker debug.ExprInspection -Wno-non-pod-varargs\
// RUN:     -analyzer-config eagerly-assume=false -verify %s\
// RUN:     -std=c++17 -analyzer-config cfg-temporary-dtors=true\
// RUN:     -DTEMPORARY_DTORS


extern bool clang_analyzer_eval(bool);
extern bool clang_analyzer_warnIfReached();
void clang_analyzer_checkInlined(bool);

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

struct Trivial {
  Trivial(int x) : value(x) {}
  int value;
};

struct NonTrivial : public Trivial {
  NonTrivial(int x) : Trivial(x) {}
  ~NonTrivial();
};


Trivial getTrivial() {
  return Trivial(42); // no-warning
}

const Trivial &getTrivialRef() {
  return Trivial(42); // expected-warning {{Address of stack memory associated with temporary object of type 'Trivial' returned to caller}}
}


NonTrivial getNonTrivial() {
  return NonTrivial(42); // no-warning
}

const NonTrivial &getNonTrivialRef() {
  return NonTrivial(42); // expected-warning {{Address of stack memory associated with temporary object of type 'NonTrivial' returned to caller}}
}

namespace rdar13265460 {
  struct TrivialSubclass : public Trivial {
    TrivialSubclass(int x) : Trivial(x), anotherValue(-x) {}
    int anotherValue;
  };

  TrivialSubclass getTrivialSub() {
    TrivialSubclass obj(1);
    obj.value = 42;
    obj.anotherValue = -42;
    return obj;
  }

  void testImmediate() {
    TrivialSubclass obj = getTrivialSub();

    clang_analyzer_eval(obj.value == 42); // expected-warning{{TRUE}}
    clang_analyzer_eval(obj.anotherValue == -42); // expected-warning{{TRUE}}

    clang_analyzer_eval(getTrivialSub().value == 42); // expected-warning{{TRUE}}
    clang_analyzer_eval(getTrivialSub().anotherValue == -42); // expected-warning{{TRUE}}
  }

  void testMaterializeTemporaryExpr() {
    const TrivialSubclass &ref = getTrivialSub();
    clang_analyzer_eval(ref.value == 42); // expected-warning{{TRUE}}

    const Trivial &baseRef = getTrivialSub();
    clang_analyzer_eval(baseRef.value == 42); // expected-warning{{TRUE}}
  }
}

namespace rdar13281951 {
  struct Derived : public Trivial {
    Derived(int value) : Trivial(value), value2(-value) {}
    int value2;
  };

  void test() {
    Derived obj(1);
    obj.value = 42;
    const Trivial * const &pointerRef = &obj;
    clang_analyzer_eval(pointerRef->value == 42); // expected-warning{{TRUE}}
  }
}

namespace compound_literals {
  struct POD {
    int x, y;
  };
  struct HasCtor {
    HasCtor(int x, int y) : x(x), y(y) {}
    int x, y;
  };
  struct HasDtor {
    int x, y;
    ~HasDtor();
  };
  struct HasCtorDtor {
    HasCtorDtor(int x, int y) : x(x), y(y) {}
    ~HasCtorDtor();
    int x, y;
  };

  void test() {
    clang_analyzer_eval(((POD){1, 42}).y == 42); // expected-warning{{TRUE}}
    clang_analyzer_eval(((HasDtor){1, 42}).y == 42); // expected-warning{{TRUE}}

#if __cplusplus >= 201103L
    clang_analyzer_eval(((HasCtor){1, 42}).y == 42); // expected-warning{{TRUE}}

    // FIXME: should be TRUE, but we don't inline the constructors of
    // temporaries because we can't model their destructors yet.
    clang_analyzer_eval(((HasCtorDtor){1, 42}).y == 42); // expected-warning{{UNKNOWN}}
#endif
  }
}

namespace destructors {
  struct Dtor {
    ~Dtor();
  };
  extern bool coin();
  extern bool check(const Dtor &);

  void testPR16664andPR18159Crash() {
    // Regression test: we used to assert here when tmp dtors are enabled.
    // PR16664 and PR18159
    if (coin() && (coin() || coin() || check(Dtor()))) {
      Dtor();
    }
  }

#ifdef TEMPORARY_DTORS
  struct NoReturnDtor {
    ~NoReturnDtor() __attribute__((noreturn));
  };

  void noReturnTemp(int *x) {
    if (! x) NoReturnDtor();
    *x = 47; // no warning
  }

  void noReturnInline(int **x) {
    NoReturnDtor();
  }

  void callNoReturn() {
    int *x;
    noReturnInline(&x);
    *x = 47; // no warning
  }

  extern bool check(const NoReturnDtor &);

  void testConsistencyIf(int i) {
    if (i != 5)
      return;
    if (i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) {
      clang_analyzer_eval(true); // no warning, unreachable code
    }
  }

  void testConsistencyTernary(int i) {
    (i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) ? 1 : 0;

    clang_analyzer_eval(true);  // expected-warning{{TRUE}}

    if (i != 5)
      return;

    (i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) ? 1 : 0;

    clang_analyzer_eval(true); // no warning, unreachable code
  }

  // Regression test: we used to assert here.
  // PR16664 and PR18159
  void testConsistencyNested(int i) {
    extern bool compute(bool);

    if (i == 5 && (i == 4 || i == 5 || check(NoReturnDtor())))
      clang_analyzer_eval(true);  // expected-warning{{TRUE}}

    if (i == 5 && (i == 4 || i == 5 || check(NoReturnDtor())))
      clang_analyzer_eval(true);  // expected-warning{{TRUE}}

    if (i != 5)
      return;

    if (compute(i == 5 &&
                (i == 4 || compute(true) ||
                 compute(i == 5 && (i == 4 || check(NoReturnDtor()))))) ||
        i != 4) {
      clang_analyzer_eval(true);  // expected-warning{{TRUE}}
    }

    if (compute(i == 5 &&
                (i == 4 || i == 4 ||
                 compute(i == 5 && (i == 4 || check(NoReturnDtor()))))) ||
        i != 4) {
      clang_analyzer_eval(true);  // no warning, unreachable code
    }
  }

  // PR16664 and PR18159
  void testConsistencyNestedSimple(bool value) {
    if (value) {
      if (!value || check(NoReturnDtor())) {
        clang_analyzer_eval(true); // no warning, unreachable code
      }
    }
  }

  // PR16664 and PR18159
  void testConsistencyNestedComplex(bool value) {
    if (value) {
      if (!value || !value || check(NoReturnDtor())) {
        clang_analyzer_eval(true);  // no warning, unreachable code
      }
    }
  }

  // PR16664 and PR18159
  void testConsistencyNestedWarning(bool value) {
    if (value) {
      if (!value || value || check(NoReturnDtor())) {
        clang_analyzer_eval(true); // expected-warning{{TRUE}}
      }
    }
  }
  // PR16664 and PR18159
  void testConsistencyNestedComplexMidBranch(bool value) {
    if (value) {
      if (!value || !value || check(NoReturnDtor()) || value) {
        clang_analyzer_eval(true);  // no warning, unreachable code
      }
    }
  }

  // PR16664 and PR18159
  void testConsistencyNestedComplexNestedBranch(bool value) {
    if (value) {
      if (!value || (!value || check(NoReturnDtor()) || value)) {
        clang_analyzer_eval(true);  // no warning, unreachable code
      }
    }
  }

  // PR16664 and PR18159
  void testConsistencyNestedVariableModification(bool value) {
    bool other = true;
    if (value) {
      if (!other || !value || (other = false) || check(NoReturnDtor()) ||
          !other) {
        clang_analyzer_eval(true);  // no warning, unreachable code
      }
    }
  }

  void testTernaryNoReturnTrueBranch(bool value) {
    if (value) {
      bool b = value && (value ? check(NoReturnDtor()) : true);
      clang_analyzer_eval(true);  // no warning, unreachable code
    }
  }
  void testTernaryNoReturnFalseBranch(bool value) {
    if (value) {
      bool b = !value && !value ? true : check(NoReturnDtor());
      clang_analyzer_eval(true);  // no warning, unreachable code
    }
  }
  void testTernaryIgnoreNoreturnBranch(bool value) {
    if (value) {
      bool b = !value && !value ? check(NoReturnDtor()) : true;
      clang_analyzer_eval(true);  // expected-warning{{TRUE}}
    }
  }
  void testTernaryTrueBranchReached(bool value) {
    value ? clang_analyzer_warnIfReached() : // expected-warning{{REACHABLE}}
            check(NoReturnDtor());
  }
  void testTernaryFalseBranchReached(bool value) {
    value ? check(NoReturnDtor()) :
            clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
  }

  void testLoop() {
    for (int i = 0; i < 10; ++i) {
      if (i < 3 && (i >= 2 || check(NoReturnDtor()))) {
        clang_analyzer_eval(true);  // no warning, unreachable code
      }
    }
  }

  bool testRecursiveFrames(bool isInner) {
    if (isInner ||
        (clang_analyzer_warnIfReached(), false) || // expected-warning{{REACHABLE}}
        check(NoReturnDtor()) ||
        testRecursiveFrames(true)) {
      clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
    }
  }
  void testRecursiveFramesStart() { testRecursiveFrames(false); }

  void testLambdas() {
    []() { check(NoReturnDtor()); } != nullptr || check(Dtor());
  }

  void testGnuExpressionStatements(int v) {
    ({ ++v; v == 10 || check(NoReturnDtor()); v == 42; }) || v == 23;
    clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}

    ({ ++v; check(NoReturnDtor()); v == 42; }) || v == 23;
    clang_analyzer_warnIfReached();  // no warning, unreachable code
  }

  void testGnuExpressionStatementsDestructionPoint(int v) {
    // In normal context, the temporary destructor runs at the end of the full
    // statement, thus the last statement is reached.
    (++v, check(NoReturnDtor()), v == 42),
        clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}

    // GNU expression statements execute temporary destructors within the
    // blocks, thus the last statement is not reached.
    ({ ++v; check(NoReturnDtor()); v == 42; }),
        clang_analyzer_warnIfReached();  // no warning, unreachable code
  }

  void testMultipleTemporaries(bool value) {
    if (value) {
      // FIXME: Find a way to verify construction order.
      // ~Dtor should run before ~NoReturnDtor() because construction order is
      // guaranteed by comma operator.
      if (!value || check((NoReturnDtor(), Dtor())) || value) {
        clang_analyzer_eval(true);  // no warning, unreachable code
      }
    }
  }

  void testBinaryOperatorShortcut(bool value) {
    if (value) {
      if (false && false && check(NoReturnDtor()) && true) {
        clang_analyzer_eval(true);
      }
    }
  }

  void testIfAtEndOfLoop() {
    int y = 0;
    while (true) {
      if (y > 0) {
        clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
      }
      ++y;
      // Test that the CFG gets hooked up correctly when temporary destructors
      // are handled after a statically known branch condition.
      if (true) (void)0; else (void)check(NoReturnDtor());
    }
  }

  void testTernaryAtEndOfLoop() {
    int y = 0;
    while (true) {
      if (y > 0) {
        clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
      }
      ++y;
      // Test that the CFG gets hooked up correctly when temporary destructors
      // are handled after a statically known branch condition.
      true ? (void)0 : (void)check(NoReturnDtor());
    }
  }

  void testNoReturnInComplexCondition() {
    check(Dtor()) &&
        (check(NoReturnDtor()) || check(NoReturnDtor())) && check(Dtor());
    clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
  }

  void testSequencingOfConditionalTempDtors(bool b) {
    b || (check(Dtor()), check(NoReturnDtor()));
    clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
  }

  void testSequencingOfConditionalTempDtors2(bool b) {
    (b || check(Dtor())), check(NoReturnDtor());
    clang_analyzer_warnIfReached();  // no warning, unreachable code
  }

  void testSequencingOfConditionalTempDtorsWithinBinaryOperators(bool b) {
    b || (check(Dtor()) + check(NoReturnDtor()));
    clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
  }

  void f(Dtor d = Dtor());
  void testDefaultParameters() {
    f();
  }

  struct DefaultParam {
    DefaultParam(int, const Dtor& d = Dtor());
    ~DefaultParam();
  };
  void testDefaultParamConstructorsInLoops() {
    while (true) {
      // FIXME: This exact pattern triggers the temporary cleanup logic
      // to fail when adding a 'clean' state.
      DefaultParam(42);
      DefaultParam(42);
    }
  }
  void testDefaultParamConstructorsInTernariesInLoops(bool value) {
    while (true) {
      // FIXME: This exact pattern triggers the temporary cleanup logic
      // to visit the bind-temporary logic with a state that already has that
      // temporary marked as executed.
      value ? DefaultParam(42) : DefaultParam(42);
    }
  }
#else // !TEMPORARY_DTORS

// Test for fallback logic that conservatively stops exploration after
// executing a temporary constructor for a class with a no-return destructor
// when temporary destructors are not enabled in the CFG.

  struct CtorWithNoReturnDtor {
    CtorWithNoReturnDtor() = default;

    CtorWithNoReturnDtor(int x) {
      clang_analyzer_checkInlined(false); // no-warning
    }

    ~CtorWithNoReturnDtor() __attribute__((noreturn));
  };

  void testDefaultContructorWithNoReturnDtor() {
    CtorWithNoReturnDtor();
    clang_analyzer_warnIfReached();  // no-warning
  }

  void testLifeExtensionWithNoReturnDtor() {
    const CtorWithNoReturnDtor &c = CtorWithNoReturnDtor();

    // This represents an (expected) loss of coverage, since the destructor
    // of the lifetime-exended temporary is executed at the end of
    // scope.
    clang_analyzer_warnIfReached();  // no-warning
  }

#if __cplusplus >= 201103L
  struct CtorWithNoReturnDtor2 {
    CtorWithNoReturnDtor2() = default;

    CtorWithNoReturnDtor2(int x) {
      clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
    }

    ~CtorWithNoReturnDtor2() __attribute__((noreturn));
  };
  CtorWithNoReturnDtor2 returnNoReturnDtor() {
    return {1}; // no-crash
  }
#endif

#endif // TEMPORARY_DTORS
}

namespace default_param_elided_destructors {
struct a {
  ~a();
};
struct F {
  a d;
  F(char *, a = a());
};
void g() {
  char h[1];
  for (int i = 0;;)
    F j(i ? j : h);
}
} // namespace default_param_elided_destructors

void testStaticMaterializeTemporaryExpr() {
  static const Trivial &ref = getTrivial();
  clang_analyzer_eval(ref.value == 42); // expected-warning{{TRUE}}

  static const Trivial &directRef = Trivial(42);
  clang_analyzer_eval(directRef.value == 42); // expected-warning{{TRUE}}

#if __has_feature(cxx_thread_local)
  thread_local static const Trivial &threadRef = getTrivial();
  clang_analyzer_eval(threadRef.value == 42); // expected-warning{{TRUE}}

  thread_local static const Trivial &threadDirectRef = Trivial(42);
  clang_analyzer_eval(threadDirectRef.value == 42); // expected-warning{{TRUE}}
#endif
}

namespace PR16629 {
  struct A {
    explicit A(int* p_) : p(p_) {}
    int* p;
  };

  extern void escape(const A*[]);
  extern void check(int);

  void callEscape(const A& a) {
    const A* args[] = { &a };
    escape(args);
  }

  void testNoWarning() {
    int x;
    callEscape(A(&x));
    check(x); // Analyzer used to give a "x is uninitialized warning" here
  }

  void set(const A*a[]) {
    *a[0]->p = 47;
  }

  void callSet(const A& a) {
    const A* args[] = { &a };
    set(args);
  }

  void testConsistency() {
    int x;
    callSet(A(&x));
    clang_analyzer_eval(x == 47); // expected-warning{{TRUE}}
  }
}

namespace PR32088 {
  void testReturnFromStmtExprInitializer() {
    // We shouldn't try to destroy the object pointed to by `obj' upon return.
    const NonTrivial &obj = ({
      return; // no-crash
      NonTrivial(42);
    });
  }
}

namespace CopyToTemporaryCorrectly {
class Super {
public:
  void m() {
    mImpl();
  }
  virtual void mImpl() = 0;
};
class Sub : public Super {
public:
  Sub(const int &p) : j(p) {}
  virtual void mImpl() override {
    // Used to be undefined pointer dereference because we didn't copy
    // the subclass data (j) to the temporary object properly.
    (void)(j + 1); // no-warning
    if (j != 22) {
      clang_analyzer_warnIfReached(); // no-warning
    }
  }
  const int &j;
};
void run() {
  int i = 22;
  Sub(i).m();
}
}

namespace test_return_temporary {
class C {
  int x, y;

public:
  C(int x, int y) : x(x), y(y) {}
  int getX() const { return x; }
  int getY() const { return y; }
  ~C() {}
};

class D: public C {
public:
  D() : C(1, 2) {}
  D(const D &d): C(d.getX(), d.getY()) {}
};

C returnTemporaryWithVariable() { C c(1, 2); return c; }
C returnTemporaryWithAnotherFunctionWithVariable() {
  return returnTemporaryWithVariable();
}
C returnTemporaryWithCopyConstructionWithVariable() {
  return C(returnTemporaryWithVariable());
}

C returnTemporaryWithConstruction() { return C(1, 2); }
C returnTemporaryWithAnotherFunctionWithConstruction() {
  return returnTemporaryWithConstruction();
}
C returnTemporaryWithCopyConstructionWithConstruction() {
  return C(returnTemporaryWithConstruction());
}

D returnTemporaryWithVariableAndNonTrivialCopy() { D d; return d; }
D returnTemporaryWithAnotherFunctionWithVariableAndNonTrivialCopy() {
  return returnTemporaryWithVariableAndNonTrivialCopy();
}
D returnTemporaryWithCopyConstructionWithVariableAndNonTrivialCopy() {
  return D(returnTemporaryWithVariableAndNonTrivialCopy());
}

#if __cplusplus >= 201103L
C returnTemporaryWithBraces() { return {1, 2}; }
C returnTemporaryWithAnotherFunctionWithBraces() {
  return returnTemporaryWithBraces();
}
C returnTemporaryWithCopyConstructionWithBraces() {
  return C(returnTemporaryWithBraces());
}
#endif // C++11

void test() {
  C c1 = returnTemporaryWithVariable();
  clang_analyzer_eval(c1.getX() == 1); // expected-warning{{TRUE}}
  clang_analyzer_eval(c1.getY() == 2); // expected-warning{{TRUE}}

  C c2 = returnTemporaryWithAnotherFunctionWithVariable();
  clang_analyzer_eval(c2.getX() == 1); // expected-warning{{TRUE}}
  clang_analyzer_eval(c2.getY() == 2); // expected-warning{{TRUE}}

  C c3 = returnTemporaryWithCopyConstructionWithVariable();
  clang_analyzer_eval(c3.getX() == 1); // expected-warning{{TRUE}}
  clang_analyzer_eval(c3.getY() == 2); // expected-warning{{TRUE}}

  C c4 = returnTemporaryWithConstruction();
  clang_analyzer_eval(c4.getX() == 1); // expected-warning{{TRUE}}
  clang_analyzer_eval(c4.getY() == 2); // expected-warning{{TRUE}}

  C c5 = returnTemporaryWithAnotherFunctionWithConstruction();
  clang_analyzer_eval(c5.getX() == 1); // expected-warning{{TRUE}}
  clang_analyzer_eval(c5.getY() == 2); // expected-warning{{TRUE}}

  C c6 = returnTemporaryWithCopyConstructionWithConstruction();
  clang_analyzer_eval(c5.getX() == 1); // expected-warning{{TRUE}}
  clang_analyzer_eval(c5.getY() == 2); // expected-warning{{TRUE}}

#if __cplusplus >= 201103L

  C c7 = returnTemporaryWithBraces();
  clang_analyzer_eval(c7.getX() == 1); // expected-warning{{TRUE}}
  clang_analyzer_eval(c7.getY() == 2); // expected-warning{{TRUE}}

  C c8 = returnTemporaryWithAnotherFunctionWithBraces();
  clang_analyzer_eval(c8.getX() == 1); // expected-warning{{TRUE}}
  clang_analyzer_eval(c8.getY() == 2); // expected-warning{{TRUE}}

  C c9 = returnTemporaryWithCopyConstructionWithBraces();
  clang_analyzer_eval(c9.getX() == 1); // expected-warning{{TRUE}}
  clang_analyzer_eval(c9.getY() == 2); // expected-warning{{TRUE}}

#endif // C++11

  D d1 = returnTemporaryWithVariableAndNonTrivialCopy();
  clang_analyzer_eval(d1.getX() == 1); // expected-warning{{TRUE}}
  clang_analyzer_eval(d1.getY() == 2); // expected-warning{{TRUE}}

  D d2 = returnTemporaryWithAnotherFunctionWithVariableAndNonTrivialCopy();
  clang_analyzer_eval(d2.getX() == 1); // expected-warning{{TRUE}}
  clang_analyzer_eval(d2.getY() == 2); // expected-warning{{TRUE}}

  D d3 = returnTemporaryWithCopyConstructionWithVariableAndNonTrivialCopy();
  clang_analyzer_eval(d3.getX() == 1); // expected-warning{{TRUE}}
  clang_analyzer_eval(d3.getY() == 2); // expected-warning{{TRUE}}
}
} // namespace test_return_temporary


namespace test_temporary_object_expr_without_dtor {
class C {
  int x;
public:
  C(int x) : x(x) {}
  int getX() const { return x; }
};

void test() {
  clang_analyzer_eval(C(3).getX() == 3); // expected-warning{{TRUE}}
};
}

namespace test_temporary_object_expr_with_dtor {
class C {
  int x;

public:
  C(int x) : x(x) {}
  ~C() {}
  int getX() const { return x; }
};

void test(int coin) {
  clang_analyzer_eval(C(3).getX() == 3);
#ifdef TEMPORARY_DTORS
  // expected-warning@-2{{TRUE}}
#else
  // expected-warning@-4{{UNKNOWN}}
#endif

  const C &c1 = coin ? C(1) : C(2);
  if (coin) {
    clang_analyzer_eval(c1.getX() == 1);
#ifdef TEMPORARY_DTORS
  // expected-warning@-2{{TRUE}}
#else
  // expected-warning@-4{{UNKNOWN}}
#endif
  } else {
    clang_analyzer_eval(c1.getX() == 2);
#ifdef TEMPORARY_DTORS
  // expected-warning@-2{{TRUE}}
#else
  // expected-warning@-4{{UNKNOWN}}
#endif
  }

  C c2 = coin ? C(1) : C(2);
  if (coin) {
    clang_analyzer_eval(c2.getX() == 1); // expected-warning{{TRUE}}
  } else {
    clang_analyzer_eval(c2.getX() == 2); // expected-warning{{TRUE}}
  }
}

} // namespace test_temporary_object_expr

namespace test_match_constructors_and_destructors {
class C {
public:
  int &x, &y;
  C(int &_x, int &_y) : x(_x), y(_y) { ++x; }
  C(const C &c): x(c.x), y(c.y) { ++x; }
  ~C() { ++y; }
};

void test_simple_temporary() {
  int x = 0, y = 0;
  {
    const C &c = C(x, y);
  }
  // One constructor and one destructor.
  clang_analyzer_eval(x == 1);
  clang_analyzer_eval(y == 1);
#ifdef TEMPORARY_DTORS
  // expected-warning@-3{{TRUE}}
  // expected-warning@-3{{TRUE}}
#else
  // expected-warning@-6{{UNKNOWN}}
  // expected-warning@-6{{UNKNOWN}}
#endif
}

void test_simple_temporary_with_copy() {
  int x = 0, y = 0;
  {
    C c = C(x, y);
  }
  // Only one constructor directly into the variable, and one destructor.
  clang_analyzer_eval(x == 1); // expected-warning{{TRUE}}
  clang_analyzer_eval(y == 1); // expected-warning{{TRUE}}
}

void test_ternary_temporary(int coin) {
  int x = 0, y = 0, z = 0, w = 0;
  {
    const C &c = coin ? C(x, y) : C(z, w);
  }
  // Only one constructor on every branch, and one automatic destructor.
  if (coin) {
    clang_analyzer_eval(x == 1);
    clang_analyzer_eval(y == 1);
#ifdef TEMPORARY_DTORS
    // expected-warning@-3{{TRUE}}
    // expected-warning@-3{{TRUE}}
#else
    // expected-warning@-6{{UNKNOWN}}
    // expected-warning@-6{{UNKNOWN}}
#endif
    clang_analyzer_eval(z == 0); // expected-warning{{TRUE}}
    clang_analyzer_eval(w == 0); // expected-warning{{TRUE}}

  } else {
    clang_analyzer_eval(x == 0); // expected-warning{{TRUE}}
    clang_analyzer_eval(y == 0); // expected-warning{{TRUE}}
    clang_analyzer_eval(z == 1);
    clang_analyzer_eval(w == 1);
#ifdef TEMPORARY_DTORS
    // expected-warning@-3{{TRUE}}
    // expected-warning@-3{{TRUE}}
#else
    // expected-warning@-6{{UNKNOWN}}
    // expected-warning@-6{{UNKNOWN}}
#endif
  }
}

void test_ternary_temporary_with_copy(int coin) {
  int x = 0, y = 0, z = 0, w = 0;
  {
    C c = coin ? C(x, y) : C(z, w);
  }
  // On each branch the variable is constructed directly.
  if (coin) {
    clang_analyzer_eval(x == 1); // expected-warning{{TRUE}}
#if __cplusplus < 201703L
    clang_analyzer_eval(y == 1); // expected-warning{{TRUE}}
#else
    // FIXME: Destructor called twice in C++17?
    clang_analyzer_eval(y == 2); // expected-warning{{TRUE}}
#endif
    clang_analyzer_eval(z == 0); // expected-warning{{TRUE}}
    clang_analyzer_eval(w == 0); // expected-warning{{TRUE}}

  } else {
    clang_analyzer_eval(x == 0); // expected-warning{{TRUE}}
    clang_analyzer_eval(y == 0); // expected-warning{{TRUE}}
    clang_analyzer_eval(z == 1); // expected-warning{{TRUE}}
#if __cplusplus < 201703L
    clang_analyzer_eval(w == 1); // expected-warning{{TRUE}}
#else
    // FIXME: Destructor called twice in C++17?
    clang_analyzer_eval(w == 2); // expected-warning{{TRUE}}
#endif
  }
}
} // namespace test_match_constructors_and_destructors

namespace destructors_for_return_values {

class C {
public:
  ~C() {
    1 / 0; // expected-warning{{Division by zero}}
  }
};

C make();

void testFloatingCall() {
  make();
  // Should have divided by zero in the destructor.
  clang_analyzer_warnIfReached();
#ifndef TEMPORARY_DTORS
    // expected-warning@-2{{REACHABLE}}
#endif
}

void testLifetimeExtendedCall() {
  {
    const C &c = make();
    clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
  }
  // Should have divided by zero in the destructor.
  clang_analyzer_warnIfReached(); // no-warning
}

void testCopiedCall() {
  {
    C c = make();
    // Should have elided the constructor/destructor for the temporary
    clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
  }
  // Should have divided by zero in the destructor.
  clang_analyzer_warnIfReached(); // no-warning
}
} // namespace destructors_for_return_values

namespace dont_forget_destructor_around_logical_op {
int glob;

class C {
public:
  ~C() {
    glob = 1;
    // FIXME: Why is destructor not inlined in C++17
    clang_analyzer_checkInlined(true);
#ifdef TEMPORARY_DTORS
#if __cplusplus < 201703L
    // expected-warning@-3{{TRUE}}
#endif
#endif
  }
};

C get();

bool is(C);


void test(int coin) {
  // Here temporaries are being cleaned up after && is evaluated. There are two
  // temporaries: the return value of get() and the elidable copy constructor
  // of that return value into is(). According to the CFG, we need to cleanup
  // both of them depending on whether the temporary corresponding to the
  // return value of get() was initialized. However, we didn't track
  // temporaries returned from functions, so we took the wrong branch.
  coin && is(get()); // no-crash
  if (coin) {
    // FIXME: Why is destructor not inlined in C++17
    clang_analyzer_eval(glob);
#ifdef TEMPORARY_DTORS
#if __cplusplus < 201703L
    // expected-warning@-3{{TRUE}}
#else
    // expected-warning@-5{{UNKNOWN}}
#endif
#else
    // expected-warning@-8{{UNKNOWN}}
#endif
  } else {
    // The destructor is not called on this branch.
    clang_analyzer_eval(glob); // expected-warning{{UNKNOWN}}
  }
}
} // namespace dont_forget_destructor_around_logical_op

#if __cplusplus >= 201103L
namespace temporary_list_crash {
class C {
public:
  C() {}
  ~C() {}
};

void test() {
  std::initializer_list<C>{C(), C()}; // no-crash
}
} // namespace temporary_list_crash
#endif // C++11

namespace implicit_constructor_conversion {
struct S {
  int x;
  S(int x) : x(x) {}
  ~S() {}
};

class C {
  int x;

public:
  C(const S &s) : x(s.x) {}
  ~C() {}
  int getX() const { return x; }
};

void test() {
  const C &c1 = S(10);
  clang_analyzer_eval(c1.getX() == 10);
#ifdef TEMPORARY_DTORS
  // expected-warning@-2{{TRUE}}
#else
  // expected-warning@-4{{UNKNOWN}}
#endif

  S s = 20;
  clang_analyzer_eval(s.x == 20); // expected-warning{{TRUE}}

  C c2 = s;
  clang_analyzer_eval(c2.getX() == 20); // expected-warning{{TRUE}}
}
} // end namespace implicit_constructor_conversion

namespace pass_references_through {
class C {
public:
  ~C() {}
};

const C &foo1();
C &&foo2();

// In these examples the foo() expression has record type, not reference type.
// Don't try to figure out how to perform construction of the record here.
const C &bar1() { return foo1(); } // no-crash
C &&bar2() { return foo2(); } // no-crash
} // end namespace pass_references_through


namespace arguments {
int glob;

struct S {
  int x;
  S(int x): x(x) {}
  S(const S &s) : x(s.x) {}
  ~S() {}

  S &operator+(S s) {
    glob = s.x;
    x += s.x;
    return *this;
  }
};

class C {
public:
  virtual void bar3(S s) {}
};

class D: public C {
public:
  D() {}
  virtual void bar3(S s) override { glob = s.x; }
};

void bar1(S s) {
  glob = s.x;
}

// Record-typed calls are a different CFGStmt, let's see if we handle that
// as well.
S bar2(S s) {
  glob = s.x;
  return S(3);
}

void bar5(int, ...);

void foo(void (*bar4)(S)) {
  bar1(S(1));
  clang_analyzer_eval(glob == 1);
#ifdef TEMPORARY_DTORS
  // expected-warning@-2{{TRUE}}
#else
  // expected-warning@-4{{UNKNOWN}}
#endif

  bar2(S(2));
  // FIXME: Why are we losing information in C++17?
  clang_analyzer_eval(glob == 2);
#ifdef TEMPORARY_DTORS
#if __cplusplus < 201703L
  // expected-warning@-3{{TRUE}}
#else
  // expected-warning@-5{{UNKNOWN}}
#endif
#else
  // expected-warning@-8{{UNKNOWN}}
#endif

  C *c = new D();
  c->bar3(S(3));
  // FIXME: Should be TRUE.
  clang_analyzer_eval(glob == 3); // expected-warning{{UNKNOWN}}
  delete c;

  // What if we've no idea what we're calling?
  bar4(S(4)); // no-crash

  S(5) + S(6);
  clang_analyzer_eval(glob == 6);
#ifdef TEMPORARY_DTORS
  // expected-warning@-2{{TRUE}}
#else
  // expected-warning@-4{{UNKNOWN}}
#endif

  // Variadic functions. This will __builtin_trap() because you cannot pass
  // an object as a variadic argument.
  bar5(7, S(7)); // no-crash
  clang_analyzer_warnIfReached(); // no-warning
}
} // namespace arguments

namespace ctor_argument {
// Stripped down unique_ptr<int>
struct IntPtr {
  IntPtr(): i(new int) {}
  IntPtr(IntPtr &&o): i(o.i) { o.i = 0; }
  ~IntPtr() { delete i; }

  int *i;
};

struct Foo {
  Foo(IntPtr);
  void bar();

  IntPtr i;
};

void bar() {
  IntPtr ptr;
  int *i = ptr.i;
  Foo f(static_cast<IntPtr &&>(ptr));
  *i = 99; // no-warning
}
} // namespace ctor_argument

namespace operator_implicit_argument {
struct S {
  bool x;
  S(bool x): x(x) {}
  operator bool() const { return x; }
};

void foo() {
  if (S(false)) {
    clang_analyzer_warnIfReached(); // no-warning
  }
  if (S(true)) {
    clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
  }
}
} // namespace operator_implicit_argument


#if __cplusplus >= 201103L
namespace argument_lazy_bindings {
int glob;

struct S {
  int x, y, z;
};

struct T {
  S s;
  int w;
  T(int w): s{5, 6, 7}, w(w) {}
};

void foo(T t) {
  t.s = {1, 2, 3};
  glob = t.w;
}

void bar() {
  foo(T(4));
  clang_analyzer_eval(glob == 4); // expected-warning{{TRUE}}
}
} // namespace argument_lazy_bindings
#endif

namespace operator_argument_cleanup {
struct S {
  S();
};

class C {
public:
  void operator=(S);
};

void foo() {
  C c;
  c = S(); // no-crash
}
} // namespace operator_argument_cleanup

namespace argument_decl_lookup {
class C {};
int foo(C);
int bar(C c) { foo(c); }
int foo(C c) {}
} // namespace argument_decl_lookup

namespace argument_virtual_decl_lookup {
class C {};

struct T  {
  virtual void foo(C);
};

void run() {
  T *t;
  t->foo(C()); // no-crash // expected-warning{{Called C++ object pointer is uninitialized}}
}

// This is after run() because the test is about picking the correct decl
// for the parameter region, which should belong to the correct function decl,
// and the non-definition decl should be found by direct lookup.
void T::foo(C) {}
} // namespace argument_virtual_decl_lookup

namespace union_indirect_field_crash {
union U {
  struct {
    int x;
  };
};

template <typename T> class C {
public:
  void foo() const {
    (void)(true ? U().x : 0);
  }
};

void test() {
  C<int> c;
  c.foo();
}
} // namespace union_indirect_field_crash

namespace return_from_top_frame {
struct S {
  int *p;
  S() { p = new int; }
  S(S &&s) : p(s.p) { s.p = 0; }
  ~S();  // Presumably releases 'p'.
};

S foo() {
  S s;
  return s;
}

S bar1() {
  return foo(); // no-warning
}

S bar2() {
  return S();
}

S bar3(int coin) {
  return coin ? S() : foo(); // no-warning
}
} // namespace return_from_top_frame
