[clang-tools-extra] [clang-tidy] Limit modernize-use-constraints to standard enable_if (PR #155237)
DonĂ¡t Nagy via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 25 05:06:09 PDT 2025
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.nagy at 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{};
+}
+}
More information about the cfe-commits
mailing list