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