// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-inlining=destructors -verify -std=c++11 %s
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-inlining=destructors -verify -std=c++17 %s

using size_t =  __typeof(sizeof(int));

void clang_analyzer_eval(bool);
void clang_analyzer_checkInlined(bool);
void clang_analyzer_warnIfReached();
void clang_analyzer_explain(int);

int a, b, c, d;

struct InlineDtor {
  static int cnt;
  static int dtorCalled;
  ~InlineDtor() {
    switch (dtorCalled % 4) {
    case 0:
      a = cnt++;
      break;
    case 1:
      b = cnt++;
      break;
    case 2:
      c = cnt++;
      break;
    case 3:
      d = cnt++;
      break;
    }

    ++dtorCalled;
  }
};

int InlineDtor::cnt = 0;
int InlineDtor::dtorCalled = 0;

void foo() {
  InlineDtor::cnt = 0;
  InlineDtor::dtorCalled = 0;
  InlineDtor arr[4];
}

void testAutoDtor() {
  foo();

  clang_analyzer_eval(a == 0); // expected-warning {{TRUE}}
  clang_analyzer_eval(b == 1); // expected-warning {{TRUE}}
  clang_analyzer_eval(c == 2); // expected-warning {{TRUE}}
  clang_analyzer_eval(d == 3); // expected-warning {{TRUE}}
}

void testDeleteDtor() {
  InlineDtor::cnt = 10;
  InlineDtor::dtorCalled = 0;

  InlineDtor *arr = new InlineDtor[4];
  delete[] arr;

  clang_analyzer_eval(a == 10); // expected-warning {{TRUE}}
  clang_analyzer_eval(b == 11); // expected-warning {{TRUE}}
  clang_analyzer_eval(c == 12); // expected-warning {{TRUE}}
  clang_analyzer_eval(d == 13); // expected-warning {{TRUE}}
}

struct MemberDtor {
  InlineDtor arr[4];
};

void testMemberDtor() {
  InlineDtor::cnt = 5;
  InlineDtor::dtorCalled = 0;

  MemberDtor *MD = new MemberDtor{};
  delete MD;

  clang_analyzer_eval(a == 5); // expected-warning {{TRUE}}
  clang_analyzer_eval(b == 6); // expected-warning {{TRUE}}
  clang_analyzer_eval(c == 7); // expected-warning {{TRUE}}
  clang_analyzer_eval(d == 8); // expected-warning {{TRUE}}
}

struct MultipleMemberDtor
{
  InlineDtor arr[4];
  InlineDtor arr2[4];
};

void testMultipleMemberDtor() {
  InlineDtor::cnt = 30;
  InlineDtor::dtorCalled = 0;

  MultipleMemberDtor *MD = new MultipleMemberDtor{};
  delete MD;

  clang_analyzer_eval(a == 34); // expected-warning {{TRUE}}
  clang_analyzer_eval(b == 35); // expected-warning {{TRUE}}
  clang_analyzer_eval(c == 36); // expected-warning {{TRUE}}
  clang_analyzer_eval(d == 37); // expected-warning {{TRUE}}
}

int EvalOrderArr[4];

struct EvalOrder
{
  int ctor = 0;
  static int dtorCalled;
  static int ctorCalled;

  EvalOrder() { ctor = ctorCalled++; };

  ~EvalOrder() { EvalOrderArr[ctor] = dtorCalled++; }
};

int EvalOrder::ctorCalled = 0;
int EvalOrder::dtorCalled = 0;

void dtorEvaluationOrder() {
  EvalOrder::ctorCalled = 0;
  EvalOrder::dtorCalled = 0;
  
  EvalOrder* eptr = new EvalOrder[4];
  delete[] eptr;

  clang_analyzer_eval(EvalOrder::dtorCalled == 4); // expected-warning {{TRUE}}
  clang_analyzer_eval(EvalOrder::dtorCalled == EvalOrder::ctorCalled); // expected-warning {{TRUE}}

  clang_analyzer_eval(EvalOrderArr[0] == 3); // expected-warning {{TRUE}}
  clang_analyzer_eval(EvalOrderArr[1] == 2); // expected-warning {{TRUE}}
  clang_analyzer_eval(EvalOrderArr[2] == 1); // expected-warning {{TRUE}}
  clang_analyzer_eval(EvalOrderArr[3] == 0); // expected-warning {{TRUE}}
}

struct EmptyDtor {
  ~EmptyDtor(){};
};

struct DefaultDtor {
  ~DefaultDtor() = default;
};

// This function used to fail on an assertion.
void no_crash() {
  EmptyDtor* eptr = new EmptyDtor[4];
  delete[] eptr;
  clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}

  DefaultDtor* dptr = new DefaultDtor[4];
  delete[] dptr;
  clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
}

// This snippet used to crash.
namespace crash2
{
  template <class _Tp> class unique_ptr {
  typedef _Tp *pointer;
  pointer __ptr_;

public:
  unique_ptr(pointer __p) : __ptr_(__p) {}
  ~unique_ptr() { reset(); }
  pointer get() { return __ptr_;}
  void reset() {}
};

struct S;

S *makeS();
int bar(S *x, S *y);

void foo() {
  unique_ptr<S> x(makeS()), y(makeS());
  bar(x.get(), y.get());
}

void bar() {
  foo();
  clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
}

} // namespace crash2

// This snippet used to crash.
namespace crash3
{
struct InlineDtor {
  ~InlineDtor() {}
};
struct MultipleMemberDtor
{
  InlineDtor arr[4];
  InlineDtor arr2[4];
};

void foo(){
  auto *arr = new MultipleMemberDtor[4];
  delete[] arr;
  clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
}
} // namespace crash3

