// RUN: %check_clang_tidy %s bugprone-sizeof-expression %t

#define offsetof(type, member) __builtin_offsetof(type, member)

typedef __SIZE_TYPE__ size_t;
typedef __WCHAR_TYPE__ wchar_t;

extern void *memset(void *Dest, int Ch, size_t Count);
extern size_t strlen(const char *Str);
extern size_t wcslen(const wchar_t *Str);
extern char *strcpy(char *Dest, const char *Src);
extern wchar_t *wcscpy(wchar_t *Dest, const wchar_t *Src);
extern int scanf(const char *Format, ...);
extern int wscanf(const wchar_t *Format, ...);

extern void sink(const void *P);

enum { BufferSize = 1024 };

void bad1a(void) {
  int Buffer[BufferSize];

  int *P = &Buffer[0];
  int *Q = P;
  while (Q < P + sizeof(Buffer)) {
    // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator [bugprone-sizeof-expression]
    // CHECK-MESSAGES: :[[@LINE-2]]:16: note: '+' in pointer arithmetic internally scales with 'sizeof(int)' == {{[0-9]+}}
    *Q++ = 0;
  }
}

void bad1b(void) {
  typedef int Integer;
  Integer Buffer[BufferSize];

  Integer *P = &Buffer[0];
  Integer *Q = P;
  while (Q < P + sizeof(Buffer)) {
    // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator
    // CHECK-MESSAGES: :[[@LINE-2]]:16: note: '+' in pointer arithmetic internally scales with 'sizeof(Integer)' == {{[0-9]+}}
    *Q++ = 0;
  }
}

void good1(void) {
  int Buffer[BufferSize];

  int *P = &Buffer[0];
  int *Q = P;
  while (Q < P + BufferSize) {
    *Q++ = 0;
  }
}

void bad2(void) {
  int Buffer[BufferSize];
  int *P = Buffer;

  while (P < Buffer + sizeof(Buffer)) {
    // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator
    // CHECK-MESSAGES: :[[@LINE-2]]:21: note: '+' in pointer arithmetic internally scales with 'sizeof(int)' == {{[0-9]+}}
    *P++ = 0;
  }
}

void good2(void) {
  int Buffer[BufferSize];
  int *P = Buffer;

  while (P < Buffer + BufferSize) {
    *P++ = 0;
  }
}

struct S {
  long A, B, C;
};

void bad3a(struct S *S) {
  const size_t Offset = offsetof(struct S, B);
  struct S *P = S;

  // This is not captureable by Tidy because the size/offset expression is
  // not a direct child of the pointer arithmetics.
  memset(P + Offset, 0, sizeof(struct S) - Offset);
}

void good3a(struct S *S) {
  const size_t Offset = offsetof(struct S, B);
  char *P = (char*)S;

  // This is not captureable by Tidy because the size/offset expression is
  // not a direct child of the pointer arithmetics.
  memset(P + Offset, 0, sizeof(struct S) - Offset);
}

void bad3b(struct S *S) {
  memset(S + offsetof(struct S, B), 0,
         sizeof(struct S) - offsetof(struct S, B));
  // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: suspicious usage of 'offsetof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator
  // CHECK-MESSAGES: :[[@LINE-3]]:12: note: '+' in pointer arithmetic internally scales with 'sizeof(struct S)' == {{[0-9]+}}
}

void good3b(struct S *S) {
  char *P = (char*)S;
  memset(P + offsetof(struct S, B), 0,
         sizeof(struct S) - offsetof(struct S, B));
}

void bad3c(void) {
  struct S Buffer[BufferSize];

  struct S *P = &Buffer[0];
  struct S *Q = P;
  while (Q < P + sizeof(Buffer)) {
    // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator
    // CHECK-MESSAGES: :[[@LINE-2]]:16: note: '+' in pointer arithmetic internally scales with 'sizeof(struct S)' == {{[0-9]+}}
    sink(Q++);
  }
}

void bad4(void) {
  int Buffer[BufferSize];

  int *P = &Buffer[0];
  int *Q = P;
  while (Q < P + BufferSize) {
    *Q = 0;
    Q += sizeof(*Q);
    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+=' operator
    // CHECK-MESSAGES: :[[@LINE-2]]:7: note: '+=' in pointer arithmetic internally scales with 'sizeof(int)' == {{[0-9]+}}
  }
}

void silenced4(void) {
  char Buffer[BufferSize];

  char *P = &Buffer[0];
  char *Q = P;
  while (Q < P + BufferSize) {
    *Q = 0;
    Q += sizeof(*Q);
  }
}

void good4(void) {
  char Buffer[BufferSize];

  char *P = &Buffer[0];
  char *Q = P;
  while (Q < P + BufferSize) {
    *Q = 0;
    Q += 1;
  }
}

void good5aa(void) {
  int Buffer[BufferSize];

  int *P = &Buffer[0];
  int *Q = P;
  while (Q < P + BufferSize) {
    *Q = 0;
    Q += ( sizeof(Buffer) / sizeof(Buffer[0]) );
  }
}

void good5ab(void) {
  int Buffer[BufferSize];

  int *P = &Buffer[0];
  int *Q = P;
  while (Q < P + BufferSize) {
    *Q = 0;
    Q = Q + ( sizeof(Buffer) / sizeof(Buffer[0]) );
  }
}

