// RUN: %clang_analyze_cc1 -std=c++14 -verify  %s \
// RUN:   -analyzer-checker=core \
// RUN:   -analyzer-checker=optin.cplusplus.UninitializedObject \
// RUN:   -analyzer-config optin.cplusplus.UninitializedObject:Pedantic=true -DPEDANTIC \
// RUN:   -analyzer-config \
// RUN:     optin.cplusplus.UninitializedObject:CheckPointeeInitialization=true
// RUN: %clang_analyze_cc1 -std=c++14 -verify  %s \
// RUN:   -analyzer-checker=core \
// RUN:   -analyzer-checker=optin.cplusplus.UninitializedObject \
// RUN:   -analyzer-config optin.cplusplus.UninitializedObject:Pedantic=true -DPEDANTIC \
// RUN:   -analyzer-config \
// RUN:     optin.cplusplus.UninitializedObject:CheckPointeeInitialization=true -DHEAP_ALLOCATION

// RUN: %clang_analyze_cc1 -std=c++14 -verify  %s \
// RUN:   -analyzer-checker=core \
// RUN:   -analyzer-checker=optin.cplusplus.UninitializedObject \
// RUN:   -analyzer-config \
// RUN:     optin.cplusplus.UninitializedObject:CheckPointeeInitialization=true
// RUN: %clang_analyze_cc1 -std=c++14 -verify  %s \
// RUN:   -analyzer-checker=core \
// RUN:   -analyzer-checker=optin.cplusplus.UninitializedObject \
// RUN:   -analyzer-config \
// RUN:     optin.cplusplus.UninitializedObject:CheckPointeeInitialization=true -DHEAP_ALLOCATION

#ifdef HEAP_ALLOCATION
#define INIT(CLS, ARGS) new CLS ARGS
#else
#define INIT(CLS, ARGS) (void) CLS ARGS
#endif

//===----------------------------------------------------------------------===//
// Default constructor test.
//===----------------------------------------------------------------------===//

class CompilerGeneratedConstructorTest {
  int a, b, c, d, e, f, g, h, i, j;

public:
  CompilerGeneratedConstructorTest() = default;
};

void fCompilerGeneratedConstructorTest() {
  INIT(CompilerGeneratedConstructorTest, ());
}

#ifdef PEDANTIC
class DefaultConstructorTest {
  int a; // expected-note{{uninitialized field 'this->a'}}

public:
  DefaultConstructorTest();
};

DefaultConstructorTest::DefaultConstructorTest() = default;

void fDefaultConstructorTest() {
  INIT(DefaultConstructorTest, ()); // expected-warning{{1 uninitialized field}}
}
#else
class DefaultConstructorTest {
  int a;

public:
  DefaultConstructorTest();
};

DefaultConstructorTest::DefaultConstructorTest() = default;

void fDefaultConstructorTest() {
  INIT(DefaultConstructorTest, ());
}
#endif // PEDANTIC

//===----------------------------------------------------------------------===//
// Initializer list test.
//===----------------------------------------------------------------------===//

class InitListTest1 {
  int a;
  int b;

public:
  InitListTest1()
      : a(1),
        b(2) {
    // All good!
  }
};

void fInitListTest1() {
  INIT(InitListTest1, ());
}

class InitListTest2 {
  int a;
  int b; // expected-note{{uninitialized field 'this->b'}}

public:
  InitListTest2()
      : a(3) {} // expected-warning{{1 uninitialized field}}
};

void fInitListTest2() {
  INIT(InitListTest2, ());
}

class InitListTest3 {
  int a; // expected-note{{uninitialized field 'this->a'}}
  int b;

public:
  InitListTest3()
      : b(4) {} // expected-warning{{1 uninitialized field}}
};

void fInitListTest3() {
  INIT(InitListTest3, ());
}

//===----------------------------------------------------------------------===//
// Constructor body test.
//===----------------------------------------------------------------------===//

class CtorBodyTest1 {
  int a, b;

public:
  CtorBodyTest1() {
    a = 5;
    b = 6;
    // All good!
  }
};

