// RUN: %check_clang_tidy %s cppcoreguidelines-missing-std-forward %t -- -- -fno-delayed-template-parsing

// NOLINTBEGIN
namespace std {

template <typename T> struct remove_reference      { using type = T; };
template <typename T> struct remove_reference<T&>  { using type = T; };
template <typename T> struct remove_reference<T&&> { using type = T; };

template <typename T> using remove_reference_t = typename remove_reference<T>::type;

template <typename T> constexpr T &&forward(remove_reference_t<T> &t) noexcept;
template <typename T> constexpr T &&forward(remove_reference_t<T> &&t) noexcept;
template <typename T> constexpr remove_reference_t<T> &&move(T &&x);

} // namespace std
// NOLINTEND

struct S {
  S();
  S(const S&);
  S(S&&) noexcept;
  S& operator=(const S&);
  S& operator=(S&&) noexcept;
};

template <class... Ts>
void consumes_all(Ts&&...);

namespace positive_cases {

template <class T>
void does_not_forward(T&& t) {
  // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward]
  T other = t;
}

template <class T>
void does_not_forward_invoked(T&& t) {
  // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward]
  T other = t();
}

template <class T>
void forwards_pairwise(T&& t) {
  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward]
  auto first = std::forward<T>(t.first);
  auto second = std::forward<T>(t.second);
}

template <class... Ts>
void does_not_forward_pack(Ts&&... ts) {
  // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: forwarding reference parameter 'ts' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward]
  consumes_all(ts...);
}

template <class T>
class AClass {

  template <class U>
  AClass(U&& u) : data(u) {}
  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: forwarding reference parameter 'u' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward]

  template <class U>
  AClass& operator=(U&& u) { }
  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: forwarding reference parameter 'u' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward]

  template <class U>
  void mixed_params(T&& t, U&& u) {
    // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: forwarding reference parameter 'u' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward]
    T other1 = std::move(t);
    U other2 = std::move(u);
  }

  T data;
};

template <class T>
void does_not_forward_in_evaluated_code(T&& t) {
  // CHECK-MESSAGES: :[[@LINE-1]]:45: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward]
  using result_t = decltype(std::forward<T>(t));
  unsigned len = sizeof(std::forward<T>(t));
  T other = t;
}

template <class T>
void lambda_value_capture(T&& t) {
  // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward]
  [=]() { T other = std::forward<T>(t); };
}

template <class T>
void lambda_value_capture_copy(T&& t) {
  // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward]
  [&,t]() { T other = std::forward<T>(t); };
}

template <typename X>
void use(const X &x) {}

template <typename X, typename Y>
void foo(X &&x, Y &&y) {
  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: forwarding reference parameter 'y' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward]
    use(std::forward<X>(x));
    use(y);
}

} // namespace positive_cases

namespace negative_cases {

template <class T>
void just_a_decl(T&&t);

template <class T>
void does_forward(T&& t) {
  T other = std::forward<T>(t);
}

template <class... Ts>
void does_forward_pack(Ts&&... ts) {
  consumes_all(std::forward<Ts>(ts)...);
}

void pass_by_value(S s) {
  S other = std::move(s);
}

void lvalue_ref(S& s) {
  S other = std::move(s);
}

void rvalue_ref(S&& s) {
  S other = std::move(s);
}

template <class T>
void templated_rvalue_ref(std::remove_reference_t<T>&& t) {
  T other = std::move(t);
}

template <class T>
class AClass {

  template <class U>
  AClass(U&& u) : data(std::forward<U>(u)) {}

  template <class U>
  AClass& operator=(U&& u) {
    data = std::forward<U>(u);
  }

  void rvalue_ref(T&& t) {
    T other = std::move(t);
  }

  T data;
};

template <class T>
void lambda_value_reference(T&& t) {
  [&]() { T other = std::forward<T>(t); };
}

template<typename T>
void lambda_value_reference_capture_list_ref_1(T&& t) {
    [=, &t] { T other = std::forward<T>(t); };
}

template<typename T>
void lambda_value_reference_capture_list_ref_2(T&& t) {
    [&t] { T other = std::forward<T>(t); };
}

template<typename T>
void lambda_value_reference_capture_list(T&& t) {
    [t = std::forward<T>(t)] { t(); };
}

template <class T>
void lambda_value_reference_auxiliary_var(T&& t) {
  [&x = t]() { T other = std::forward<T>(x); };
}

} // namespace negative_cases

namespace deleted_functions {

template <typename T>
void f(T &&t) = delete;

struct S {
    template <typename T>
    S(T &&t) = delete;

    template <typename T>
    void operator&(T &&t) = delete;
};

} // namespace deleted_functions

namespace unused_arguments {

template<typename F>
void unused_argument1(F&&) {}

template<typename F>
void unused_argument2([[maybe_unused]] F&& f) {}

template<typename F>
void unused_argument3(F&& _) {}

} // namespace unused_arguments
