// RUN: %clang_cc1 --std=c++20 -fsyntax-only -verify -Wdangling-capture %s

#include "Inputs/lifetime-analysis.h"

// ****************************************************************************
// Capture an integer
// ****************************************************************************
namespace capture_int {
struct X {} x;
void captureInt(const int &i [[clang::lifetime_capture_by(x)]], X &x);
void captureRValInt(int &&i [[clang::lifetime_capture_by(x)]], X &x);
void noCaptureInt(int i [[clang::lifetime_capture_by(x)]], X &x);

void use() {
  int local;
  captureInt(1, // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
            x);
  captureRValInt(1, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
  captureInt(local, x);
  noCaptureInt(1, x);
  noCaptureInt(local, x);
}
} // namespace capture_int

// ****************************************************************************
// Capture std::string (gsl owner types)
// ****************************************************************************
namespace capture_string {
struct X {} x;
void captureString(const std::string &s [[clang::lifetime_capture_by(x)]], X &x);
void captureRValString(std::string &&s [[clang::lifetime_capture_by(x)]], X &x);

void use() {
  std::string local_string;
  captureString(std::string(), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
  captureString(local_string, x);
  captureRValString(std::move(local_string), x);
  captureRValString(std::string(), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
}
} // namespace capture_string

// ****************************************************************************
// Capture std::string_view (gsl pointer types)
// ****************************************************************************
namespace capture_string_view {
struct X {} x;
void captureStringView(std::string_view s [[clang::lifetime_capture_by(x)]], X &x);
void captureRValStringView(std::string_view &&sv [[clang::lifetime_capture_by(x)]], X &x);
void noCaptureStringView(std::string_view sv, X &x);

std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
std::string_view getNotLifetimeBoundView(const std::string& s);
const std::string& getLifetimeBoundString(const std::string &s [[clang::lifetimebound]]);
const std::string& getLifetimeBoundString(std::string_view sv [[clang::lifetimebound]]);

void use() {
  std::string_view local_string_view;
  std::string local_string;
  captureStringView(local_string_view, x);
  captureStringView(std::string(), // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
            x);

  captureStringView(getLifetimeBoundView(local_string), x);
  captureStringView(getNotLifetimeBoundView(std::string()), x);
  captureRValStringView(std::move(local_string_view), x);
  captureRValStringView(std::string(), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
  captureRValStringView(std::string_view{"abcd"}, x);

  noCaptureStringView(local_string_view, x);
  noCaptureStringView(std::string(), x);

  // With lifetimebound functions.
  captureStringView(getLifetimeBoundView(
  std::string() // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
  ), x);
  captureRValStringView(getLifetimeBoundView(local_string), x);
  captureRValStringView(getLifetimeBoundView(std::string()), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
  captureRValStringView(getNotLifetimeBoundView(std::string()), x);
  noCaptureStringView(getLifetimeBoundView(std::string()), x);
  captureStringView(getLifetimeBoundString(std::string()), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
  captureStringView(getLifetimeBoundString(getLifetimeBoundView(std::string())), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
  captureStringView(getLifetimeBoundString(getLifetimeBoundString(
    std::string()  // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
    )), x);
}
} // namespace capture_string_view

// ****************************************************************************
// Capture pointer (eg: std::string*)
// ****************************************************************************
const std::string* getLifetimeBoundPointer(const std::string &s [[clang::lifetimebound]]);
const std::string* getNotLifetimeBoundPointer(const std::string &s);

namespace capture_pointer {
struct X {} x;
void capturePointer(const std::string* sp [[clang::lifetime_capture_by(x)]], X &x);
void use() {
  capturePointer(getLifetimeBoundPointer(std::string()), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
  capturePointer(getLifetimeBoundPointer(*getLifetimeBoundPointer(
    std::string()  // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
    )), x);
  capturePointer(getNotLifetimeBoundPointer(std::string()), x);

}
} // namespace capture_pointer

// ****************************************************************************
// Arrays and initializer lists.
// ****************************************************************************
namespace init_lists {
struct X {} x;
void captureVector(const std::vector<int> &a [[clang::lifetime_capture_by(x)]], X &x);
void captureArray(int array [[clang::lifetime_capture_by(x)]] [2], X &x);
void captureInitList(std::initializer_list<int> abc [[clang::lifetime_capture_by(x)]], X &x);


std::initializer_list<int> getLifetimeBoundInitList(std::initializer_list<int> abc [[clang::lifetimebound]]);

void use() {
  captureVector({1, 2, 3}, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
  captureVector(std::vector<int>{}, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
  std::vector<int> local_vector;
  captureVector(local_vector, x);
  int local_array[2]; 
  captureArray(local_array, x);
  captureInitList({1, 2}, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
  captureInitList(getLifetimeBoundInitList({1, 2}), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
}
} // namespace init_lists

// ****************************************************************************
// Implicit object param 'this' is captured
// ****************************************************************************
namespace this_is_captured {
struct X {} x;
struct S {
  void capture(X &x) [[clang::lifetime_capture_by(x)]];
};
void use() {
  S{}.capture(x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
  S s;
  s.capture(x);
}
} // namespace this_is_captured

namespace temporary_capturing_object {
struct S {
  void add(const int& x [[clang::lifetime_capture_by(this)]]);
};

void test() {
  // We still give an warning even the capturing object is a temoprary.
  // It is possible that the capturing object uses the captured object in its
  // destructor.
  S().add(1); // expected-warning {{object whose reference is captured}}
  S{}.add(1); // expected-warning {{object whose reference is captured}}
}
} // namespace ignore_temporary_class_object

// ****************************************************************************
// Capture by Global and Unknown.
// ****************************************************************************
namespace capture_by_global_unknown {
void captureByGlobal(std::string_view s [[clang::lifetime_capture_by(global)]]);
void captureByUnknown(std::string_view s [[clang::lifetime_capture_by(unknown)]]);

std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);

void use() {  
  std::string_view local_string_view;
  std::string local_string;
  // capture by global.
  captureByGlobal(std::string()); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}
  captureByGlobal(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}
  captureByGlobal(local_string);
  captureByGlobal(local_string_view);

  // capture by unknown.
  captureByUnknown(std::string()); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}
  captureByUnknown(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}
  captureByUnknown(local_string);
  captureByUnknown(local_string_view);
}
} // namespace capture_by_global_unknown

// ****************************************************************************
// Member functions: Capture by 'this'
// ****************************************************************************
namespace capture_by_this {
struct S {
  void captureInt(const int& x [[clang::lifetime_capture_by(this)]]);
  void captureView(std::string_view sv [[clang::lifetime_capture_by(this)]]);
};
std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
std::string_view getNotLifetimeBoundView(const std::string& s);
const std::string& getLifetimeBoundString(const std::string &s [[clang::lifetimebound]]);

void use() {
  S s;
  s.captureInt(1); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}}
  s.captureView(std::string()); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}}
  s.captureView(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}}
  s.captureView(getLifetimeBoundString(std::string()));  // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}}
  s.captureView(getNotLifetimeBoundView(std::string()));
}  
} // namespace capture_by_this

// ****************************************************************************
// Struct with field as a reference
// ****************************************************************************
namespace reference_field {
struct X {} x;
struct Foo {
  const int& b;
};
void captureField(Foo param [[clang::lifetime_capture_by(x)]], X &x);
void use() {
  captureField(Foo{
    1 // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
  }, x);
  int local;
  captureField(Foo{local}, x);
}
} // namespace reference_field

// ****************************************************************************
// Capture default argument.
// ****************************************************************************
namespace default_arg {
struct X {} x;
void captureDefaultArg(X &x, std::string_view s [[clang::lifetime_capture_by(x)]] = std::string());

std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);

void useCaptureDefaultArg() {
  X x;
  captureDefaultArg(x); // FIXME: Diagnose temporary default arg.
  captureDefaultArg(x, std::string("temp")); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
  captureDefaultArg(x, getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
  std::string local;
  captureDefaultArg(x, local);
}
} // namespace default_arg 

// ****************************************************************************
// Container: *No* distinction between pointer-like and other element type
// ****************************************************************************
namespace containers_no_distinction {
template<class T>
struct MySet {
  void insert(T&& t [[clang::lifetime_capture_by(this)]]);
  void insert(const T& t [[clang::lifetime_capture_by(this)]]);
};
void user_defined_containers() {
  MySet<int> set_of_int;
  set_of_int.insert(1); // expected-warning {{object whose reference is captured by 'set_of_int' will be destroyed at the end of the full-expression}}
  MySet<std::string_view> set_of_sv;
  set_of_sv.insert(std::string());  // expected-warning {{object whose reference is captured by 'set_of_sv' will be destroyed at the end of the full-expression}}
  set_of_sv.insert(std::string_view());
}
} // namespace containers_no_distinction

// ****************************************************************************
// Container: Different for pointer-like and other element type.
// ****************************************************************************
namespace conatiners_with_different {
template<typename T> struct IsPointerLikeTypeImpl : std::false_type {};
template<> struct IsPointerLikeTypeImpl<std::string_view> : std::true_type {};
template<typename T> concept IsPointerLikeType = std::is_pointer<T>::value || IsPointerLikeTypeImpl<T>::value;

template<class T> struct MyVector {
  void push_back(T&& t [[clang::lifetime_capture_by(this)]]) requires IsPointerLikeType<T>;
  void push_back(const T& t [[clang::lifetime_capture_by(this)]]) requires IsPointerLikeType<T>;

  void push_back(T&& t) requires (!IsPointerLikeType<T>);
  void push_back(const T& t) requires (!IsPointerLikeType<T>);
};

std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);

void use_container() {
  std::string local;

  MyVector<std::string> vector_of_string;
  vector_of_string.push_back(std::string()); // Ok.
  
  MyVector<std::string_view> vector_of_view;
  vector_of_view.push_back(std::string()); // expected-warning {{object whose reference is captured by 'vector_of_view' will be destroyed at the end of the full-expression}}
  vector_of_view.push_back(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 'vector_of_view' will be destroyed at the end of the full-expression}}
  
  MyVector<const std::string*> vector_of_pointer;
  vector_of_pointer.push_back(getLifetimeBoundPointer(std::string())); // expected-warning {{object whose reference is captured by 'vector_of_pointer' will be destroyed at the end of the full-expression}}
  vector_of_pointer.push_back(getLifetimeBoundPointer(*getLifetimeBoundPointer(std::string()))); // expected-warning {{object whose reference is captured by 'vector_of_pointer' will be destroyed at the end of the full-expression}}
  vector_of_pointer.push_back(getLifetimeBoundPointer(local));
  vector_of_pointer.push_back(getNotLifetimeBoundPointer(std::string()));
}

// ****************************************************************************
// Container: For user defined view types
// ****************************************************************************
struct [[gsl::Pointer()]] MyStringView : public std::string_view {
  MyStringView();
  MyStringView(std::string_view&&);
  MyStringView(const MyStringView&);
  MyStringView(const std::string&);
};
template<> struct IsPointerLikeTypeImpl<MyStringView> : std::true_type {};

std::optional<std::string_view> getOptionalSV();
std::optional<std::string> getOptionalS();
std::optional<MyStringView> getOptionalMySV();
MyStringView getMySV();

class MyStringViewNotPointer : public std::string_view {};
std::optional<MyStringViewNotPointer> getOptionalMySVNotP();
MyStringViewNotPointer getMySVNotP();

std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
std::string_view getNotLifetimeBoundView(const std::string& s);
const std::string& getLifetimeBoundString(const std::string &s [[clang::lifetimebound]]);
const std::string& getLifetimeBoundString(std::string_view sv [[clang::lifetimebound]]);

void use_my_view() {
  std::string local;
  MyVector<MyStringView> vector_of_my_view;
  vector_of_my_view.push_back(getMySV());
  vector_of_my_view.push_back(MyStringView{});
  vector_of_my_view.push_back(std::string_view{});
  vector_of_my_view.push_back(std::string{}); // expected-warning {{object whose reference is captured by 'vector_of_my_view' will be destroyed at the end of the full-expression}}
  vector_of_my_view.push_back(getLifetimeBoundView(std::string{})); // expected-warning {{object whose reference is captured by 'vector_of_my_view' will be destroyed at the end of the full-expression}}
  vector_of_my_view.push_back(getLifetimeBoundString(getLifetimeBoundView(std::string{}))); // expected-warning {{object whose reference is captured by 'vector_of_my_view' will be destroyed at the end of the full-expression}}
  vector_of_my_view.push_back(getNotLifetimeBoundView(getLifetimeBoundString(getLifetimeBoundView(std::string{}))));
  
  // Use with container of other view types.
  MyVector<std::string_view> vector_of_view;
  vector_of_view.push_back(getMySV());
  vector_of_view.push_back(getMySVNotP());
}

// ****************************************************************************
// Container: Use with std::optional<view> (owner<pointer> types)
// ****************************************************************************
void use_with_optional_view() {
  MyVector<std::string_view> vector_of_view;

  std::optional<std::string_view> optional_of_view;
  vector_of_view.push_back(optional_of_view.value());
  vector_of_view.push_back(getOptionalS().value()); // expected-warning {{object whose reference is captured by 'vector_of_view' will be destroyed at the end of the full-expression}}
  
  vector_of_view.push_back(getOptionalSV().value());
  vector_of_view.push_back(getOptionalMySV().value());
  vector_of_view.push_back(getOptionalMySVNotP().value());
}
} // namespace conatiners_with_different

// ****************************************************************************
// Capture 'temporary' views
// ****************************************************************************
namespace temporary_views {
void capture1(std::string_view s [[clang::lifetime_capture_by(x)]], std::vector<std::string_view>& x);

// Intended to capture the "string_view" itself
void capture2(const std::string_view& s [[clang::lifetime_capture_by(x)]], std::vector<std::string_view*>& x);
// Intended to capture the pointee of the "string_view"
void capture3(const std::string_view& s [[clang::lifetime_capture_by(x)]], std::vector<std::string_view>& x);

void use() {
  std::vector<std::string_view> x1;
  capture1(std::string(), x1); // expected-warning {{object whose reference is captured by 'x1' will be destroyed at the end of the full-expression}}
  capture1(std::string_view(), x1);

  std::vector<std::string_view*> x2;
  // Clang considers 'const std::string_view&' to refer to the owner
  // 'std::string' and not 'std::string_view'. Therefore no diagnostic here.
  capture2(std::string_view(), x2);
  capture2(std::string(), x2); // expected-warning {{object whose reference is captured by 'x2' will be destroyed at the end of the full-expression}}
  
  std::vector<std::string_view> x3;
  capture3(std::string_view(), x3);
  capture3(std::string(), x3); // expected-warning {{object whose reference is captured by 'x3' will be destroyed at the end of the full-expression}}
}
} // namespace temporary_views

// ****************************************************************************
// Inferring annotation for STL containers
// ****************************************************************************
namespace inferred_capture_by {
const std::string* getLifetimeBoundPointer(const std::string &s [[clang::lifetimebound]]);
const std::string* getNotLifetimeBoundPointer(const std::string &s);

std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
std::string_view getNotLifetimeBoundView(const std::string& s);
void use() {
  std::string local;
  std::vector<std::string_view> views;
  views.push_back(std::string()); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}}
  views.insert(views.begin(), 
            std::string()); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}}
  views.push_back(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}}
  views.push_back(getNotLifetimeBoundView(std::string()));
  views.push_back(local);
  views.insert(views.end(), local);

  std::vector<std::string> strings;
  strings.push_back(std::string());
  strings.insert(strings.begin(), std::string());

  std::vector<const std::string*> pointers;
  pointers.push_back(getLifetimeBoundPointer(std::string())); // expected-warning {{object whose reference is captured by 'pointers' will be destroyed at the end of the full-expression}}
  pointers.push_back(&local);
}