void fCtorBodyTest1() {
  INIT(CtorBodyTest1, ());
}

class CtorBodyTest2 {
  int a;
  int b; // expected-note{{uninitialized field 'this->b'}}

public:
  CtorBodyTest2() {
    a = 7; // expected-warning{{1 uninitialized field}}
  }
};

void fCtorBodyTest2() {
  INIT(CtorBodyTest2, ());
}

class CtorBodyTest3 {
  int a; // expected-note{{uninitialized field 'this->a'}}
  int b;

public:
  CtorBodyTest3() {
    b = 8; // expected-warning{{1 uninitialized field}}
  }
};

void fCtorBodyTest3() {
  INIT(CtorBodyTest3, ());
}

#ifdef PEDANTIC
class CtorBodyTest4 {
  int a; // expected-note{{uninitialized field 'this->a'}}
  int b; // expected-note{{uninitialized field 'this->b'}}

public:
  CtorBodyTest4() {}
};

void fCtorBodyTest4() {
  INIT(CtorBodyTest4, ()); // expected-warning{{2 uninitialized fields}}
}
#else
class CtorBodyTest4 {
  int a;
  int b;

public:
  CtorBodyTest4() {}
};

void fCtorBodyTest4() {
  INIT(CtorBodyTest4, ());
}
#endif

//===----------------------------------------------------------------------===//
// Constructor delegation test.
//===----------------------------------------------------------------------===//

class CtorDelegationTest1 {
  int a;
  int b;

public:
  CtorDelegationTest1(int)
      : a(9) {
    // leaves 'b' uninitialized, but we'll never check this function
  }

  CtorDelegationTest1()
      : CtorDelegationTest1(int{}) { // Initializing 'a'
    b = 10;
    // All good!
  }
};

void fCtorDelegationTest1() {
  INIT(CtorDelegationTest1, ());
}

class CtorDelegationTest2 {
  int a; // expected-note{{uninitialized field 'this->a'}}
  int b;

public:
  CtorDelegationTest2(int)
      : b(11) {
    // leaves 'a' uninitialized, but we'll never check this function
  }

  CtorDelegationTest2()
      : CtorDelegationTest2(int{}) { // expected-warning{{1 uninitialized field}}
  }
};

void fCtorDelegationTest2() {
  INIT(CtorDelegationTest2, ());
}

//===----------------------------------------------------------------------===//
// Tests for classes containing records.
//===----------------------------------------------------------------------===//

class ContainsRecordTest1 {
  struct RecordType {
    int x;
    int y;
  } rec;
  int c, d;

public:
  ContainsRecordTest1()
      : rec({12, 13}),
        c(14),
        d(15) {
    // All good!
  }
};

void fContainsRecordTest1() {
  INIT(ContainsRecordTest1, ());
}

class ContainsRecordTest2 {
  struct RecordType {
    int x;
    int y; // expected-note{{uninitialized field 'this->rec.y'}}
  } rec;
  int c, d;

public:
  ContainsRecordTest2()
      : c(16),
        d(17) {
    rec.x = 18; // expected-warning{{1 uninitialized field}}
  }
};

void fContainsRecordTest2() {
  INIT(ContainsRecordTest2, ());
}

class ContainsRecordTest3 {
  struct RecordType {
    int x; // expected-note{{uninitialized field 'this->rec.x'}}
    int y; // expected-note{{uninitialized field 'this->rec.y'}}
  } rec;
  int c, d;

public:
  ContainsRecordTest3()
      : c(19),
        d(20) { // expected-warning{{2 uninitialized fields}}
  }
};

void fContainsRecordTest3() {
  INIT(ContainsRecordTest3, ());
}

class ContainsRecordTest4 {
  struct RecordType {
    int x; // expected-note{{uninitialized field 'this->rec.x'}}
    int y; // expected-note{{uninitialized field 'this->rec.y'}}
  } rec;
  int c, d; // expected-note{{uninitialized field 'this->d'}}

public:
  ContainsRecordTest4()
      : c(19) { // expected-warning{{3 uninitialized fields}}
  }
};

