// RUN: %clang_analyze_cc1 -std=c++20 \
// RUN:  -analyzer-checker=core,unix,cplusplus,debug.ExprInspection \
// RUN:  -triple x86_64-unknown-linux-gnu \
// RUN:  -verify %s

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

typedef __SIZE_TYPE__ size_t;
void *malloc(size_t);
void *alloca(size_t);
void *realloc(void *ptr, size_t size);
void *calloc(size_t number, size_t size);
void free(void *);

struct S {
  int f;
};

void clang_analyzer_dump(int);
void clang_analyzer_dump(const void *);
void clang_analyzer_dumpExtent(int);
void clang_analyzer_dumpExtent(const void *);
void clang_analyzer_dumpElementCount(int);
void clang_analyzer_dumpElementCount(const void *);

int clang_analyzer_getExtent(void *);
void clang_analyzer_eval(bool);

void var_simple_ref() {
  int a = 13;
  clang_analyzer_dump(&a);             // expected-warning {{a}}
  clang_analyzer_dumpExtent(&a);       // expected-warning {{4 S64b}}
  clang_analyzer_dumpElementCount(&a); // expected-warning {{1 S64b}}
}

void var_simple_ptr(int *a) {
  clang_analyzer_dump(a);             // expected-warning {{SymRegion{reg_$1<int * a>}}}
  clang_analyzer_dumpExtent(a);       // expected-warning {{extent_$2{SymRegion{reg_$1<int * a>}}}}
  clang_analyzer_dumpElementCount(a); // expected-warning {{(extent_$2{SymRegion{reg_$1<int * a>}}) / 4}}
}

void var_array() {
  int a[] = {1, 2, 3};
  clang_analyzer_dump(a);             // expected-warning {{Element{a,0 S64b,int}}}
  clang_analyzer_dumpExtent(a);       // expected-warning {{12 S64b}}
  clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}}
}

void string() {
  clang_analyzer_dump("foo");             // expected-warning {{Element{"foo",0 S64b,char}}}
  clang_analyzer_dumpExtent("foo");       // expected-warning {{4 S64b}}
  clang_analyzer_dumpElementCount("foo"); // expected-warning {{4 S64b}}
}

void struct_simple_ptr(S *a) {
  clang_analyzer_dump(a);             // expected-warning {{SymRegion{reg_$1<S * a>}}}
  clang_analyzer_dumpExtent(a);       // expected-warning {{extent_$2{SymRegion{reg_$1<S * a>}}}}
  clang_analyzer_dumpElementCount(a); // expected-warning {{(extent_$2{SymRegion{reg_$1<S * a>}}) / 4}}
}

void field_ref(S a) {
  clang_analyzer_dump(&a.f);             // expected-warning {{a.f}}
  clang_analyzer_dumpExtent(&a.f);       // expected-warning {{4 S64b}}
  clang_analyzer_dumpElementCount(&a.f); // expected-warning {{1 S64b}}
}

void field_ptr(S *a) {
  clang_analyzer_dump(&a->f);             // expected-warning {{Element{SymRegion{reg_$1<S * a>},0 S64b,struct S}.f}}
  clang_analyzer_dumpExtent(&a->f);       // expected-warning {{extent_$2{SymRegion{reg_$1<S * a>}}}}
  clang_analyzer_dumpElementCount(&a->f); // expected-warning {{(extent_$2{SymRegion{reg_$1<S * a>}}) / 4U}}
}

void symbolic_array() {
  int *a = new int[3];
  clang_analyzer_dump(a);             // expected-warning {{Element{HeapSymRegion{conj}}
  clang_analyzer_dumpExtent(a);       // expected-warning {{12 S64b}}
  clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}}
  delete[] a;
}

void symbolic_placement_new() {
  char *buf = new char[sizeof(int) * 3];
  int *a = new (buf) int(12);
  clang_analyzer_dump(a);             // expected-warning {{Element{HeapSymRegion{conj}}
  clang_analyzer_dumpExtent(a);       // expected-warning {{12 S64b}}
  clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}}
  delete[] buf;
}