void good5ba(void) {
  int Buffer[BufferSize];

  int *P = &Buffer[0];
  int *Q = P;
  while (Q < P + BufferSize) {
    *Q = 0;
    Q -= ( sizeof(Buffer) / sizeof(Buffer[0]) );
  }
}

void good5bb(void) {
  int Buffer[BufferSize];

  int *P = &Buffer[0];
  int *Q = P;
  while (Q < P + BufferSize) {
    *Q = 0;
    Q = Q - ( sizeof(Buffer) / sizeof(Buffer[0]) );
  }
}

void bad6(void) {
  int Buffer[BufferSize];

  int *P = &Buffer[0];
  int *Q = P;
  while (Q < P + BufferSize) {
    *Q = 0;
    Q = Q + sizeof(*Q);
    // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator
    // CHECK-MESSAGES: :[[@LINE-2]]:11: note: '+' in pointer arithmetic internally scales with 'sizeof(int)' == {{[0-9]+}}
  }
}

void silenced6(void) {
  char Buffer[BufferSize];

  char *P = &Buffer[0];
  char *Q = P;
  while (Q < P + BufferSize) {
    *Q = 0;
    Q = Q + sizeof(*Q);
  }
}

void good6(void) {
  char Buffer[BufferSize];

  char *P = &Buffer[0];
  char *Q = P;
  while (Q < P + BufferSize) {
    *Q = 0;
    Q = Q + 1;
  }
}

void silenced7(void) {
  char Buffer[BufferSize];

  char *P = &Buffer[0];
  const char *Q = P;
  while (Q < P + BufferSize) {
    sink(Q);
    Q = Q + sizeof(*Q);
  }
}

void good7(void) {
  char Buffer[BufferSize];

  char *P = &Buffer[0];
  const char *Q = P;
  while (Q < P + BufferSize) {
    sink(Q);
    Q = Q + 1;
  }
}

void bad8(void) {
  int Buffer[BufferSize];

  int *P = &Buffer[0];
  int *Q = P;
  while (Q >= P) {
    *Q = 0;
    Q = Q - sizeof(*Q);
    // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '-' operator
    // CHECK-MESSAGES: :[[@LINE-2]]:11: note: '-' in pointer arithmetic internally scales with 'sizeof(int)' == {{[0-9]+}}
  }
}

void silenced8(void) {
  char Buffer[BufferSize];

  char *P = &Buffer[0];
  char *Q = P;
  while (Q >= P) {
    *Q = 0;
    Q = Q - sizeof(*Q);
  }
}

void good8(void) {
  char Buffer[BufferSize];

  char *P = &Buffer[0];
  char *Q = P;
  while (Q >= P) {
    *Q = 0;
    Q = Q - 1;
  }
}

void good9(void) {
  int Buffer[BufferSize];

  int *P = &Buffer[0];
  int *Q = P + BufferSize;
  int N = Q - P;
  while (N >= 0) {
    Q[N] = 0;
    N = N - 1;
  }
}

void good10(void) {
  int Buffer[BufferSize];

  int *P = &Buffer[0];
  int *Q = Buffer + BufferSize;
  int I = sizeof(*P) - sizeof(*Q);

  sink(&I);
}

void good11(void) {
  int Buffer[BufferSize];

  int *P = &Buffer[0];
  int *Q = Buffer + BufferSize;
  int I = sizeof(Q) - sizeof(*P);

  sink(&I);
}

void bad12(void) {
  wchar_t Message[BufferSize];
  wcscpy(Message, L"Message: ");
  wscanf(L"%s", Message + wcslen(Message) * sizeof(wchar_t));
  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator
  // CHECK-MESSAGES: :[[@LINE-2]]:25: note: '+' in pointer arithmetic internally scales with 'sizeof(wchar_t)' == {{[0-9]+}}
}

void silenced12(void) {
  char Message[BufferSize];
  strcpy(Message, "Message: ");
  scanf("%s", Message + strlen(Message) * sizeof(char));
}

void nomatch12(void) {
  char Message[BufferSize];
  strcpy(Message, "Message: ");
  scanf("%s", Message + strlen(Message));
}

void good12(void) {
  wchar_t Message[BufferSize];
  wcscpy(Message, L"Message: ");
  wscanf(L"%s", Message + wcslen(Message));
}

void good13(void) {
  int Buffer[BufferSize];

  int *P = &Buffer[0];
  while (P < Buffer + sizeof(Buffer) / sizeof(int)) {
    // NO-WARNING: Calculating the element count of the buffer here, which is
    // safe with this idiom (as long as the types don't change).
    ++P;
  }

  while (P < Buffer + sizeof(Buffer) / sizeof(Buffer[0])) {
    // NO-WARNING: Calculating the element count of the buffer here, which is
    // safe with this idiom.
    ++P;
  }

  while (P < Buffer + sizeof(Buffer) / sizeof(*P)) {
    // NO-WARNING: Calculating the element count of the buffer here, which is
    // safe with this idiom.
    ++P;
  }
}

void situational14(int *Buffer, size_t BufferSize) {
  int *P = &Buffer[0];
  while (P < Buffer + BufferSize / sizeof(*Buffer)) {
    // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator
    // CHECK-MESSAGES: :[[@LINE-2]]:21: note: '+' in pointer arithmetic internally scales with 'sizeof(int)' == {{[0-9]+}}
    ++P;
  }
}