void fContainsRecordTest4() {
  INIT(ContainsRecordTest4, ());
}

//===----------------------------------------------------------------------===//
// Tests for template classes.
//===----------------------------------------------------------------------===//

template <class T>
class IntTemplateClassTest1 {
  T t;
  int b;

public:
  IntTemplateClassTest1(T i) {
    b = 21;
    t = i;
    // All good!
  }
};

void fIntTemplateClassTest1() {
  INIT(IntTemplateClassTest1<int>, (22));
}

template <class T>
class IntTemplateClassTest2 {
  T t; // expected-note{{uninitialized field 'this->t'}}
  int b;

public:
  IntTemplateClassTest2() {
    b = 23; // expected-warning{{1 uninitialized field}}
  }
};

void fIntTemplateClassTest2() {
  INIT(IntTemplateClassTest2<int>, ());
}

struct Record {
  int x; // expected-note{{uninitialized field 'this->t.x'}}
  int y; // expected-note{{uninitialized field 'this->t.y'}}
};

template <class T>
class RecordTemplateClassTest {
  T t;
  int b;

public:
  RecordTemplateClassTest() {
    b = 24; // expected-warning{{2 uninitialized fields}}
  }
};

void fRecordTemplateClassTest() {
  INIT(RecordTemplateClassTest<Record>, ());
}

//===----------------------------------------------------------------------===//
// Tests involving functions with unknown implementations.
//===----------------------------------------------------------------------===//

template <class T>
void mayInitialize(T &);

template <class T>
void wontInitialize(const T &);

class PassingToUnknownFunctionTest1 {
  int a, b;

public:
  PassingToUnknownFunctionTest1() {
    mayInitialize(a);
    mayInitialize(b);
    // All good!
  }

  PassingToUnknownFunctionTest1(int) {
    mayInitialize(a);
    // All good!
  }

  PassingToUnknownFunctionTest1(int, int) {
    mayInitialize(*this);
    // All good!
  }
};

void fPassingToUnknownFunctionTest1() {
  INIT(PassingToUnknownFunctionTest1, ());
  INIT(PassingToUnknownFunctionTest1, (int()));
  INIT(PassingToUnknownFunctionTest1, (int(), int()));
}

class PassingToUnknownFunctionTest2 {
  int a; // expected-note{{uninitialized field 'this->a'}}
  int b;

public:
  PassingToUnknownFunctionTest2() {
    wontInitialize(a);
    b = 4; // expected-warning{{1 uninitialized field}}
  }
};

void fPassingToUnknownFunctionTest2() {
  INIT(PassingToUnknownFunctionTest2, ());
}

//===----------------------------------------------------------------------===//
// Tests for classes containing unions.
//===----------------------------------------------------------------------===//

// FIXME: As of writing this checker, there is no good support for union types
// in the Static Analyzer. Here is non-exhaustive list of cases.
// Note that the rules for unions are different in C and C++.
// http://lists.llvm.org/pipermail/cfe-dev/2017-March/052910.html

class ContainsSimpleUnionTest1 {
  union SimpleUnion {
    float uf;
    int ui;
    char uc;
  } u;

public:
  ContainsSimpleUnionTest1() {
    u.uf = 3.14;
    // All good!
  }
};

void fContainsSimpleUnionTest1() {
  INIT(ContainsSimpleUnionTest1, ());
}

class ContainsSimpleUnionTest2 {
  union SimpleUnion {
    float uf;
    int ui;
    char uc;
    // TODO: we'd expect the note: {{uninitialized field 'this->u'}}
  } u; // no-note

public:
  ContainsSimpleUnionTest2() {}
};

void fContainsSimpleUnionTest2() {
  // TODO: we'd expect the warning: {{1 uninitialized field}}
  INIT(ContainsSimpleUnionTest2, ()); // no-warning
}

class UnionPointerTest1 {
public:
  union SimpleUnion {
    float uf;
    int ui;
    char uc;
  };

private:
  SimpleUnion *uptr;

public:
  UnionPointerTest1(SimpleUnion *uptr, int) : uptr(uptr) {
    // All good!
  }
};

