// RUN: %check_clang_tidy %s misc-const-correctness %t \
// RUN: -config='{CheckOptions: {\
// RUN:   misc-const-correctness.AnalyzeValues: false,\
// RUN:   misc-const-correctness.AnalyzeReferences: false,\
// RUN:   misc-const-correctness.AnalyzePointers: true,\
// RUN:   misc-const-correctness.WarnPointersAsValues: false,\
// RUN:   misc-const-correctness.WarnPointersAsPointers: true,\
// RUN:   misc-const-correctness.TransformPointersAsValues: false,\
// RUN:   misc-const-correctness.TransformPointersAsPointers: true\
// RUN: }}' \
// RUN: -- -fno-delayed-template-parsing

#include <memory>
#include <vector>

void pointee_to_const() {
  int a[] = {1, 2};
  int *p_local0 = &a[0];
  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: pointee of variable 'p_local0' of type 'int *' can be declared 'const'
  // CHECK-FIXES: int  const*p_local0 = &a[0];
  p_local0 = &a[1];
}

void array_of_pointer_to_const() {
  int a[] = {1, 2};
  int *p_local0[1] = {&a[0]};
  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: pointee of variable 'p_local0' of type 'int *[1]' can be declared 'const'
  // CHECK-FIXES: int  const*p_local0[1] = {&a[0]};
  p_local0[0] = &a[1];
}

template<class T>
void template_fn() {
  T a[] = {1, 2};
  T *p_local0 = &a[0];
  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: pointee of variable 'p_local0' of type 'char *' can be declared 'const'
  // CHECK-FIXES: T  const*p_local0 = &a[0];
  p_local0 = &a[1];
}

void instantiate() {
  template_fn<char>();
  template_fn<int>();
  template_fn<char const>();
}

using const_int = int const;
void ignore_const_alias() {
  const_int a[] = {1, 2};
  const_int *p_local0 = &a[0];
  p_local0 = &a[1];
}

void *return_non_const() {
  void *const a = nullptr;
  return a;
}

void function_pointer_basic() {
  void (*const fp)() = nullptr;
  fp();
}

void takeNonConstRef(int *&r);

void ignoreNonConstRefOps() {
  // init with non-const ref
  int* p0 {nullptr};
  int*& r1 = p0;
  
  // non-const ref param
  int* p1 {nullptr};
  takeNonConstRef(p1);

  // cast
  int* p2 {nullptr};
  int*& r2 = (int*&)p2;
}

void pointer_to_pointer_param(int**);
void pass_address_to_pointer_to_pointer() {
  int i = 0;
  int* ip = &i;
  // CHECK-NOT: warning
  pointer_to_pointer_param(&ip);
}

void void_pointer_to_pointer_param(void**);
void pass_address_to_void_pointer_to_pointer() {
  void* ptr = nullptr;
  // CHECK-NOT: warning
  void_pointer_to_pointer_param(&ptr);
}

void already_const_pointee() {
  const char* foo[] = {"a", "b"};
  // CHECK-NOT: warning
  foo[0] = "c";
}

void array_of_const_pointers() {
  const int i = 0;
  const int* const bar[] = {&i};
  // CHECK-NOT: warning
}

using ConstChar = const char;
void alias_to_const_pointer() {
  ConstChar * foo[] = {"a", "b"};
  // CHECK-NOT: warning
  foo[0] = "c";
}

void multi_level_pointer() {
  const char * s = "a";
  const char ** foo[] = {&s};
  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: pointee of variable 'foo' of type 'const char **[1]' can be declared 'const'
  // CHECK-FIXES: const char * const* foo[] = {&s};
  const char * p = *foo[0];
}

void* operator new(decltype(sizeof(void*)), void*) noexcept;

void pointer_in_emplacement_new() {
  int* ptr = nullptr;
  // CHECK-NOT: warning
  new(ptr) int {123};
}

void takesConstPointerRef(int *const &);
void takesConstPointerRRef(int *const &&);
using IntPtrAlias = int *;
typedef int *IntPtrTypedef;
void takesAliasConstPointerRef(IntPtrAlias const &);
void takesTypedefConstPointerRef(IntPtrTypedef const &);

void ignore_const_pointer_reference_sinks() {
  int value = 0;

  int *p_local0 = &value;
  takesConstPointerRef(p_local0);

  int *p_local1 = &value;
  int *const &ref = p_local1;
  (void)ref;

  IntPtrAlias p_local2 = &value;
  takesAliasConstPointerRef(p_local2);

  int *p_local3 = &value;
  takesConstPointerRRef(static_cast<int *const &&>(p_local3));

  IntPtrTypedef p_local4 = &value;
  takesTypedefConstPointerRef(p_local4);

  int *p_local5 = &value;
  int *volatile &volatile_ref = p_local5;
  (void)volatile_ref;
}

void ignore_range_for_pointer_reference_sink() {
  int value = 0;
  std::vector<int *> const source = {&value};
  std::vector<int *> sink;
  for (int *element : source)
    sink.push_back(element);
}

struct UniquePtrData {};

void ignore_unique_ptr_emplace_sink() {
  /*const*/ UniquePtrData *const newdata = new UniquePtrData;

  std::vector<std::unique_ptr<UniquePtrData>> data;
  data.emplace_back(newdata);
}
