// RUN: %check_clang_tidy %s bugprone-undefined-memory-manipulation %t

void *memset(void *, int, __SIZE_TYPE__);
void *memcpy(void *, const void *, __SIZE_TYPE__);
void *memmove(void *, const void *, __SIZE_TYPE__);

namespace std {
using ::memcpy;
using ::memmove;
using ::memset;
}

// TriviallyCopyable types:
struct Plain {
  int n;
};

enum E {
  X,
  Y,
  Z
};

struct Base {
  float b;
};

struct Derived : Base {
  bool d;
};

// not TriviallyCopyable types:
struct Destruct {
  ~Destruct() {}
};

struct Copy {
  Copy() {}
  Copy(const Copy &) {}
};

struct Move {
  Move() {}
  Move(Move &&) {}
};

struct VirtualFunc {
  virtual void f() {}
};

struct VirtualBase : virtual Base {
  int vb;
};

template <typename T>
void memset_temp(T *b) {
  memset(b, 0, sizeof(T));
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
}

template <typename S, typename T>
void memcpy_temp(S *a, T *b) {
  memcpy(a, b, sizeof(T));
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
}

template <typename S, typename T>
void memmove_temp(S *a, T *b) {
  memmove(a, b, sizeof(T));
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
}

void notTriviallyCopyable() {
  Plain p; // TriviallyCopyable for variety
  Destruct d;
  Copy c;
  Move m;
  VirtualFunc vf;
  VirtualBase vb;

  memset(&vf, 0, sizeof(int));
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
  memset(&d, 0, sizeof(int));
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
  memset(&c, 0, sizeof(int));
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
  std::memset(&m, 0, sizeof(int));
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
  ::memset(&vb, 0, sizeof(int));
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]

  memcpy(&p, &vf, sizeof(int));
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
  memcpy(&p, &d, sizeof(int));
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
  memcpy(&c, &p, sizeof(int));
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
  std::memcpy(&m, &p, sizeof(int));
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
  ::memcpy(&vb, &p, sizeof(int));
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]

  memmove(&vf, &p, sizeof(int));
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
  memmove(&d, &p, sizeof(int));
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
  memmove(&p, &c, sizeof(int));
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
  std::memmove(&p, &m, sizeof(int));
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
  ::memmove(&p, &vb, sizeof(int));
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]

#define MEMSET memset(&vf, 0, sizeof(int));
  MEMSET
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
#define MEMCPY memcpy(&d, &p, sizeof(int));
  MEMCPY
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
#define MEMMOVE memmove(&p, &c, sizeof(int));
  MEMMOVE
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]

  memset_temp<VirtualFunc>(&vf);
  memcpy_temp<Plain, VirtualFunc>(&p, &vf);
  memmove_temp<Plain, VirtualFunc>(&p, &vf);
}

void triviallyCopyable() {
  Plain p;
  Base base;
  Derived derived;

  int i = 5;
  int ia[3] = {1, 2, 3};
  float f = 3.14;
  float fa[3] = {1.1, 2.2, 3.3};
  bool b = false;
  bool ba[2] = {true, false};
  E e = X;
  p.n = 2;

  memset(&p, 0, sizeof(int));
  memset(&base, 0, sizeof(float));
  memset(&derived, 0, sizeof(bool));
  memset(&i, 0, sizeof(int));
  memset(ia, 0, sizeof(int));
  memset(&f, 0, sizeof(float));
  memset(fa, 0, sizeof(float));
  memset(&b, 0, sizeof(bool));
  memset(ba, 0, sizeof(bool));
  memset(&e, 0, sizeof(int));
  memset(&p.n, 0, sizeof(int));

  memcpy(&p, &p, sizeof(int));
  memcpy(&base, &base, sizeof(float));
  memcpy(&derived, &derived, sizeof(bool));
  memcpy(&i, &i, sizeof(int));
  memcpy(ia, ia, sizeof(int));
  memcpy(&f, &f, sizeof(float));
  memcpy(fa, fa, sizeof(float));
  memcpy(&b, &b, sizeof(bool));
  memcpy(ba, ba, sizeof(bool));
  memcpy(&e, &e, sizeof(int));
  memcpy(&p.n, &p.n, sizeof(int));

  memmove(&p, &p, sizeof(int));
  memmove(&base, &base, sizeof(float));
  memmove(&derived, &derived, sizeof(bool));
  memmove(&i, &i, sizeof(int));
  memmove(ia, ia, sizeof(int));
  memmove(&f, &f, sizeof(float));
  memmove(fa, fa, sizeof(float));
  memmove(&b, &b, sizeof(bool));
  memmove(ba, ba, sizeof(bool));
  memmove(&e, &e, sizeof(int));
  memmove(&p.n, &p.n, sizeof(int));
}