void fUnionPointerTest1() {
  UnionPointerTest1::SimpleUnion u;
  u.uf = 41;
  INIT(UnionPointerTest1, (&u, int()));
}

class UnionPointerTest2 {
public:
  union SimpleUnion {
    float uf;
    int ui;
    char uc;
  };

private:
  // TODO: we'd expect the note: {{uninitialized field 'this->uptr'}}
  SimpleUnion *uptr; // no-note

public:
  UnionPointerTest2(SimpleUnion *uptr, char) : uptr(uptr) {}
};

void fUnionPointerTest2() {
  UnionPointerTest2::SimpleUnion u;
  // TODO: we'd expect the warning: {{1 uninitialized field}}
  INIT(UnionPointerTest2, (&u, int())); // no-warning
}

class ContainsUnionWithRecordTest1 {
  union UnionWithRecord {
    struct RecordType {
      int x;
      int y;
    } us;
    double ud;
    long ul;

    UnionWithRecord(){};
  } u;

public:
  ContainsUnionWithRecordTest1() {
    u.ud = 3.14;
    // All good!
  }
};

void fContainsUnionWithRecordTest1() {
  INIT(ContainsUnionWithRecordTest1, ());
}

class ContainsUnionWithRecordTest2 {
  union UnionWithRecord {
    struct RecordType {
      int x;
      int y;
    } us;
    double ud;
    long ul;

    UnionWithRecord(){};
  } u;

public:
  ContainsUnionWithRecordTest2() {
    u.us = UnionWithRecord::RecordType{42, 43};
    // All good!
  }
};

void fContainsUnionWithRecordTest2() {
  INIT(ContainsUnionWithRecordTest1, ());
}

class ContainsUnionWithRecordTest3 {
  union UnionWithRecord {
    struct RecordType {
      int x;
      int y;
    } us;
    double ud;
    long ul;

    UnionWithRecord(){};
    // TODO: we'd expect the note: {{uninitialized field 'this->u'}}
  } u; // no-note

public:
  ContainsUnionWithRecordTest3() {
    UnionWithRecord::RecordType rec;
    rec.x = 44;
    // TODO: we'd expect the warning: {{1 uninitialized field}}
    u.us = rec; // no-warning
  }
};

void fContainsUnionWithRecordTest3() {
  INIT(ContainsUnionWithRecordTest3, ());
}

class ContainsUnionWithSimpleUnionTest1 {
  union UnionWithSimpleUnion {
    union SimpleUnion {
      float uf;
      int ui;
      char uc;
    } usu;
    long ul;
    unsigned uu;
  } u;

public:
  ContainsUnionWithSimpleUnionTest1() {
    u.usu.ui = 5;
    // All good!
  }
};

void fContainsUnionWithSimpleUnionTest1() {
  INIT(ContainsUnionWithSimpleUnionTest1, ());
}

class ContainsUnionWithSimpleUnionTest2 {
  union UnionWithSimpleUnion {
    union SimpleUnion {
      float uf;
      int ui;
      char uc;
    } usu;
    long ul;
    unsigned uu;
    // TODO: we'd expect the note: {{uninitialized field 'this->u'}}
  } u; // no-note

public:
  ContainsUnionWithSimpleUnionTest2() {}
};

void fContainsUnionWithSimpleUnionTest2() {
  // TODO: we'd expect the warning: {{1 uninitialized field}}
  INIT(ContainsUnionWithSimpleUnionTest2, ()); // no-warning
}

//===----------------------------------------------------------------------===//
// Zero initialization tests.
//===----------------------------------------------------------------------===//

struct GlobalVariableTest {
  int i;

  GlobalVariableTest() {}
};

GlobalVariableTest gvt; // no-warning

//===----------------------------------------------------------------------===//
// Copy and move constructor tests.
//===----------------------------------------------------------------------===//

template <class T>
void funcToSquelchCompilerWarnings(const T &t);