namespace with_span {
// Templated view types.
template<typename T>
struct [[gsl::Pointer]] Span {
  Span(const std::vector<T> &V);
};

void use() {
  std::vector<Span<int>> spans;
  spans.push_back(std::vector<int>{1, 2, 3}); // expected-warning {{object whose reference is captured by 'spans' will be destroyed at the end of the full-expression}}
  std::vector<int> local;
  spans.push_back(local);
}
} // namespace with_span
} // namespace inferred_capture_by

namespace on_constructor {
struct T {
  T(const int& t [[clang::lifetime_capture_by(this)]]);
};
struct T2 {
  T2(const int& t [[clang::lifetime_capture_by(x)]], int& x);
};
struct T3 {
  T3(const T& t [[clang::lifetime_capture_by(this)]]);
};

int foo(const T& t);
int bar(const T& t[[clang::lifetimebound]]);

void test() {
  auto x = foo(T(1)); // OK. no diagnosic
  T(1); // OK. no diagnostic
  T t(1); // expected-warning {{temporary whose address is used}}
  auto y = bar(T(1)); // expected-warning {{temporary whose address is used}}
  T3 t3(T(1)); // expected-warning {{temporary whose address is used}}
    
  int a;
  T2(1, a); // expected-warning {{object whose reference is captured by}}
}
} // namespace on_constructor
