ccotter updated this revision to Diff 491201.
ccotter added a comment.

- Use nested namespaces


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D141892/new/

https://reviews.llvm.org/D141892

Files:
  clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
  clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
  clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp
  clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.h
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/docs/clang-tidy/checks/list.rst
  clang-tools-extra/docs/clang-tidy/checks/modernize/use-constraints.rst
  
clang-tools-extra/test/clang-tidy/checkers/modernize/use-constraints-first-greatergreater.cpp
  clang-tools-extra/test/clang-tidy/checkers/modernize/use-constraints.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/modernize/use-constraints.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/modernize/use-constraints.cpp
@@ -0,0 +1,693 @@
+// RUN: %check_clang_tidy -std=c++20 %s modernize-use-constraints %t -- -- -fno-delayed-template-parsing
+
+// NOLINTBEGIN
+namespace std {
+template <bool B, class T = void> struct enable_if { };
+
+template <class T> struct enable_if<true, T> { typedef T type; };
+
+template <bool B, class T = void>
+using enable_if_t = typename enable_if<B, T>::type;
+
+} // namespace std
+// NOLINTEND
+
+template <typename...>
+struct ConsumeVariadic;
+
+struct Obj {
+};
+
+namespace enable_if_in_return_type {
+
+////////////////////////////////
+// Section 1: enable_if in return type of function
+////////////////////////////////
+
+////////////////////////////////
+// General tests
+////////////////////////////////
+
+template <typename T>
+typename std::enable_if<T::some_value, Obj>::type basic() {
+  return Obj{};
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}Obj basic() requires T::some_value {{{$}}
+
+template <typename T>
+std::enable_if_t<T::some_value, Obj> basic_t() {
+  return Obj{};
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}Obj basic_t() requires T::some_value {{{$}}
+
+template <typename T>
+auto basic_trailing() -> typename std::enable_if<T::some_value, Obj>::type {
+  return Obj{};
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:26: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}auto basic_trailing() -> Obj requires T::some_value {{{$}}
+
+template <typename T>
+typename std::enable_if<T::some_value, Obj>::type existing_constraint() requires (T::another_value) {
+  return Obj{};
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}typename std::enable_if<T::some_value, Obj>::type existing_constraint() requires (T::another_value) {{{$}}
+
+template <typename U>
+typename std::enable_if<U::some_value, Obj>::type decl_without_def();
+
+template <typename U>
+typename std::enable_if<U::some_value, Obj>::type decl_with_separate_def();
+
+template <typename U>
+typename std::enable_if<U::some_value, Obj>::type decl_with_separate_def() {
+  return Obj{};
+}
+// FIXME - Support definitions with separate decls
+
+template <typename U>
+std::enable_if_t<true, Obj> no_dependent_type(U) {
+  return Obj{};
+}
+// FIXME - Support non-dependent enable_ifs. Low priority though...
+
+template <typename T>
+typename std::enable_if<T::some_value, int>::type* pointer_of_enable_if() {
+  return nullptr;
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T>{{$}}
+// CHECK-FIXES-NEXT: {{^}}int* pointer_of_enable_if() requires T::some_value {{{$}}
+
+template <typename T>
+std::enable_if_t<T::some_value, int>* pointer_of_enable_if_t() {
+  return nullptr;
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T>{{$}}
+// CHECK-FIXES-NEXT: {{^}}int* pointer_of_enable_if_t() requires T::some_value {{{$}}
+
+template <typename T>
+const std::enable_if_t<T::some_value, int>* const_pointer_of_enable_if_t() {
+  return nullptr;
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T>{{$}}
+// CHECK-FIXES-NEXT: {{^}}const int* const_pointer_of_enable_if_t() requires T::some_value {{{$}}
+
+template <typename T>
+std::enable_if_t<T::some_value, int> const * const_pointer_of_enable_if_t2() {
+  return nullptr;
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T>{{$}}
+// CHECK-FIXES-NEXT: {{^}}int const * const_pointer_of_enable_if_t2() requires T::some_value {{{$}}
+
+
+template <typename T>
+std::enable_if_t<T::some_value, int>& reference_of_enable_if_t() {
+  static int x; return x;
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T>{{$}}
+// CHECK-FIXES-NEXT: {{^}}int& reference_of_enable_if_t() requires T::some_value {{{$}}
+
+template <typename T>
+const std::enable_if_t<T::some_value, int>& const_reference_of_enable_if_t() {
+  static int x; return x;
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T>{{$}}
+// CHECK-FIXES-NEXT: {{^}}const int& const_reference_of_enable_if_t() requires T::some_value {{{$}}
+
+template <typename T>
+typename std::enable_if<T::some_value>::type enable_if_default_void() {
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}void enable_if_default_void() requires T::some_value {{{$}}
+
+template <typename T>
+std::enable_if_t<T::some_value> enable_if_t_default_void() {
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}void enable_if_t_default_void() requires T::some_value {{{$}}
+
+template <typename T>
+std::enable_if_t<T::some_value>* enable_if_t_default_void_pointer() {
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}void* enable_if_t_default_void_pointer() requires T::some_value {{{$}}
+
+namespace using_namespace_std {
+
+using namespace std;
+
+template <typename T>
+typename enable_if<T::some_value>::type with_typename() {
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}void with_typename() requires T::some_value {{{$}}
+
+template <typename T>
+enable_if_t<T::some_value> with_t() {
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}void with_t() requires T::some_value {{{$}}
+
+template <typename T>
+typename enable_if<T::some_value, int>::type with_typename_and_type() {
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}int with_typename_and_type() requires T::some_value {{{$}}
+
+template <typename T>
+enable_if_t<T::some_value, int> with_t_and_type() {
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}int with_t_and_type() requires T::some_value {{{$}}
+
+} // namespace using_namespace_std
+
+
+////////////////////////////////
+// Negative tests - incorrect uses of enable_if
+////////////////////////////////
+template <typename U>
+std::enable_if<U::some_value, Obj> not_enable_if() {
+  return {};
+}
+template <typename U>
+typename std::enable_if<U::some_value, Obj>::type123 not_enable_if_wrong_type() {
+  return {};
+}
+template <typename U>
+typename std::enable_if_t<U::some_value, Obj>::type not_enable_if_t() {
+  return {};
+}
+template <typename U>
+typename std::enable_if_t<U::some_value, Obj>::type123 not_enable_if_t_again() {
+  return {};
+}
+template <typename U>
+std::enable_if<U::some_value, int>* not_pointer_of_enable_if() {
+  return nullptr;
+}
+template <typename U>
+typename std::enable_if<U::some_value, int>::type123 * not_pointer_of_enable_if_t() {
+  return nullptr;
+}
+
+
+namespace primary_expression_tests {
+
+////////////////////////////////
+// Primary/non-primary expression tests
+////////////////////////////////
+
+template <typename T> struct Traits;
+
+template <typename T>
+std::enable_if_t<Traits<T>::value> type_trait_value() {
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}void type_trait_value() requires Traits<T>::value {{{$}}
+
+template <typename T>
+std::enable_if_t<Traits<T>::member()> type_trait_member_call() {
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}void type_trait_member_call() requires (Traits<T>::member()) {{{$}}
+
+template <typename T>
+std::enable_if_t<!Traits<T>::value> negate() {
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}void negate() requires (!Traits<T>::value) {{{$}}
+
+template <typename T>
+std::enable_if_t<Traits<T>::value1 && Traits<T>::value2> conjunction() {
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}void conjunction() requires (Traits<T>::value1 && Traits<T>::value2) {{{$}}
+
+template <typename T>
+std::enable_if_t<Traits<T>::value1 || Traits<T>::value2> disjunction() {
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}void disjunction() requires (Traits<T>::value1 || Traits<T>::value2) {{{$}}
+
+template <typename T>
+std::enable_if_t<Traits<T>::value1 && !Traits<T>::value2> conjunction_with_negate() {
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}void conjunction_with_negate() requires (Traits<T>::value1 && !Traits<T>::value2) {{{$}}
+
+template <typename T>
+std::enable_if_t<Traits<T>::value1 == (Traits<T>::value2 + 5)> complex_operators() {
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}void complex_operators() requires (Traits<T>::value1 == (Traits<T>::value2 + 5)) {{{$}}
+
+} // namespace primary_expression_tests
+
+
+////////////////////////////////
+// Functions with specifier
+////////////////////////////////
+
+template <typename T>
+constexpr typename std::enable_if<T::some_value, int>::type constexpr_decl() {
+  return 10;
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:11: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}constexpr int constexpr_decl() requires T::some_value {{{$}}
+
+template <typename T>
+static inline constexpr typename std::enable_if<T::some_value, int>::type static_inline_constexpr_decl() {
+  return 10;
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:25: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}static inline constexpr int static_inline_constexpr_decl() requires T::some_value {{{$}}
+
+template <typename T>
+static
+typename std::enable_if<T::some_value, int>::type
+static_decl() {
+  return 10;
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}static{{$}}
+// CHECK-FIXES-NEXT: {{^}}int{{$}}
+// CHECK-FIXES-NEXT: {{^}}static_decl() requires T::some_value {{{$}}
+
+template <typename T>
+constexpr /* comment */ typename std::enable_if<T::some_value, int>::type constexpr_comment_decl() {
+  return 10;
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:25: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}constexpr /* comment */ int constexpr_comment_decl() requires T::some_value {{{$}}
+
+
+////////////////////////////////
+// Class definition tests
+////////////////////////////////
+
+struct AClass {
+  template <typename T>
+  static typename std::enable_if<T::some_value, Obj>::type static_method() {
+    return Obj{};
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:10: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+  // CHECK-FIXES: {{^}}  static Obj static_method() requires T::some_value {{{$}}
+
+  template <typename T>
+  typename std::enable_if<T::some_value, Obj>::type member() {
+    return Obj{};
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+  // CHECK-FIXES: {{^}}  Obj member() requires T::some_value {{{$}}
+
+  template <typename T>
+  typename std::enable_if<T::some_value, Obj>::type const_qualifier() const {
+    return Obj{};
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+  // CHECK-FIXES: {{^}}  Obj const_qualifier() const requires T::some_value {{{$}}
+
+  template <typename T>
+  typename std::enable_if<T::some_value, Obj>::type rvalue_ref_qualifier() && {
+    return Obj{};
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+  // CHECK-FIXES: {{^}}  Obj rvalue_ref_qualifier() && requires T::some_value {{{$}}
+
+  template <typename T>
+  typename std::enable_if<T::some_value, Obj>::type rvalue_ref_qualifier_comment() /* c1 */ && /* c2 */ {
+    return Obj{};
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+  // CHECK-FIXES: {{^}}  Obj rvalue_ref_qualifier_comment() /* c1 */ && /* c2 */ requires T::some_value {{{$}}
+
+  template <typename T>
+  std::enable_if_t<T::some_value, AClass&> operator=(T&&) = delete;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+  // CHECK-FIXES: {{^}}  AClass& operator=(T&&) requires T::some_value = delete;
+
+};
+
+
+////////////////////////////////
+// Comments and whitespace tests
+////////////////////////////////
+
+template <typename T>
+typename std::enable_if</* check1 */ T::some_value, Obj>::type leading_comment() {
+  return Obj{};
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}Obj leading_comment() requires /* check1 */ T::some_value {{{$}}
+
+template <typename T>
+typename std::enable_if<T::some_value, Obj>::type body_on_next_line()
+{
+  return Obj{};
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}Obj body_on_next_line(){{$}}
+// CHECK-FIXES-NEXT: {{^}}requires T::some_value {{{$}}
+
+template <typename T>
+typename std::enable_if<  /* check1 */ T::some_value, Obj>::type leading_comment_whitespace() {
+  return Obj{};
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}Obj leading_comment_whitespace() requires /* check1 */ T::some_value {{{$}}
+
+template <typename T>
+typename std::enable_if</* check1 */ T::some_value /* check2 */, Obj>::type leading_and_trailing_comment() {
+  return Obj{};
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}Obj leading_and_trailing_comment() requires /* check1 */ T::some_value /* check2 */ {{{$}}
+
+template <typename T, typename U>
+typename std::enable_if<T::some_value &&
+                        U::another_value, Obj>::type condition_on_two_lines() {
+  return Obj{};
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}Obj condition_on_two_lines() requires (T::some_value &&{{$}}
+// CHECK-FIXES-NEXT: U::another_value) {{{$}}
+
+template <typename T>
+typename std::enable_if<T::some_value, int> :: type* pointer_of_enable_if_t_with_spaces() {
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T>{{$}}
+// CHECK-FIXES-NEXT: {{^}}int* pointer_of_enable_if_t_with_spaces() requires T::some_value {{{$}}
+
+template <typename T>
+typename std::enable_if<T::some_value, int> :: /*c*/ type* pointer_of_enable_if_t_with_comment() {
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T>{{$}}
+// CHECK-FIXES-NEXT: {{^}}int* pointer_of_enable_if_t_with_comment() requires T::some_value {{{$}}
+
+template <typename T>
+std::enable_if_t<T::some_value // comment
+              > trailing_slash_slash_comment() {
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T>{{$}}
+// CHECK-FIXES-NEXT: {{^}}void trailing_slash_slash_comment() requires T::some_value // comment{{$}}
+// CHECK-FIXES-NEXT: {{^}}               {{{$}}
+
+} // namespace enable_if_in_return_type
+
+
+namespace enable_if_trailing_non_type_parameter {
+
+////////////////////////////////
+// Section 2: enable_if as final template non-type parameter
+////////////////////////////////
+
+template <typename T, typename std::enable_if<T::some_value, int>::type = 0>
+void basic() {
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:23: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T>{{$}}
+// CHECK-FIXES-NEXT: {{^}}void basic() requires T::some_value {{{$}}
+
+template <typename T, std::enable_if_t<T::some_value, int> = 0>
+void basic_t() {
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:23: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T>{{$}}
+// CHECK-FIXES-NEXT: {{^}}void basic_t() requires T::some_value {{{$}}
+
+template <typename T, template <typename> class U, class V, std::enable_if_t<T::some_value, int> = 0>
+void basic_many_template_params() {
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:61: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T, template <typename> class U, class V>{{$}}
+// CHECK-FIXES-NEXT: {{^}}void basic_many_template_params() requires T::some_value {{{$}}
+
+template <std::enable_if_t<true, int> = 0>
+void no_dependent_type() {
+}
+// FIXME - Support non-dependent enable_ifs. Low priority though...
+
+struct ABaseClass {
+  ABaseClass();
+  ABaseClass(int);
+};
+
+template <typename T>
+struct AClass : ABaseClass {
+  template <std::enable_if_t<T::some_value, int> = 0>
+  void no_other_template_params() {
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:13: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+  // CHECK-FIXES: {{^}}  {{$}}
+  // CHECK-FIXES-NEXT: {{^}}  void no_other_template_params() requires T::some_value {{{$}}
+
+  template <typename U, std::enable_if_t<U::some_value, int> = 0>
+  AClass() {}
+  // CHECK-MESSAGES: :[[@LINE-2]]:25: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+  // CHECK-FIXES: {{^}}  template <typename U>{{$}}
+  // CHECK-FIXES-NEXT: {{^}}  AClass() requires U::some_value {}{{$}}
+
+  template <typename U, std::enable_if_t<U::some_value, int> = 0>
+  AClass(int) : data(0) {}
+  // CHECK-MESSAGES: :[[@LINE-2]]:25: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+  // CHECK-FIXES: {{^}}  template <typename U>{{$}}
+  // CHECK-FIXES-NEXT: {{^}}  AClass(int) requires U::some_value : data(0) {}{{$}}
+
+  template <typename U, std::enable_if_t<U::some_value, int> = 0>
+  AClass(int, int) : AClass(0) {}
+  // CHECK-MESSAGES: :[[@LINE-2]]:25: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+  // CHECK-FIXES: {{^}}  template <typename U>{{$}}
+  // CHECK-FIXES-NEXT: {{^}}  AClass(int, int) requires U::some_value : AClass(0) {}{{$}}
+
+  template <typename U, std::enable_if_t<U::some_value, int> = 0>
+  AClass(int, int, int) : ABaseClass(0) {}
+  // CHECK-MESSAGES: :[[@LINE-2]]:25: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+  // CHECK-FIXES: {{^}}  template <typename U>{{$}}
+  // CHECK-FIXES-NEXT: {{^}}  AClass(int, int, int) requires U::some_value : ABaseClass(0) {}{{$}}
+
+  int data;
+};
+
+template <typename T, std::enable_if_t<T::some_value, T>* = 0>
+void pointer_type() {
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:23: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T>{{$}}
+// CHECK-FIXES-NEXT: {{^}}void pointer_type() requires T::some_value {{{$}}
+
+template <typename T,
+          std::enable_if_t<T::some_value, T>* = nullptr>
+void param_on_newline() {
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:11: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T>{{$}}
+// CHECK-FIXES-NEXT: {{^}}void param_on_newline() requires T::some_value {{{$}}
+
+template <typename T,
+          typename U,
+          std::enable_if_t<
+            ConsumeVariadic<T,
+                            U>::value, T>* = nullptr>
+void param_split_on_two_lines() {
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:11: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T,{{$}}
+// CHECK-FIXES-NEXT: {{^}}          typename U>{{$}}
+// CHECK-FIXES-NEXT: {{^}}void param_split_on_two_lines() requires ConsumeVariadic<T,{{$}}
+// CHECK-FIXES-NEXT: {{^}}                            U>::value {{{$}}
+
+template <typename T, std::enable_if_t<T::some_value // comment
+         >* = nullptr>
+void trailing_slash_slash_comment() {
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:23: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T>{{$}}
+// CHECK-FIXES-NEXT: {{^}}void trailing_slash_slash_comment() requires T::some_value // comment{{$}}
+// CHECK-FIXES-NEXT: {{^}}          {{{$}}
+
+template <typename T, std::enable_if_t<T::some_value>* = nullptr, std::enable_if_t<T::another_value>* = nullptr>
+void two_enable_ifs() {
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:67: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T, std::enable_if_t<T::some_value>* = nullptr>{{$}}
+// CHECK-FIXES-NEXT: {{^}}void two_enable_ifs() requires T::another_value {{{$}}
+
+////////////////////////////////
+// Negative tests
+////////////////////////////////
+
+template <typename U, std::enable_if_t<U::some_value, int> V = 0>
+void non_type_param_has_name() {
+}
+template <typename U, std::enable_if_t<U::some_value, int>>
+void non_type_param_has_no_default() {
+}
+template <typename U, std::enable_if_t<U::some_value, int> V>
+void non_type_param_has_name_and_no_default() {
+}
+template <typename U, std::enable_if_t<U::some_value, int>...>
+void non_type_variadic() {
+}
+template <typename U, std::enable_if_t<U::some_value, int> = 0, int = 0>
+void non_type_not_last() {
+}
+
+#define TEMPLATE_REQUIRES(U, IF) template <typename U, std::enable_if_t<IF, int> = 0>
+TEMPLATE_REQUIRES(U, U::some_value)
+void macro_entire_enable_if() {
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-MESSAGES: :[[@LINE-5]]:56: note: expanded from macro 'TEMPLATE_REQUIRES'
+// CHECK-FIXES: {{^}}TEMPLATE_REQUIRES(U, U::some_value)
+// CHECK-FIXES-NEXT: {{^}}void macro_entire_enable_if() {{{$}}
+
+#define CONDITION U::some_value
+template <typename U, std::enable_if_t<CONDITION, int> = 0>
+void macro_condition() {
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:23: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename U>{{$}}
+// CHECK-FIXES-NEXT: {{^}}void macro_condition() requires CONDITION {{{$}}
+
+#undef CONDITION
+#define CONDITION !U::some_value
+template <typename U, std::enable_if_t<CONDITION, int> = 0>
+void macro_condition_not_primary() {
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:23: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename U>{{$}}
+// CHECK-FIXES-NEXT: {{^}}void macro_condition_not_primary() requires (CONDITION) {{{$}}
+
+} // namespace enable_if_trailing_non_type_parameter
+
+
+namespace enable_if_trailing_type_parameter {
+
+////////////////////////////////
+// Section 3: enable_if as final template nameless defaulted type parameter
+////////////////////////////////
+
+template <typename T, typename = std::enable_if<T::some_value>::type>
+void basic() {
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:23: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T>{{$}}
+// CHECK-FIXES-NEXT: {{^}}void basic() requires T::some_value {{{$}}
+
+template <typename T, typename = std::enable_if_t<T::some_value>>
+void basic_t() {
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:23: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T>{{$}}
+// CHECK-FIXES-NEXT: {{^}}void basic_t() requires T::some_value {{{$}}
+
+template <typename T, template <typename> class U, class V, typename = std::enable_if_t<T::some_value>>
+void basic_many_template_params() {
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:61: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T, template <typename> class U, class V>{{$}}
+// CHECK-FIXES-NEXT: {{^}}void basic_many_template_params() requires T::some_value {{{$}}
+
+struct ABaseClass {
+  ABaseClass();
+  ABaseClass(int);
+};
+
+template <typename T>
+struct AClass : ABaseClass {
+  template <typename = std::enable_if_t<T::some_value>>
+  void no_other_template_params() {
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:13: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+  // CHECK-FIXES: {{^}}  {{$}}
+  // CHECK-FIXES-NEXT: {{^}}  void no_other_template_params() requires T::some_value {{{$}}
+
+  template <typename U, typename = std::enable_if_t<U::some_value>>
+  AClass() {}
+  // CHECK-MESSAGES: :[[@LINE-2]]:25: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+  // CHECK-FIXES: {{^}}  template <typename U>{{$}}
+  // CHECK-FIXES-NEXT: {{^}}  AClass() requires U::some_value {}{{$}}
+
+  template <typename U, typename = std::enable_if_t<U::some_value>>
+  AClass(int) : data(0) {}
+  // CHECK-MESSAGES: :[[@LINE-2]]:25: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+  // CHECK-FIXES: {{^}}  template <typename U>{{$}}
+  // CHECK-FIXES-NEXT: {{^}}  AClass(int) requires U::some_value : data(0) {}{{$}}
+
+  template <typename U, typename = std::enable_if_t<U::some_value>>
+  AClass(int, int) : AClass(0) {}
+  // CHECK-MESSAGES: :[[@LINE-2]]:25: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+  // CHECK-FIXES: {{^}}  template <typename U>{{$}}
+  // CHECK-FIXES-NEXT: {{^}}  AClass(int, int) requires U::some_value : AClass(0) {}{{$}}
+
+  template <typename U, typename = std::enable_if_t<U::some_value>>
+  AClass(int, int, int) : ABaseClass(0) {}
+  // CHECK-MESSAGES: :[[@LINE-2]]:25: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+  // CHECK-FIXES: {{^}}  template <typename U>{{$}}
+  // CHECK-FIXES-NEXT: {{^}}  AClass(int, int, int) requires U::some_value : ABaseClass(0) {}{{$}}
+
+  int data;
+};
+
+template <typename T, typename = std::enable_if_t<T::some_value>*>
+void pointer_type() {
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:23: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T>{{$}}
+// CHECK-FIXES-NEXT: {{^}}void pointer_type() requires T::some_value {{{$}}
+
+template <typename T, typename = std::enable_if_t<T::some_value>&>
+void reference_type() {
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:23: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T>{{$}}
+// CHECK-FIXES-NEXT: {{^}}void reference_type() requires T::some_value {{{$}}
+
+template <typename T,
+          typename = std::enable_if_t<T::some_value>*>
+void param_on_newline() {
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:11: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T>{{$}}
+// CHECK-FIXES-NEXT: {{^}}void param_on_newline() requires T::some_value {{{$}}
+
+template <typename T,
+          typename U,
+          typename = std::enable_if_t<
+            ConsumeVariadic<T,
+                            U>::value>>
+void param_split_on_two_lines() {
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:11: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}template <typename T,{{$}}
+// CHECK-FIXES-NEXT: {{^}}          typename U>{{$}}
+// CHECK-FIXES-NEXT: {{^}}void param_split_on_two_lines() requires ConsumeVariadic<T,{{$}}
+// CHECK-FIXES-NEXT: {{^}}                            U>::value {{{$}}
+
+
+////////////////////////////////
+// Negative tests
+////////////////////////////////
+
+template <typename U, typename Named = std::enable_if_t<U::some_value>>
+void param_has_name() {
+}
+
+template <typename U, typename = std::enable_if_t<U::some_value>, typename = int>
+void not_last_param() {
+}
+
+} // namespace enable_if_trailing_type_parameter
Index: clang-tools-extra/test/clang-tidy/checkers/modernize/use-constraints-first-greatergreater.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/modernize/use-constraints-first-greatergreater.cpp
@@ -0,0 +1,22 @@
+// RUN: %check_clang_tidy -std=c++20 %s modernize-use-constraints %t -- -- -fno-delayed-template-parsing
+
+// NOLINTBEGIN
+namespace std {
+template <bool B, class T = void> struct enable_if { };
+
+template <class T> struct enable_if<true, T> { typedef T type; };
+
+template <bool B, class T = void>
+using enable_if_t = typename enable_if<B, T>::type;
+
+} // namespace std
+// NOLINTEND
+
+// Separate test file for the case where the first '>>' token part of
+// an enable_if expression correctly handles the synthesized token.
+
+template <typename T, typename = std::enable_if_t<T::some_value>>
+void first_greatergreater_is_enable_if() {
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:23: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}void first_greatergreater_is_enable_if() requires T::some_value {{{$}}
Index: clang-tools-extra/docs/clang-tidy/checks/modernize/use-constraints.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/modernize/use-constraints.rst
@@ -0,0 +1,24 @@
+.. title:: clang-tidy - modernize-use-constraints
+
+modernize-use-constraints
+======================
+
+Replace ``enable_if`` with C++20 requires clauses.
+
+``enable_if`` is a SFINAE mechanism for selecting the desired function or class
+template based on type traits or other requirements. ``enable_if`` changes the
+meta-arity of the template, and has other `adverse side effects <https://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0225r0.html>`_
+in the code. C++20 introduces concepts and constraints as a cleaner language
+provided solution to achieve the same outcome.
+
+This check replaces common ``enable_if`` patterns that can be replaced
+by C++20 requires clauses. Uses that cannot be replaced automatically
+will emit a diagnostic without a replacement
+
+.. code-block:: c++
+
+  template <typename T>
+  std::enable_if_t<T::some_trait, int> only_if_t_has_the_trait() { ... }
+
+  template <typename T, std::enable_if_t<T::some_trait, int> = 0>
+  void another_version() { ... }
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -282,6 +282,7 @@
    `modernize-unary-static-assert <modernize/unary-static-assert.html>`_, "Yes"
    `modernize-use-auto <modernize/use-auto.html>`_, "Yes"
    `modernize-use-bool-literals <modernize/use-bool-literals.html>`_, "Yes"
+   `modernize-use-constraints <modernize/use-constraints.html>`_, "Yes"
    `modernize-use-default-member-init <modernize/use-default-member-init.html>`_, "Yes"
    `modernize-use-emplace <modernize/use-emplace.html>`_, "Yes"
    `modernize-use-equals-default <modernize/use-equals-default.html>`_, "Yes"
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -141,6 +141,11 @@
   Warns when `empty()` is used on a range and the result is ignored. Suggests `clear()`
   if it is an existing member function.
 
+- New :doc:`modernize-use-constraints
+  <clang-tidy/checks/modernize/use-constraints>` check.
+
+  Replace ``enable_if`` with C++20 requires clauses.
+
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
Index: clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.h
@@ -0,0 +1,33 @@
+//===--- UseConstraintsCheck.h - clang-tidy ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USECONSTRAINTSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USECONSTRAINTSCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::modernize {
+
+/// Replace enable_if with C++20 requires clauses.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-constraints.html
+class UseConstraintsCheck : public ClangTidyCheck {
+public:
+  UseConstraintsCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus20;
+  }
+};
+
+} // namespace clang::tidy::modernize
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USECONSTRAINTSCHECK_H
Index: clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp
@@ -0,0 +1,494 @@
+//===--- UseConstraintsCheck.cpp - clang-tidy -----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseConstraintsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+#include "../utils/LexerUtils.h"
+
+#include <optional>
+#include <tuple>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::modernize {
+
+struct EnableIfData {
+  TemplateSpecializationTypeLoc Loc;
+  TypeLoc Outer;
+};
+
+namespace {
+AST_MATCHER(FunctionDecl, hasOtherDeclarations) {
+  auto It = Node.redecls_begin();
+  auto EndIt = Node.redecls_end();
+
+  if (It == EndIt)
+    return false;
+
+  ++It;
+  return It != EndIt;
+}
+} // namespace
+
+void UseConstraintsCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      functionTemplateDecl(
+          has(functionDecl(unless(hasOtherDeclarations()), isDefinition(),
+                           hasReturnTypeLoc(typeLoc().bind("return")))
+                  .bind("function")))
+          .bind("functionTemplate"),
+      this);
+}
+
+static std::optional<TemplateSpecializationTypeLoc>
+matchEnableIfSpecialization_impl_type(TypeLoc TheType) {
+  if (const auto Dep = TheType.getAs<DependentNameTypeLoc>()) {
+    if (Dep.getTypePtr()->getIdentifier()->getName() != "type" ||
+        Dep.getTypePtr()->getKeyword() != ETK_Typename) {
+      return std::nullopt;
+    }
+    return matchEnableIfSpecialization_impl_type(
+        Dep.getQualifierLoc().getTypeLoc());
+  } else if (const auto Elaborated = TheType.getAs<ElaboratedTypeLoc>()) {
+    return std::nullopt;
+  } else if (const auto Specialization =
+                 TheType.getAs<TemplateSpecializationTypeLoc>()) {
+    std::string Name = TheType.getType().getAsString();
+    if (Name.find("enable_if<") == std::string::npos) {
+      return std::nullopt;
+    }
+    int NumArgs = Specialization.getNumArgs();
+    if (NumArgs != 1 && NumArgs != 2) {
+      return std::nullopt;
+    }
+    return std::make_optional(Specialization);
+  }
+  return std::nullopt;
+}
+
+static std::optional<TemplateSpecializationTypeLoc>
+matchEnableIfSpecialization_impl_t(TypeLoc TheType) {
+  if (const auto Dep = TheType.getAs<DependentNameTypeLoc>()) {
+    return std::nullopt;
+  } else if (const auto Elaborated = TheType.getAs<ElaboratedTypeLoc>()) {
+    return matchEnableIfSpecialization_impl_t(Elaborated.getNamedTypeLoc());
+  } else if (const auto Specialization =
+                 TheType.getAs<TemplateSpecializationTypeLoc>()) {
+    std::string Name = TheType.getType().getAsString();
+    if (Name.find("enable_if_t<") == std::string::npos) {
+      return std::nullopt;
+    }
+    if (!Specialization.getTypePtr()->isTypeAlias()) {
+      return std::nullopt;
+    }
+    if (const auto *AliasedType = llvm::dyn_cast<DependentNameType>(
+            Specialization.getTypePtr()->getAliasedType())) {
+      if (AliasedType->getIdentifier()->getName() != "type" ||
+          AliasedType->getKeyword() != ETK_Typename) {
+        return std::nullopt;
+      }
+    } else {
+      return std::nullopt;
+    }
+    int NumArgs = Specialization.getNumArgs();
+    if (NumArgs != 1 && NumArgs != 2) {
+      return std::nullopt;
+    }
+    return std::make_optional(Specialization);
+  }
+  return std::nullopt;
+}
+
+static std::optional<TemplateSpecializationTypeLoc>
+matchEnableIfSpecialization_impl(TypeLoc TheType) {
+  std::optional<TemplateSpecializationTypeLoc> EnableIf;
+  EnableIf = matchEnableIfSpecialization_impl_type(TheType);
+  if (EnableIf) {
+    return EnableIf;
+  }
+  return matchEnableIfSpecialization_impl_t(TheType);
+}
+
+static std::optional<EnableIfData>
+matchEnableIfSpecialization(TypeLoc TheType) {
+  if (const auto Qualified = TheType.getAs<QualifiedTypeLoc>()) {
+  }
+  if (const auto Pointer = TheType.getAs<PointerTypeLoc>()) {
+    TheType = Pointer.getPointeeLoc();
+  } else if (const auto Reference = TheType.getAs<ReferenceTypeLoc>()) {
+    TheType = Reference.getPointeeLoc();
+  }
+  if (const auto Qualified = TheType.getAs<QualifiedTypeLoc>()) {
+    TheType = Qualified.getUnqualifiedLoc();
+  }
+
+  std::optional<TemplateSpecializationTypeLoc> EnableIf =
+      matchEnableIfSpecialization_impl(TheType);
+  if (EnableIf) {
+    return std::make_optional(EnableIfData{std::move(*EnableIf), TheType});
+  } else {
+    return std::nullopt;
+  }
+}
+
+static std::tuple<std::optional<EnableIfData>, const Decl *>
+matchTrailingTemplateParam(const FunctionTemplateDecl *FunctionTemplate) {
+  // For non-type trailing param, match very specifically
+  // 'template <..., enable_if_type<Condition, Type> = Default>' where
+  // enable_if_type is 'enable_if' or 'enable_if_t'. E.g., 'template <typename
+  // T, enable_if_t<is_same_v<T, bool>, int*> = nullptr>
+  //
+  // Otherwise, match a trailing default type arg.
+  // E.g., 'template <typename T, typename = enable_if_t<is_same_v<T, bool>>>'
+
+  const TemplateParameterList *TemplateParams =
+      FunctionTemplate->getTemplateParameters();
+  if (TemplateParams->size() == 0) {
+    return {};
+  }
+  const NamedDecl *LastParam =
+      TemplateParams->getParam(TemplateParams->size() - 1);
+  if (const auto *LastTemplateParam =
+          llvm::dyn_cast<NonTypeTemplateParmDecl>(LastParam)) {
+
+    if (!LastTemplateParam->hasDefaultArgument() ||
+        !LastTemplateParam->getName().empty()) {
+      return {};
+    }
+
+    return std::make_tuple(
+        matchEnableIfSpecialization(
+            LastTemplateParam->getTypeSourceInfo()->getTypeLoc()),
+        LastTemplateParam);
+  } else if (const auto *LastTemplateParam =
+                 llvm::dyn_cast<TemplateTypeParmDecl>(LastParam)) {
+    if (LastTemplateParam->hasDefaultArgument() &&
+        LastTemplateParam->getIdentifier() == nullptr) {
+      return std::make_tuple(
+          matchEnableIfSpecialization(
+              LastTemplateParam->getDefaultArgumentInfo()->getTypeLoc()),
+          LastTemplateParam);
+    }
+  }
+  return {};
+}
+
+template <typename T>
+static SourceLocation getRAngleFileLoc(const SourceManager &SM,
+                                       const T &Element) {
+  // getFileLoc handles the case where the RAngle loc is part of a synthesized
+  // '>>', which ends up allocating a 'scratch space' buffer in the source
+  // manager.
+  return SM.getFileLoc(Element.getRAngleLoc());
+}
+
+static SourceRange
+getConditionRange(ASTContext &Context,
+                  const TemplateSpecializationTypeLoc &EnableIf) {
+  // TemplateArgumentLoc's SourceRange End is the location of the last token
+  // (per UnqualifiedId docs). E.g., in `enable_if<AAA && BBB>`, the End
+  // location will be the first 'B' in 'BBB'.
+  const LangOptions &LangOpts = Context.getLangOpts();
+  const SourceManager &SM = Context.getSourceManager();
+  if (EnableIf.getNumArgs() > 1) {
+    TemplateArgumentLoc NextArg = EnableIf.getArgLoc(1);
+    return SourceRange(
+        EnableIf.getLAngleLoc().getLocWithOffset(1),
+        utils::lexer::findPreviousTokenKind(NextArg.getSourceRange().getBegin(),
+                                            SM, LangOpts, tok::comma));
+  } else {
+    return SourceRange(EnableIf.getLAngleLoc().getLocWithOffset(1),
+                       getRAngleFileLoc(SM, EnableIf));
+  }
+}
+
+static SourceRange getTypeRange(ASTContext &Context,
+                                const TemplateSpecializationTypeLoc &EnableIf) {
+  TemplateArgumentLoc Arg = EnableIf.getArgLoc(1);
+  const LangOptions &LangOpts = Context.getLangOpts();
+  const SourceManager &SM = Context.getSourceManager();
+  return SourceRange(
+      utils::lexer::findPreviousTokenKind(Arg.getSourceRange().getBegin(), SM,
+                                          LangOpts, tok::comma)
+          .getLocWithOffset(1),
+      getRAngleFileLoc(SM, EnableIf));
+}
+
+static std::optional<std::string>
+getTypeText(ASTContext &Context,
+            const TemplateSpecializationTypeLoc &EnableIf) {
+  if (EnableIf.getNumArgs() > 1) {
+    const LangOptions &LangOpts = Context.getLangOpts();
+    const SourceManager &SM = Context.getSourceManager();
+    bool Invalid = false;
+    std::string Text =
+        Lexer::getSourceText(
+            CharSourceRange::getCharRange(getTypeRange(Context, EnableIf)), SM,
+            LangOpts, &Invalid)
+            .trim()
+            .str();
+    if (Invalid) {
+      return std::nullopt;
+    }
+    return std::make_optional(std::move(Text));
+  } else {
+    return std::make_optional("void");
+  }
+}
+
+static std::optional<SourceLocation>
+findInsertionForConstraint(const FunctionDecl *Function, ASTContext &Context) {
+  SourceManager &SM = Context.getSourceManager();
+  const LangOptions &LangOpts = Context.getLangOpts();
+
+  if (const auto *Constructor = llvm::dyn_cast<CXXConstructorDecl>(Function)) {
+    if (Constructor->init_begin() != Constructor->init_end()) {
+      const CXXCtorInitializer *FirstInit = *Constructor->init_begin();
+      return utils::lexer::findPreviousTokenKind(FirstInit->getSourceLocation(),
+                                                 SM, LangOpts, tok::colon);
+    }
+  }
+  if (Function->isDeleted()) {
+    SourceRange ParamsRange = Function->getParametersSourceRange();
+    if (!ParamsRange.isValid()) {
+      return std::nullopt;
+    }
+    SourceLocation EndParens = utils::lexer::findNextAnyTokenKind(
+        ParamsRange.getEnd(), SM, LangOpts, tok::r_paren, tok::r_paren);
+    return utils::lexer::findNextAnyTokenKind(EndParens, SM, LangOpts,
+                                              tok::equal, tok::equal);
+  }
+  const Stmt *Body = Function->getBody();
+  if (!Body) {
+    return std::nullopt;
+  }
+  return Body->getBeginLoc();
+}
+
+static Token getPreviousToken(SourceLocation &Location, const SourceManager &SM,
+                              const LangOptions &LangOpts,
+                              bool SkipComments = false) {
+  Token Token;
+  Token.setKind(tok::unknown);
+
+  Location = Location.getLocWithOffset(-1);
+  if (Location.isInvalid())
+    return Token;
+
+  auto StartOfFile = SM.getLocForStartOfFile(SM.getFileID(Location));
+  while (Location != StartOfFile) {
+    Location = Lexer::GetBeginningOfToken(Location, SM, LangOpts);
+    if (!Lexer::getRawToken(Location, Token, SM, LangOpts) &&
+        (!SkipComments || !Token.is(tok::comment))) {
+      break;
+    }
+    Location = Location.getLocWithOffset(-1);
+  }
+  return Token;
+}
+
+bool isPrimaryExpression(const Expr *Expression) {
+  // This function is an incomplete approximation of checking whether
+  // an Expr is a primary expression. In particular, if this function
+  // returns true, the expression is a primary expression. The converse
+  // is not necessarily true.
+
+  if (const auto *Cast = llvm::dyn_cast<ImplicitCastExpr>(Expression)) {
+    Expression = Cast->getSubExprAsWritten();
+  }
+  if (llvm::dyn_cast<ParenExpr>(Expression)) {
+    return true;
+  }
+  if (llvm::dyn_cast<DependentScopeDeclRefExpr>(Expression)) {
+    return true;
+  }
+
+  return false;
+}
+
+static std::optional<std::string> getConditionText(const Expr *ConditionExpr,
+                                                   SourceRange ConditionRange,
+                                                   ASTContext &Context) {
+  SourceManager &SM = Context.getSourceManager();
+  const LangOptions &LangOpts = Context.getLangOpts();
+
+  SourceLocation PrevTokenLoc = ConditionRange.getEnd();
+  if (PrevTokenLoc.isInvalid()) {
+    return std::nullopt;
+  }
+  Token PrevToken = getPreviousToken(PrevTokenLoc, SM, LangOpts);
+  bool EndsWithDoubleSlash =
+      PrevToken.is(tok::comment) &&
+      Lexer::getSourceText(CharSourceRange::getCharRange(
+                               PrevTokenLoc, PrevTokenLoc.getLocWithOffset(2)),
+                           SM, LangOpts) == "//";
+
+  bool Invalid = false;
+  llvm::StringRef ConditionText = Lexer::getSourceText(
+      CharSourceRange::getCharRange(ConditionRange), SM, LangOpts, &Invalid);
+  if (Invalid) {
+    return std::nullopt;
+  }
+
+  auto AddParens = [&](llvm::StringRef Text) -> std::string {
+    if (isPrimaryExpression(ConditionExpr)) {
+      return Text.str();
+    } else {
+      return "(" + Text.str() + ")";
+    }
+  };
+
+  if (EndsWithDoubleSlash) {
+    return std::make_optional(AddParens(ConditionText.str()));
+  } else {
+    return std::make_optional(AddParens(ConditionText.trim().str()));
+  }
+}
+
+static std::vector<FixItHint> handleReturnType(const FunctionDecl *Function,
+                                               const TypeLoc &ReturnType,
+                                               const EnableIfData &EnableIf,
+                                               ASTContext &Context) {
+  TemplateArgumentLoc EnableCondition = EnableIf.Loc.getArgLoc(0);
+
+  SourceRange ConditionRange = getConditionRange(Context, EnableIf.Loc);
+
+  std::optional<std::string> ConditionText = getConditionText(
+      EnableCondition.getSourceExpression(), ConditionRange, Context);
+  std::optional<std::string> TypeText = getTypeText(Context, EnableIf.Loc);
+  if (!TypeText) {
+    return {};
+  }
+
+  SmallVector<const Expr *, 3> ExistingConstraints;
+  Function->getAssociatedConstraints(ExistingConstraints);
+  if (ExistingConstraints.size() > 0) {
+    // FIXME - Support adding new constraints to existing ones. Do we need to
+    // consider subsumption?
+    return {};
+  }
+
+  std::optional<SourceLocation> ConstraintInsertionLoc =
+      findInsertionForConstraint(Function, Context);
+  if (!ConstraintInsertionLoc) {
+    return {};
+  }
+  std::vector<FixItHint> FixIts;
+  FixIts.push_back(FixItHint::CreateReplacement(
+      CharSourceRange::getTokenRange(EnableIf.Outer.getSourceRange()),
+      *TypeText));
+  FixIts.push_back(FixItHint::CreateInsertion(
+      *ConstraintInsertionLoc, "requires " + *ConditionText + " "));
+  return FixIts;
+}
+
+static std::vector<FixItHint>
+handleTrailingTemplateType(const FunctionTemplateDecl *FunctionTemplate,
+                           const FunctionDecl *Function,
+                           const Decl *LastTemplateParam,
+                           const EnableIfData &EnableIf, ASTContext &Context) {
+  SourceManager &SM = Context.getSourceManager();
+  const LangOptions &LangOpts = Context.getLangOpts();
+
+  TemplateArgumentLoc EnableCondition = EnableIf.Loc.getArgLoc(0);
+
+  SourceRange ConditionRange = getConditionRange(Context, EnableIf.Loc);
+
+  std::optional<std::string> ConditionText = getConditionText(
+      EnableCondition.getSourceExpression(), ConditionRange, Context);
+  if (!ConditionText) {
+    return {};
+  }
+
+  SmallVector<const Expr *, 3> ExistingConstraints;
+  Function->getAssociatedConstraints(ExistingConstraints);
+  if (ExistingConstraints.size() > 0) {
+    // FIXME - Support adding new constraints to existing ones. Do we need to
+    // consider subsumption?
+    return {};
+  }
+
+  SourceRange RemovalRange;
+  const TemplateParameterList *TemplateParams =
+      FunctionTemplate->getTemplateParameters();
+  if (!TemplateParams || TemplateParams->size() == 0) {
+    return {};
+  }
+
+  if (TemplateParams->size() == 1) {
+    RemovalRange =
+        SourceRange(TemplateParams->getTemplateLoc(),
+                    getRAngleFileLoc(SM, *TemplateParams).getLocWithOffset(1));
+  } else {
+    RemovalRange =
+        SourceRange(utils::lexer::findPreviousTokenKind(
+                        LastTemplateParam->getSourceRange().getBegin(), SM,
+                        LangOpts, tok::comma),
+                    getRAngleFileLoc(SM, *TemplateParams));
+  }
+
+  std::optional<SourceLocation> ConstraintInsertionLoc =
+      findInsertionForConstraint(Function, Context);
+  if (!ConstraintInsertionLoc) {
+    return {};
+  }
+  std::vector<FixItHint> FixIts;
+  FixIts.push_back(
+      FixItHint::CreateRemoval(CharSourceRange::getCharRange(RemovalRange)));
+  FixIts.push_back(FixItHint::CreateInsertion(
+      *ConstraintInsertionLoc, "requires " + *ConditionText + " "));
+  return FixIts;
+}
+
+void UseConstraintsCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *FunctionTemplate =
+      Result.Nodes.getNodeAs<FunctionTemplateDecl>("functionTemplate");
+  const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("function");
+  const auto *ReturnType = Result.Nodes.getNodeAs<TypeLoc>("return");
+  if (!FunctionTemplate || !Function || !ReturnType) {
+    return;
+  }
+
+  // Check for
+  //   template <...>
+  //   enable_if<Condition, ReturnType> function();
+  //
+  //   template <..., enable_if<Condition, Type> = Type{}>
+  //   function();
+  //
+  //   template <..., typename = enable_if<Condition, void>>
+  //   function();
+
+  std::optional<EnableIfData> EnableIf;
+  EnableIf = matchEnableIfSpecialization(*ReturnType);
+  if (EnableIf.has_value()) {
+    std::vector<FixItHint> FixIts =
+        handleReturnType(Function, *ReturnType, *EnableIf, *Result.Context);
+    diag(ReturnType->getBeginLoc(),
+         "use C++20 requires constraints instead of enable_if")
+        << FixIts;
+    return;
+  }
+  const Decl *LastTemplateParam = nullptr;
+  std::tie(EnableIf, LastTemplateParam) =
+      matchTrailingTemplateParam(FunctionTemplate);
+  if (EnableIf.has_value() && LastTemplateParam) {
+    std::vector<FixItHint> FixIts = handleTrailingTemplateType(
+        FunctionTemplate, Function, LastTemplateParam, *EnableIf,
+        *Result.Context);
+    diag(LastTemplateParam->getSourceRange().getBegin(),
+         "use C++20 requires constraints instead of enable_if")
+        << FixIts;
+    return;
+  }
+}
+
+} // namespace clang::tidy::modernize
Index: clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -29,6 +29,7 @@
 #include "UnaryStaticAssertCheck.h"
 #include "UseAutoCheck.h"
 #include "UseBoolLiteralsCheck.h"
+#include "UseConstraintsCheck.h"
 #include "UseDefaultMemberInitCheck.h"
 #include "UseEmplaceCheck.h"
 #include "UseEqualsDefaultCheck.h"
@@ -81,6 +82,8 @@
     CheckFactories.registerCheck<UseAutoCheck>("modernize-use-auto");
     CheckFactories.registerCheck<UseBoolLiteralsCheck>(
         "modernize-use-bool-literals");
+    CheckFactories.registerCheck<UseConstraintsCheck>(
+        "modernize-use-constraints");
     CheckFactories.registerCheck<UseDefaultMemberInitCheck>(
         "modernize-use-default-member-init");
     CheckFactories.registerCheck<UseEmplaceCheck>("modernize-use-emplace");
Index: clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -28,6 +28,7 @@
   UnaryStaticAssertCheck.cpp
   UseAutoCheck.cpp
   UseBoolLiteralsCheck.cpp
+  UseConstraintsCheck.cpp
   UseDefaultMemberInitCheck.cpp
   UseEmplaceCheck.cpp
   UseEqualsDefaultCheck.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to