#ifdef PEDANTIC
struct CopyConstructorTest {
  int i; // expected-note{{uninitialized field 'this->i'}}

  CopyConstructorTest() : i(1337) {}
  CopyConstructorTest(const CopyConstructorTest &other) {}
};

void fCopyConstructorTest() {
  CopyConstructorTest cct;
  INIT(CopyConstructorTest, (cct)); // expected-warning{{1 uninitialized field}}
}
#else
struct CopyConstructorTest {
  int i;

  CopyConstructorTest() : i(1337) {}
  CopyConstructorTest(const CopyConstructorTest &other) {}
};

void fCopyConstructorTest() {
  CopyConstructorTest cct;
  INIT(CopyConstructorTest, (cct));
}
#endif // PEDANTIC

struct MoveConstructorTest {
  // TODO: we'd expect the note: {{uninitialized field 'this->i'}}
  int i; // no-note

  MoveConstructorTest() : i(1337) {}
  MoveConstructorTest(const CopyConstructorTest &other) = delete;
  MoveConstructorTest(const CopyConstructorTest &&other) {}
};

void fMoveConstructorTest() {
  MoveConstructorTest cct;
  // TODO: we'd expect the warning: {{1 uninitialized field}}
  INIT(MoveConstructorTest, (static_cast<MoveConstructorTest &&>(cct))); // no-warning
}

//===----------------------------------------------------------------------===//
// Array tests.
//===----------------------------------------------------------------------===//

struct IntArrayTest {
  int arr[256];

  IntArrayTest() {
    // All good!
  }
};

void fIntArrayTest() {
  INIT(IntArrayTest, ());
}

struct RecordTypeArrayTest {
  struct RecordType {
    int x, y;
  } arr[256];

  RecordTypeArrayTest() {
    // All good!
  }
};

void fRecordTypeArrayTest() {
  INIT(RecordTypeArrayTest, ());
}

template <class T>
class CharArrayPointerTest {
  T *t; // no-crash

public:
  CharArrayPointerTest(T *t, int) : t(t) {}
};

void fCharArrayPointerTest() {
  char str[16] = "012345678912345";
  CharArrayPointerTest<char[16]>(&str, int());
}

//===----------------------------------------------------------------------===//
// Memset tests.
//===----------------------------------------------------------------------===//

struct MemsetTest1 {
  int a, b, c;

  MemsetTest1() {
    __builtin_memset(this, 0, sizeof(decltype(*this)));
  }
};

void fMemsetTest1() {
  INIT(MemsetTest1, ());
}

struct MemsetTest2 {
  int a;

  MemsetTest2() {
    __builtin_memset(&a, 0, sizeof(int));
  }
};

void fMemsetTest2() {
  INIT(MemsetTest2, ());
}

//===----------------------------------------------------------------------===//
// Lambda tests.
//===----------------------------------------------------------------------===//

template <class Callable>
struct LambdaThisTest {
  Callable functor;

  LambdaThisTest(const Callable &functor, int) : functor(functor) {
    // All good!
  }
};

struct HasCapturableThis {
  void fLambdaThisTest() {
    auto isEven = [this](int a) { return a % 2 == 0; }; // no-crash
    LambdaThisTest<decltype(isEven)>(isEven, int());
  }
};

template <class Callable>
struct LambdaTest1 {
  Callable functor;

  LambdaTest1(const Callable &functor, int) : functor(functor) {
    // All good!
  }
};

void fLambdaTest1() {
  auto isEven = [](int a) { return a % 2 == 0; };
  INIT(LambdaTest1<decltype(isEven)>, (isEven, int()));
}

#ifdef PEDANTIC
template <class Callable>
struct LambdaTest2 {
  Callable functor;

  LambdaTest2(const Callable &functor, int) : functor(functor) {} // expected-warning{{1 uninitialized field}}
};

void fLambdaTest2() {
  int b;
  auto equals = [&b](int a) { return a == b; }; // expected-note{{uninitialized pointee 'this->functor./*captured variable*/b'}}
  INIT(LambdaTest2<decltype(equals)>, (equals, int()));
}
#else
template <class Callable>
struct LambdaTest2 {
  Callable functor;

