Author: Victor Chernyakin
Date: 2025-08-23T02:05:08-07:00
New Revision: 5b5a8cd63a9c7ea769b8651ceec05de0835dacfb

URL: 
https://github.com/llvm/llvm-project/commit/5b5a8cd63a9c7ea769b8651ceec05de0835dacfb
DIFF: 
https://github.com/llvm/llvm-project/commit/5b5a8cd63a9c7ea769b8651ceec05de0835dacfb.diff

LOG: [clang-tidy] Fix `modernize-use-constraints` crash on uses of nonstandard 
`enable_if`s (#152938)

Fixes #152868. See that issue for details.
Fixes #123726.

Added: 
    

Modified: 
    clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp
    clang-tools-extra/docs/ReleaseNotes.rst
    clang-tools-extra/test/clang-tidy/checkers/modernize/use-constraints.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp 
b/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp
index 07274d0376207..c4a64be537a44 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp
@@ -8,6 +8,7 @@
 
 #include "UseConstraintsCheck.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclTemplate.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Lex/Lexer.h"
 
@@ -80,6 +81,13 @@ matchEnableIfSpecializationImplTypename(TypeLoc TheType) {
     if (!TD || TD->getName() != "enable_if")
       return std::nullopt;
 
+    assert(!TD->getTemplateParameters()->empty() &&
+           "found template with no template parameters?");
+    const auto *FirstParam = dyn_cast<NonTypeTemplateParmDecl>(
+        TD->getTemplateParameters()->getParam(0));
+    if (!FirstParam || !FirstParam->getType()->isBooleanType())
+      return std::nullopt;
+
     int NumArgs = SpecializationLoc.getNumArgs();
     if (NumArgs != 1 && NumArgs != 2)
       return std::nullopt;
@@ -107,6 +115,13 @@ matchEnableIfSpecializationImplTrait(TypeLoc TheType) {
     if (!Specialization->isTypeAlias())
       return std::nullopt;
 
+    assert(!TD->getTemplateParameters()->empty() &&
+           "found template with no template parameters?");
+    const auto *FirstParam = dyn_cast<NonTypeTemplateParmDecl>(
+        TD->getTemplateParameters()->getParam(0));
+    if (!FirstParam || !FirstParam->getType()->isBooleanType())
+      return std::nullopt;
+
     if (const auto *AliasedType =
             dyn_cast<DependentNameType>(Specialization->getAliasedType())) {
       ElaboratedTypeKeyword Keyword = AliasedType->getKeyword();

diff  --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index 780e5b3fc21cf..93fc368f14344 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -188,6 +188,11 @@ Changes in existing checks
 - Improved :doc:`misc-header-include-cycle
   <clang-tidy/checks/misc/header-include-cycle>` check performance.
 
+- Improved :doc:`modernize-use-constraints
+  <clang-tidy/checks/modernize/use-constraints>` check by fixing a crash on
+  uses of non-standard ``enable_if`` with a signature 
diff erent from
+  ``std::enable_if`` (such as ``boost::enable_if``).
+
 - Improved :doc:`modernize-use-designated-initializers
   <clang-tidy/checks/modernize/use-designated-initializers>` check to
   suggest using designated initializers for aliased aggregate types.

diff  --git 
a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constraints.cpp 
b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constraints.cpp
index 3bcd5cd74024e..90131c3d86920 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constraints.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constraints.cpp
@@ -1,4 +1,4 @@
-// RUN: %check_clang_tidy -std=c++20 %s modernize-use-constraints %t -- -- 
-fno-delayed-template-parsing
+// RUN: %check_clang_tidy -std=c++20-or-later %s modernize-use-constraints %t 
-- -- -fno-delayed-template-parsing
 
 // NOLINTBEGIN
 namespace std {
@@ -756,3 +756,69 @@ abs(const number<T, ExpressionTemplates> &v) {
 }
 
 }
+
+template <typename T>
+struct some_type_trait {
+  static constexpr bool value = true;
+};
+
+// Fix-its are offered even for a non-standard enable_if.
+namespace nonstd {
+
+template <bool Condition, typename T = void>
+struct enable_if : std::enable_if<Condition, T> {};
+
+template <bool Condition, typename T = void>
+using enable_if_t = typename enable_if<Condition, T>::type;
+
+}
+
+template <typename T>
+typename nonstd::enable_if<some_type_trait<T>::value, void>::type 
nonstd_enable_if() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use C++20 requires constraints 
instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}void nonstd_enable_if() requires 
some_type_trait<T>::value {}{{$}}
+
+template <typename T>
+nonstd::enable_if_t<some_type_trait<T>::value, void> nonstd_enable_if_t() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use C++20 requires constraints 
instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}void nonstd_enable_if_t() requires 
some_type_trait<T>::value {}{{$}}
+
+template <>
+nonstd::enable_if_t<some_type_trait<int>::value, void> 
nonstd_enable_if_t<int>() {}
+// FIXME - Support non-dependent enable_ifs.
+
+template <typename T>
+typename nonstd::enable_if<some_type_trait<T>::value>::type 
nonstd_enable_if_one_param() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use C++20 requires constraints 
instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}void nonstd_enable_if_one_param() requires 
some_type_trait<T>::value {}{{$}}
+
+template <typename T>
+nonstd::enable_if_t<some_type_trait<T>::value> nonstd_enable_if_t_one_param() 
{}
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use C++20 requires constraints 
instead of enable_if [modernize-use-constraints]
+// CHECK-FIXES: {{^}}void nonstd_enable_if_t_one_param() requires 
some_type_trait<T>::value {}{{$}}
+
+// No fix-its are offered for an enable_if with a 
diff erent signature from the standard one.
+namespace boost {
+
+template <typename Condition, typename T = void>
+struct enable_if : std::enable_if<Condition::value, T> {};
+
+template <typename Condition, typename T = void>
+using enable_if_t = typename enable_if<Condition, T>::type;
+
+}
+
+template <typename T>
+typename boost::enable_if<some_type_trait<T>, void>::type boost_enable_if() {}
+
+template <typename T>
+boost::enable_if_t<some_type_trait<T>, void> boost_enable_if_t() {}
+
+template <>
+boost::enable_if_t<some_type_trait<int>, void> boost_enable_if_t<int>() {}
+
+template <typename T>
+typename boost::enable_if<some_type_trait<T>>::type 
boost_enable_if_one_param() {}
+
+template <typename T>
+boost::enable_if_t<some_type_trait<T>> boost_enable_if_t_one_param() {}


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to