https://github.com/NagyDonat created 
https://github.com/llvm/llvm-project/pull/155237

This commit ensures that the modernize-use-constraints check ignores templates 
that happen to be named `enable_if` or `enable_if_t` if they are not declared 
in the namespace `std`.

This patch motivated by a crash observed during the analysis of the open source 
library https://github.com/Neargye/magic_enum/ which declares a template 
`detail::enable_if_t` with semantics that significantly differ from the 
standard one. (I was unable to reproduce that crash with the standard 
`enable_if_t`.)

However, there are other projects that use non-standard `enable_if`: even 
`boost` declares a `boost::enable_if` which excepts different parameters than 
`std::enable_if`.

From 414468ef4dc2a5e3e372079418634b55a33dc7dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.n...@ericsson.com>
Date: Mon, 25 Aug 2025 13:48:51 +0200
Subject: [PATCH] [clang-tidy] Limit modernize-use-constraints to standard
 enable_if

This commit ensures that the modernize-use-constraints check ignores
templates that happen to be named `enable_if` or `enable_if_t` if they
are not declared in the namespace `std`.

This patch motivated by a crash observed during the analysis of the open
source library https://github.com/Neargye/magic_enum/ which declares a
template `detail::enable_if_t` with semantics that significantly differ
from the standard one. (I was unable to reproduce that crash with the
standard `enable_if_t`.)

However, there are other projects that use non-standard `enable_if`:
even `boost` declares a `boost::enable_if` which excepts different
parameters than `std::enable_if`.
---
 .../modernize/UseConstraintsCheck.cpp         |  4 +--
 .../checkers/modernize/use-constraints.cpp    | 30 +++++++++++++++++++
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp 
b/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp
index 07274d0376207..1818e30971c21 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp
@@ -77,7 +77,7 @@ matchEnableIfSpecializationImplTypename(TypeLoc TheType) {
 
     const TemplateDecl *TD =
         Specialization->getTemplateName().getAsTemplateDecl();
-    if (!TD || TD->getName() != "enable_if")
+    if (!TD || TD->getName() != "enable_if" || !TD->isInStdNamespace())
       return std::nullopt;
 
     int NumArgs = SpecializationLoc.getNumArgs();
@@ -101,7 +101,7 @@ matchEnableIfSpecializationImplTrait(TypeLoc TheType) {
 
     const TemplateDecl *TD =
         Specialization->getTemplateName().getAsTemplateDecl();
-    if (!TD || TD->getName() != "enable_if_t")
+    if (!TD || TD->getName() != "enable_if_t" || !TD->isInStdNamespace())
       return std::nullopt;
 
     if (!Specialization->isTypeAlias())
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..8289efe338e3f 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
@@ -756,3 +756,33 @@ abs(const number<T, ExpressionTemplates> &v) {
 }
 
 }
+
+// NOLINTBEGIN
+namespace custom {
+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 custom
+// NOLINTEND
+
+namespace use_custom {
+// We cannot assume anything about the behavior of templates that happen to be
+// named `enable_if` or `enable_if_t` if they are not declared in the namespace
+// `std`. (E.g. the first template parameter of `boost::enable_if` is a class
+// and not a boolean and `boost::enable_if<Cond, T>` is equivalent to
+// `std::enable_if<Cond::value, T>`.)
+
+template <typename T>
+typename custom::enable_if<T::some_value, Obj>::type custom_basic() {
+  return Obj{};
+}
+
+template <typename T>
+custom::enable_if_t<T::some_value, Obj> custom_basic_t() {
+  return Obj{};
+}
+}

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

Reply via email to