  LambdaTest2(const Callable &functor, int) : functor(functor) {}
};

void fLambdaTest2() {
  int b;
  auto equals = [&b](int a) { return a == b; };
  INIT(LambdaTest2<decltype(equals)>, (equals, int()));
}
#endif //PEDANTIC

#ifdef PEDANTIC
namespace LT3Detail {

struct RecordType {
  int x; // expected-note{{uninitialized field 'this->functor./*captured variable*/rec1.x'}}
  int y; // expected-note{{uninitialized field 'this->functor./*captured variable*/rec1.y'}}
};

} // namespace LT3Detail
template <class Callable>
struct LambdaTest3 {
  Callable functor;

  LambdaTest3(const Callable &functor, int) : functor(functor) {} // expected-warning{{2 uninitialized fields}}
};

void fLambdaTest3() {
  LT3Detail::RecordType rec1;
  auto equals = [&rec1](LT3Detail::RecordType rec2) {
    return rec1.x == rec2.x;
  };
  INIT(LambdaTest3<decltype(equals)>, (equals, int()));
}
#else
namespace LT3Detail {

struct RecordType {
  int x;
  int y;
};

} // namespace LT3Detail
template <class Callable>
struct LambdaTest3 {
  Callable functor;

  LambdaTest3(const Callable &functor, int) : functor(functor) {}
};

void fLambdaTest3() {
  LT3Detail::RecordType rec1;
  auto equals = [&rec1](LT3Detail::RecordType rec2) {
    return rec1.x == rec2.x;
  };
  INIT(LambdaTest3<decltype(equals)>, (equals, int()));
}
#endif //PEDANTIC

template <class Callable>
struct MultipleLambdaCapturesTest1 {
  Callable functor;
  int dontGetFilteredByNonPedanticMode = 0;

  MultipleLambdaCapturesTest1(const Callable &functor, int) : functor(functor) {} // expected-warning{{2 uninitialized field}}
};

void fMultipleLambdaCapturesTest1() {
  int b1, b2 = 3, b3;
  auto equals = [&b1, &b2, &b3](int a) { return a == b1 == b2 == b3; }; // expected-note{{uninitialized pointee 'this->functor./*captured variable*/b1'}}
  // expected-note@-1{{uninitialized pointee 'this->functor./*captured variable*/b3'}}
  INIT(MultipleLambdaCapturesTest1<decltype(equals)>, (equals, int()));
}

template <class Callable>
struct MultipleLambdaCapturesTest2 {
  Callable functor;
  int dontGetFilteredByNonPedanticMode = 0;

  MultipleLambdaCapturesTest2(const Callable &functor, int) : functor(functor) {} // expected-warning{{1 uninitialized field}}
};

void fMultipleLambdaCapturesTest2() {
  int b1, b2 = 3, b3;
  auto equals = [b1, &b2, &b3](int a) { return a == b1 == b2 == b3; }; // expected-note{{uninitialized pointee 'this->functor./*captured variable*/b3'}}
  INIT(MultipleLambdaCapturesTest2<decltype(equals)>, (equals, int()));
}

struct LambdaWrapper {
  void *func; // no-crash
  int dontGetFilteredByNonPedanticMode = 0;

  LambdaWrapper(void *ptr) : func(ptr) {} // expected-warning{{1 uninitialized field}}
};

struct ThisCapturingLambdaFactory {
  int a; // expected-note{{uninitialized field 'static_cast<decltype(a.ret()) *>(this->func)->/*'this' capture*/->a'}}

  auto ret() {
    return [this] { (void)this; };
  }
};

void fLambdaFieldWithInvalidThisCapture() {
  void *ptr;
  {
    ThisCapturingLambdaFactory a;
    decltype(a.ret()) lambda = a.ret();
    ptr = &lambda;
  }
  LambdaWrapper t(ptr);
}

//===----------------------------------------------------------------------===//
// System header tests.
//===----------------------------------------------------------------------===//

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

