// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage -fcxx-exceptions -fsafe-buffer-usage-suggestions -verify %s
// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fcxx-exceptions -fdiagnostics-parseable-fixits -fsafe-buffer-usage-suggestions %s 2>&1 | FileCheck %s

typedef int * TYPEDEF_PTR;
#define MACRO_PTR int*

// We CANNOT fix a pointer whose type is defined in a typedef or a
// macro. Because if the typedef is changed after the fix, the fix
// becomes incorrect and may not be noticed.

// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE+1]]
void typedefPointer(TYPEDEF_PTR p) {  // expected-warning{{'p' is an unsafe pointer used for buffer access}}
  if (++p) {  // expected-note{{used in pointer arithmetic here}}
  }
}

// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE+1]]
void macroPointer(MACRO_PTR p) {  // expected-warning{{'p' is an unsafe pointer used for buffer access}}
  if (++p) {  // expected-note{{used in pointer arithmetic here}}
  }
}

// The analysis requires accurate source location informations from
// `TypeLoc`s of types of variable (parameter) declarations in order
// to generate fix-its for them. But those information is not always
// available (probably due to some bugs in clang but it is irrelevant
// to the safe-buffer project).  The following is an example.  When
// `_Atomic` is used, we cannot get valid source locations of the
// pointee type of `unsigned *`.  The analysis gives up in such a
// case.
// CHECK-NOT: fix-it:
void typeLocSourceLocationInvalid(_Atomic unsigned *map) { // expected-warning{{'map' is an unsafe pointer used for buffer access}}
  map[5] = 5; // expected-note{{used in buffer access here}}
}

// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:33-[[@LINE+1]]:46}:"std::span<unsigned> map"
void typeLocSourceLocationValid(unsigned *map) { // expected-warning{{'map' is an unsafe pointer used for buffer access}} \
						    expected-note{{change type of 'map' to 'std::span' to preserve bounds information}}
  map[5] = 5; // expected-note{{used in buffer access here}}
}
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void typeLocSourceLocationValid(unsigned *map) {return typeLocSourceLocationValid(std::span<unsigned>(map, <# size #>));}\n"

// We do not fix parameters participating unsafe operations for the
// following functions/methods or function-like expressions:

// CHECK-NOT: fix-it:
class A {
  // constructor & descructor
  A(int * p) {  // expected-warning{{'p' is an unsafe pointer used for buffer access}}
    int tmp;
    tmp = p[5]; // expected-note{{used in buffer access here}}
  }

  // class member methods
  void foo(int *p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}}
    int tmp;
    tmp = p[5];      // expected-note{{used in buffer access here}}
  }

  // overload operator
  int operator+(int * p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}}
    int tmp;
    tmp = p[5];            // expected-note{{used in buffer access here}}
    return tmp;
  }
};

// lambdas
void foo() {
  auto Lamb = [&](int *p) // expected-warning{{'p' is an unsafe pointer used for buffer access}}
    -> int {
    int tmp;
    tmp = p[5];           // expected-note{{used in buffer access here}}
    return tmp;
  };
}

// template
template<typename T>
void template_foo(T * p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}}
  T tmp;
  tmp = p[5];              // expected-note{{used in buffer access here}}
}

void instantiate_template_foo() {
  int * p;
  template_foo(p);        // FIXME expected note {{in instantiation of function template specialization 'template_foo<int>' requested here}}
}

// variadic function
void vararg_foo(int * p...) { // expected-warning{{'p' is an unsafe pointer used for buffer access}}
  int tmp;
  tmp = p[5];                 // expected-note{{used in buffer access here}}
}

// constexpr functions
constexpr int constexpr_foo(int * p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}}
  return p[5];                         // expected-note{{used in buffer access here}}
}

// function body is a try-block
void fn_with_try_block(int* p)    // expected-warning{{'p' is an unsafe pointer used for buffer access}}
  try {
    int tmp;

    if (p == nullptr)
      throw 42;
    tmp = p[5];                   // expected-note{{used in buffer access here}}
  }
  catch (int) {
    *p = 0;
  }

// The following two unsupported cases are not specific to
// parm-fixits. Adding them here in case they get forgotten.
void isArrayDecayToPointerUPC(int a[][10], int (*b)[10]) {
// expected-warning@-1{{'a' is an unsafe pointer used for buffer access}}
// expected-warning@-2{{'b' is an unsafe pointer used for buffer access}}
  int tmp;

  tmp = a[5][5] + b[5][5];  // expected-note2{{used in buffer access here}}
}

// parameter having default values:
void parmWithDefaultValue(int * x = 0) {
  // expected-warning@-1{{'x' is an unsafe pointer used for buffer access}}
  int tmp;
  tmp = x[5]; // expected-note{{used in buffer access here}}
}

void parmWithDefaultValueDecl(int * x = 0);

void parmWithDefaultValueDecl(int * x) {
  // expected-warning@-1{{'x' is an unsafe pointer used for buffer access}}
  int tmp;
  tmp = x[5]; // expected-note{{used in buffer access here}}
}

#define MACRO_NAME MyName

// The fix-it ends with a macro. It will be discarded due to overlap with macros.
// CHECK-NOT: fix-it:{{.*}}:{[[@LINE+1]]
void macroIdentifier(int * MACRO_NAME) { // expected-warning{{'MyName' is an unsafe pointer used for buffer access}}
  if (++MyName){} // expected-note{{used in pointer arithmetic here}}
}

// CHECK-NOT: fix-it:{{.*}}:{[[@LINE+1]]
void parmHasNoName(int *p, int *) { // cannot fix the function because there is one parameter has no name. \
				       expected-warning{{'p' is an unsafe pointer used for buffer access}}
  p[5] = 5; // expected-note{{used in buffer access here}}
}
