diff --git a/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp index e9b96c4016af6..4dc1a0a2c47a4 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" @@ -78,6 +79,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( + TD->getTemplateParameters()->getParam(0)); + if (!FirstParam || !FirstParam->getType()->isBooleanType()) + return std::nullopt; + int NumArgs = SpecializationLoc.getNumArgs(); if (NumArgs != 1 && NumArgs != 2) return std::nullopt; @@ -108,6 +116,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( + TD->getTemplateParameters()->getParam(0)); + if (!FirstParam || !FirstParam->getType()->isBooleanType()) + return std::nullopt; + if (const auto *AliasedType = dyn_cast(Specialization->getAliasedType())) { ElaboratedTypeKeyword Keyword = AliasedType->getKeyword(); diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 2de2818172850..51deab4dfd234 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -136,6 +136,11 @@ Changes in existing checks - Improved :doc:`misc-header-include-cycle ` check performance. +- Improved :doc:`modernize-use-constraints + ` check by fixing a crash on + uses of non-standard ``enable_if`` with a signature different from + ``std::enable_if`` (such as ``boost::enable_if``). + - Improved :doc:`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 &v) { } } + +template +struct some_type_trait { + static constexpr bool value = true; +}; + +// Fix-its are offered even for a non-standard enable_if. +namespace nonstd { + +template +struct enable_if : std::enable_if {}; + +template +using enable_if_t = typename enable_if::type; + +} + +template +typename nonstd::enable_if::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::value {}{{$}} + +template +nonstd::enable_if_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::value {}{{$}} + +template <> +nonstd::enable_if_t::value, void> nonstd_enable_if_t() {} +// FIXME - Support non-dependent enable_ifs. + +template +typename nonstd::enable_if::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::value {}{{$}} + +template +nonstd::enable_if_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::value {}{{$}} + +// No fix-its are offered for an enable_if with a different signature from the standard one. +namespace boost { + +template +struct enable_if : std::enable_if {}; + +template +using enable_if_t = typename enable_if::type; + +} + +template +typename boost::enable_if, void>::type boost_enable_if() {} + +template +boost::enable_if_t, void> boost_enable_if_t() {} + +template <> +boost::enable_if_t, void> boost_enable_if_t() {} + +template +typename boost::enable_if>::type boost_enable_if_one_param() {} + +template +boost::enable_if_t> boost_enable_if_t_one_param() {}