struct SystemHeaderTest1 {
  RecordInSystemHeader rec; // defined in the system header simulator

  SystemHeaderTest1() {
    // All good!
  }
};

void fSystemHeaderTest1() {
  INIT(SystemHeaderTest1, ());
}

#ifdef PEDANTIC
struct SystemHeaderTest2 {
  struct RecordType {
    int x; // expected-note{{uninitialized field 'this->container.t.x}}
    int y; // expected-note{{uninitialized field 'this->container.t.y}}
  };
  ContainerInSystemHeader<RecordType> container;

  SystemHeaderTest2(RecordType &rec, int) : container(rec) {} // expected-warning{{2 uninitialized fields}}
};

void fSystemHeaderTest2() {
  SystemHeaderTest2::RecordType rec;
  INIT(SystemHeaderTest2, (rec, int()));
}
#else
struct SystemHeaderTest2 {
  struct RecordType {
    int x;
    int y;
  };
  ContainerInSystemHeader<RecordType> container;

  SystemHeaderTest2(RecordType &rec, int) : container(rec) {}
};

void fSystemHeaderTest2() {
  SystemHeaderTest2::RecordType rec;
  INIT(SystemHeaderTest2, (rec, int()));
}
#endif //PEDANTIC

//===----------------------------------------------------------------------===//
// Incomplete type tests.
//===----------------------------------------------------------------------===//

struct IncompleteTypeTest1 {
  struct RecordType;
  // no-crash
  RecordType *recptr; // expected-note{{uninitialized pointer 'this->recptr}}
  int dontGetFilteredByNonPedanticMode = 0;

  IncompleteTypeTest1() {} // expected-warning{{1 uninitialized field}}
};

void fIncompleteTypeTest1() {
  INIT(IncompleteTypeTest1, ());
}

struct IncompleteTypeTest2 {
  struct RecordType;
  RecordType *recptr; // no-crash
  int dontGetFilteredByNonPedanticMode = 0;

  RecordType *recordTypeFactory();

  IncompleteTypeTest2() : recptr(recordTypeFactory()) {}
};

void fIncompleteTypeTest2() {
  INIT(IncompleteTypeTest2, ());
}

struct IncompleteTypeTest3 {
  struct RecordType;
  RecordType &recref; // no-crash
  int dontGetFilteredByNonPedanticMode = 0;

  RecordType &recordTypeFactory();

  IncompleteTypeTest3() : recref(recordTypeFactory()) {}
};

void fIncompleteTypeTest3() {
  INIT(IncompleteTypeTest3, ());
}

//===----------------------------------------------------------------------===//
// Builtin type or enumeration type related tests.
//===----------------------------------------------------------------------===//

struct IntegralTypeTest {
  int a; // expected-note{{uninitialized field 'this->a'}}
  int dontGetFilteredByNonPedanticMode = 0;

  IntegralTypeTest() {} // expected-warning{{1 uninitialized field}}
};

void fIntegralTypeTest() {
  INIT(IntegralTypeTest, ());
}

struct FloatingTypeTest {
  float a; // expected-note{{uninitialized field 'this->a'}}
  int dontGetFilteredByNonPedanticMode = 0;

  FloatingTypeTest() {} // expected-warning{{1 uninitialized field}}
};

void fFloatingTypeTest() {
  INIT(FloatingTypeTest, ());
}

struct NullptrTypeTypeTest {
  decltype(nullptr) a; // expected-note{{uninitialized field 'this->a'}}
  int dontGetFilteredByNonPedanticMode = 0;

  NullptrTypeTypeTest() {} // expected-warning{{1 uninitialized field}}
};

void fNullptrTypeTypeTest() {
  INIT(NullptrTypeTypeTest, ());
}

struct EnumTest {
  enum Enum {
    A,
    B
  } enum1; // expected-note{{uninitialized field 'this->enum1'}}
  enum class Enum2 {
    A,
    B
  } enum2; // expected-note{{uninitialized field 'this->enum2'}}
  int dontGetFilteredByNonPedanticMode = 0;

  EnumTest() {} // expected-warning{{2 uninitialized fields}}
};