namespace crash4 {
struct a {
  a *b;
};
struct c {
  a d;
  c();
  ~c() {
    for (a e = d;; e = *e.b)
      ;
  }
};
void f() { 
  c g; 
  clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
}

} // namespace crash4

namespace crash5 {
namespace std {
template <class _Tp> class unique_ptr {
  _Tp *__ptr_;
public:
  unique_ptr(_Tp *__p) : __ptr_(__p) {}
  ~unique_ptr() {}
};
} // namespace std

int SSL_use_certificate(int *arg) {
  std::unique_ptr<int> free_x509(arg);
  {
    if (SSL_use_certificate(arg)) {
      return 0;
    }
  }
  clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
  return 1;
}

} // namespace crash5

void zeroLength(){
  InlineDtor::dtorCalled = 0;

  auto *arr = new InlineDtor[0];
  delete[] arr;

  auto *arr2 = new InlineDtor[2][0][2];
  delete[] arr2;

  auto *arr3 = new InlineDtor[0][2][2];
  delete[] arr3;

  auto *arr4 = new InlineDtor[2][2][0];
  delete[] arr4;

  clang_analyzer_eval(InlineDtor::dtorCalled == 0); // expected-warning {{TRUE}}
}


void evalOrderPrep() {
  EvalOrderArr[0] = 0;
  EvalOrderArr[1] = 0;
  EvalOrderArr[2] = 0;
  EvalOrderArr[3] = 0;

  EvalOrder::ctorCalled = 0;
  EvalOrder::dtorCalled = 0;
}

void multidimensionalPrep(){
  EvalOrder::ctorCalled = 0;
  EvalOrder::dtorCalled = 0;

  EvalOrder arr[2][2];
}

void multidimensional(){
  evalOrderPrep();
  multidimensionalPrep();
  
  clang_analyzer_eval(EvalOrder::dtorCalled == 4); // expected-warning {{TRUE}}
  clang_analyzer_eval(EvalOrder::dtorCalled == EvalOrder::ctorCalled); // expected-warning {{TRUE}}

  clang_analyzer_eval(EvalOrderArr[0] == 3); // expected-warning {{TRUE}}
  clang_analyzer_eval(EvalOrderArr[1] == 2); // expected-warning {{TRUE}}
  clang_analyzer_eval(EvalOrderArr[2] == 1); // expected-warning {{TRUE}}
  clang_analyzer_eval(EvalOrderArr[3] == 0); // expected-warning {{TRUE}}
}

void multidimensionalHeap() {
  evalOrderPrep();

  auto* eptr = new EvalOrder[2][2];
  delete[] eptr;

  clang_analyzer_eval(EvalOrder::dtorCalled == 4); // expected-warning {{TRUE}}
  clang_analyzer_eval(EvalOrder::dtorCalled == EvalOrder::ctorCalled); // expected-warning {{TRUE}}

  clang_analyzer_eval(EvalOrderArr[0] == 3); // expected-warning {{TRUE}}
  clang_analyzer_eval(EvalOrderArr[1] == 2); // expected-warning {{TRUE}}
  clang_analyzer_eval(EvalOrderArr[2] == 1); // expected-warning {{TRUE}}
  clang_analyzer_eval(EvalOrderArr[3] == 0); // expected-warning {{TRUE}}
}

struct MultiWrapper{
  EvalOrder arr[2][2];
};

void multidimensionalMember(){
  evalOrderPrep();
  
  auto* mptr = new MultiWrapper;
  delete mptr;

  clang_analyzer_eval(EvalOrder::dtorCalled == 4); // expected-warning {{TRUE}}
  clang_analyzer_eval(EvalOrder::dtorCalled == EvalOrder::ctorCalled); // expected-warning {{TRUE}}

  clang_analyzer_eval(EvalOrderArr[0] == 3); // expected-warning {{TRUE}}
  clang_analyzer_eval(EvalOrderArr[1] == 2); // expected-warning {{TRUE}}
  clang_analyzer_eval(EvalOrderArr[2] == 1); // expected-warning {{TRUE}}
  clang_analyzer_eval(EvalOrderArr[3] == 0); // expected-warning {{TRUE}}
}

void *memset(void *, int, size_t);
void clang_analyzer_dumpElementCount(InlineDtor *);

void nonConstantRegionExtent(){

  InlineDtor::dtorCalled = 0;

  int x = 3;
  memset(&x, 1, sizeof(x));

  InlineDtor *arr = new InlineDtor[x];
  clang_analyzer_dumpElementCount(arr); // expected-warning {{conj_$0}}
  delete [] arr;

  //FIXME: This should be TRUE but memset also sets this
  // region to a conjured symbol.
  clang_analyzer_eval(InlineDtor::dtorCalled == 0); // expected-warning {{TRUE}} expected-warning {{FALSE}}
}

namespace crash6 {

struct NonTrivialItem {
  ~NonTrivialItem();
};

struct WeirdVec {
  void clear() {
    delete[] data;
    size = 0;
  }
  NonTrivialItem *data;
  unsigned size;
};

void top(int j) {
  WeirdVec *p = new WeirdVec;

  p[j].size = 0;
  delete[] p->data; // no-crash
}

template <typename T>
T make_unknown() {
  return reinterpret_cast<T>(static_cast<int>(0.404));
}

void directUnknownSymbol() {
  delete[] make_unknown<NonTrivialItem*>(); // no-crash
}

}