void symbolic_malloc() {
  int *a = (int *)malloc(12);
  clang_analyzer_dump(a);             // expected-warning {{Element{HeapSymRegion{conj}}
  clang_analyzer_dumpExtent(a);       // expected-warning {{12 S64b}}
  clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}}
  free(a);
}

void symbolic_alloca() {
  int *a = (int *)alloca(12);
  clang_analyzer_dump(a);             // expected-warning {{Element{alloca{}}
  clang_analyzer_dumpExtent(a);       // expected-warning {{12 S64b}}
  clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}}
}

void symbolic_complex() {
  int *a = (int *)malloc(4);
  clang_analyzer_dumpExtent(a);       // expected-warning {{4 S64b}}
  clang_analyzer_dumpElementCount(a); // expected-warning {{1 S64b}}

  int *b = (int *)realloc(a, sizeof(int) * 2);
  clang_analyzer_dumpExtent(b);       // expected-warning {{8 S64b}}
  clang_analyzer_dumpElementCount(b); // expected-warning {{2 S64b}}
  free(b);

  int *c = (int *)calloc(3, 4);
  clang_analyzer_dumpExtent(c);       // expected-warning {{12 S64b}}
  clang_analyzer_dumpElementCount(c); // expected-warning {{3 S64b}}
  free(c);
}

void signedness_equality() {
  char *a = new char[sizeof(char) * 13];
  char *b = (char *)malloc(13);

  clang_analyzer_dump(clang_analyzer_getExtent(a)); // expected-warning {{13 S64b}}
  clang_analyzer_dump(clang_analyzer_getExtent(b)); // expected-warning {{13 S64b}}
  clang_analyzer_eval(clang_analyzer_getExtent(a) ==
                      clang_analyzer_getExtent(b));
  // expected-warning@-2 {{TRUE}}

  delete[] a;
  free(b);
}

void default_new_aligned() {
  struct alignas(32) S {};

  S *a = new S[10];

  clang_analyzer_dump(a);             // expected-warning {{Element{HeapSymRegion{conj}}
  clang_analyzer_dumpExtent(a);       // expected-warning {{320 S64b}}
  clang_analyzer_dumpElementCount(a); // expected-warning {{10 S64b}}

  delete[] a;
}

void *operator new[](std::size_t, std::align_val_t, bool hack) throw();

void user_defined_new() {
  int *a = new (std::align_val_t(32), true) int[10];

  clang_analyzer_dump(a);             // expected-warning {{Element{SymRegion{conj}}
  clang_analyzer_dumpExtent(a);       // expected-warning-re {{{{^extent_\$[0-9]\{SymRegion{conj}}}}
  clang_analyzer_dumpElementCount(a); // expected-warning-re {{{{^\(extent_\$[0-9]\{SymRegion{conj.*\) / 4}}}}

  operator delete[](a, std::align_val_t(32));
}

int a[5][0];
struct T {
  int x[];
};

T t[5];
void zero_sized_element() {
  clang_analyzer_dumpExtent(a);       // expected-warning {{0 S64b}}
  clang_analyzer_dumpElementCount(a); // expected-warning {{5 S64b}}
  clang_analyzer_dumpExtent(t);       // expected-warning {{0 S64b}}
  clang_analyzer_dumpElementCount(t); // expected-warning {{5 S64b}}
}

void extent_with_offset() {
  int nums[] = {1,2,3};
  char *p = (char*)&nums[1];

  clang_analyzer_dumpExtent(p);         // expected-warning {{8 S64b}}
  clang_analyzer_dumpElementCount(p);   // expected-warning {{8 S64b}}
  ++p;
  clang_analyzer_dumpExtent(p);         // expected-warning {{7 S64b}}
  clang_analyzer_dumpElementCount(p);   // expected-warning {{7 S64b}}
  ++p;
  int *q = (int*)p;
  clang_analyzer_dumpExtent(q);         // expected-warning {{6 S64b}}
  clang_analyzer_dumpElementCount(q);   // expected-warning {{1 S64b}}
}