void fEnumTest() {
  INIT(EnumTest, ());
}

//===----------------------------------------------------------------------===//
// Tests for constructor calls within another cunstructor, without the two
// records being in any relation.
//===----------------------------------------------------------------------===//

void halt() __attribute__((__noreturn__));
void assert(int b) {
  if (!b)
    halt();
}

// While a singleton would make more sense as a static variable, that would zero
// initialize all of its fields, hence the not too practical implementation.
struct Singleton {
  int i; // expected-note{{uninitialized field 'this->i'}}
  int dontGetFilteredByNonPedanticMode = 0;

  Singleton() {
    assert(!isInstantiated);
    isInstantiated = true; // expected-warning{{1 uninitialized field}}
  }

  ~Singleton() {
    isInstantiated = false;
  }

  static bool isInstantiated;
};

bool Singleton::isInstantiated = false;

struct SingletonTest {
  int dontGetFilteredByNonPedanticMode = 0;

  SingletonTest() {
    INIT(Singleton, ());
  }
};

void fSingletonTest() {
  SingletonTest();
}

//===----------------------------------------------------------------------===//
// C++11 member initializer tests.
//===----------------------------------------------------------------------===//

struct CXX11MemberInitTest1 {
  int a = 3;
  int b;
  CXX11MemberInitTest1() : b(2) {
    // All good!
  }
};

void fCXX11MemberInitTest1() {
  INIT(CXX11MemberInitTest1, ());
}

struct CXX11MemberInitTest2 {
  struct RecordType {
    // TODO: we'd expect the note: {{uninitialized field 'this->rec.a'}}
    int a; // no-note
    // TODO: we'd expect the note: {{uninitialized field 'this->rec.b'}}
    int b; // no-note

    RecordType(int) {}
  };

  RecordType rec = RecordType(int());
  int dontGetFilteredByNonPedanticMode = 0;

  CXX11MemberInitTest2() {}
};

void fCXX11MemberInitTest2() {
  // TODO: we'd expect the warning: {{2 uninitializeds field}}
  INIT(CXX11MemberInitTest2, ()); // no-warning
}

//===----------------------------------------------------------------------===//
// "Esoteric" primitive type tests.
//===----------------------------------------------------------------------===//

struct MyAtomicInt {
  _Atomic(int) x; // expected-note{{uninitialized field 'this->x'}}
  int dontGetFilteredByNonPedanticMode = 0;

  MyAtomicInt() {} // expected-warning{{1 uninitialized field}}
};

void _AtomicTest() {
  INIT(MyAtomicInt, ());
}

struct VectorSizeLong {
  VectorSizeLong() {}
  __attribute__((__vector_size__(16))) long x;
};

void __vector_size__LongTest() {
  // TODO: Warn for v.x.
  VectorSizeLong v;
  new VectorSizeLong();
  v.x[0] = 0;
}

struct ComplexUninitTest {
  ComplexUninitTest() {}
  __complex__ float x;
  __complex__ int y;
};

struct ComplexInitTest {
  ComplexInitTest() {
    x = {1.0f, 1.0f};
    y = {1, 1};
  }
  __complex__ float x;
  __complex__ int y;
};

void fComplexTest() {
  INIT(ComplexInitTest, ());

  // TODO: we should emit a warning for x2.x and x2.y.
  INIT(ComplexUninitTest, ());
}

struct PaddingBitfieldTest {
  int a;
  long long : 7; // padding, previously flagged as uninitialized
  PaddingBitfieldTest(int a) : a(a) {}
};

void fPaddingBitfieldTest() {
  PaddingBitfieldTest pb(42);
  // no-warning: Unnamed bitfield is now ignored, fixing false positive
}

struct NamedBitfieldTest {
  int b; 
  long long named : 7; // expected-note{{uninitialized field 'this->named'}}
  NamedBitfieldTest(int b) : b(b) {} // expected-warning{{1 uninitialized field at the end of the constructor call}}
};

void fNamedBitfieldTest() {
  NamedBitfieldTest nb(42); 
}
