https://github.com/hjanuschka created https://github.com/llvm/llvm-project/pull/118120
Adds a new check that converts std::make_* function calls to direct constructor calls using CTAD. Transforms make_optional, make_unique, make_shared and make_pair into their equivalent direct constructor calls, leveraging C++17's class template argument deduction. >From 2bcf868d023b5be2f3c88f1d68dd2084b7db4604 Mon Sep 17 00:00:00 2001 From: Helmut Januschka <hel...@januschka.com> Date: Fri, 29 Nov 2024 19:17:36 +0100 Subject: [PATCH] [clang-tidy] Add modernize-make-direct check Adds a new check that converts std::make_* function calls to direct constructor calls using CTAD. Transforms make_optional, make_unique, make_shared and make_pair into their equivalent direct constructor calls, leveraging C++17's class template argument deduction. --- .../clang-tidy/modernize/CMakeLists.txt | 1 + .../modernize/MakeFunctionToDirectCheck.cpp | 108 ++++++++++++++++++ .../modernize/MakeFunctionToDirectCheck.h | 20 ++++ .../modernize/ModernizeTidyModule.cpp | 3 + clang-tools-extra/docs/ReleaseNotes.rst | 6 + .../modernize/modernize-make-direct.rst | 17 +++ .../checkers/modernize/make-direct-check.cpp | 66 +++++++++++ 7 files changed, 221 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/modernize/MakeFunctionToDirectCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/modernize/MakeFunctionToDirectCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/modernize-make-direct.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/make-direct-check.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index c919d49b42873a..7e8d9296c6a64c 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -49,6 +49,7 @@ add_clang_library(clangTidyModernizeModule STATIC UseTransparentFunctorsCheck.cpp UseUncaughtExceptionsCheck.cpp UseUsingCheck.cpp + MakeFunctionToDirectCheck.cpp LINK_LIBS clangTidy diff --git a/clang-tools-extra/clang-tidy/modernize/MakeFunctionToDirectCheck.cpp b/clang-tools-extra/clang-tidy/modernize/MakeFunctionToDirectCheck.cpp new file mode 100644 index 00000000000000..0262f77f4992cb --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/MakeFunctionToDirectCheck.cpp @@ -0,0 +1,108 @@ +#include "MakeFunctionToDirectCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Type.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::modernize { + +void MakeFunctionToDirectCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus17) + return; + // Match make_xxx function calls + Finder->addMatcher(callExpr(callee(functionDecl(hasAnyName( + "std::make_optional", "std::make_unique", + "std::make_shared", "std::make_pair")))) + .bind("make_call"), + this); +} + +bool MakeFunctionToDirectCheck::isMakeFunction( + const std::string &FuncName) const { + static const std::array<std::string_view, 4> MakeFuncs = { + "make_optional", "make_unique", "make_shared", "make_pair"}; + + return std::any_of(MakeFuncs.begin(), MakeFuncs.end(), + [&](const auto &Prefix) { + return FuncName.find(Prefix) != std::string::npos; + }); +} + +std::string MakeFunctionToDirectCheck::getTemplateType( + const CXXConstructExpr *Construct) const { + if (!Construct) + return {}; + + const auto *RecordType = + dyn_cast<clang::RecordType>(Construct->getType().getTypePtr()); + if (!RecordType) + return {}; + + return RecordType->getDecl()->getNameAsString(); +} + +void MakeFunctionToDirectCheck::check(const MatchFinder::MatchResult &Result) { + const auto *Call = Result.Nodes.getNodeAs<CallExpr>("make_call"); + if (!Call) + return; + + const auto *FuncDecl = dyn_cast<FunctionDecl>(Call->getCalleeDecl()); + if (!FuncDecl || !FuncDecl->getTemplateSpecializationArgs()) + return; + + std::string FuncName = FuncDecl->getNameAsString(); + if (!isMakeFunction(FuncName)) + return; + + std::string Args; + if (Call->getNumArgs() > 0) { + SourceRange ArgRange(Call->getArg(0)->getBeginLoc(), + Call->getArg(Call->getNumArgs() - 1)->getEndLoc()); + Args = std::string(Lexer::getSourceText( + CharSourceRange::getTokenRange(ArgRange), *Result.SourceManager, + Result.Context->getLangOpts())); + } + + std::string Replacement; + if (FuncName == "make_unique" || FuncName == "make_shared") { + const auto *TemplateArgs = FuncDecl->getTemplateSpecializationArgs(); + if (!TemplateArgs || TemplateArgs->size() == 0) + return; + + QualType Type = TemplateArgs->get(0).getAsType(); + PrintingPolicy Policy(Result.Context->getLangOpts()); + Policy.SuppressTagKeyword = true; + std::string TypeStr = Type.getAsString(Policy); + + std::string SmartPtr = + (FuncName == "make_unique") ? "unique_ptr" : "shared_ptr"; + Replacement = "std::" + SmartPtr + "(new " + TypeStr + "(" + Args + "))"; + } else { + std::string TemplateType; + if (FuncName == "make_optional") + TemplateType = "std::optional"; + else if (FuncName == "make_shared") + TemplateType = "std::shared_ptr"; + else if (FuncName == "make_pair") + TemplateType = "std::pair"; + + if (TemplateType.empty()) + return; + + Replacement = TemplateType + "(" + Args + ")"; + } + + if (!Replacement.empty()) { + diag(Call->getBeginLoc(), + "use class template argument deduction (CTAD) instead of %0") + << FuncName + << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(Call->getSourceRange()), + Replacement); + } +} + +} // namespace clang::tidy::modernize \ No newline at end of file diff --git a/clang-tools-extra/clang-tidy/modernize/MakeFunctionToDirectCheck.h b/clang-tools-extra/clang-tidy/modernize/MakeFunctionToDirectCheck.h new file mode 100644 index 00000000000000..0fb786e827a71e --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/MakeFunctionToDirectCheck.h @@ -0,0 +1,20 @@ +#include "../ClangTidyCheck.h" + +namespace clang::tidy::modernize { + +class MakeFunctionToDirectCheck : public ClangTidyCheck { +public: + MakeFunctionToDirectCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + // Helper to check if the call is a make_xxx function + bool isMakeFunction(const std::string &FuncName) const; + // Get the template type from make_xxx call + std::string getTemplateType(const CXXConstructExpr *Construct) const; +}; + +} // namespace clang::tidy::modernize \ No newline at end of file diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index 18607593320635..45754f0a0bc8f0 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -16,6 +16,7 @@ #include "DeprecatedIosBaseAliasesCheck.h" #include "LoopConvertCheck.h" #include "MacroToEnumCheck.h" +#include "MakeFunctionToDirectCheck.h" #include "MakeSharedCheck.h" #include "MakeUniqueCheck.h" #include "MinMaxUseInitializerListCheck.h" @@ -122,6 +123,8 @@ class ModernizeModule : public ClangTidyModule { CheckFactories.registerCheck<UseUncaughtExceptionsCheck>( "modernize-use-uncaught-exceptions"); CheckFactories.registerCheck<UseUsingCheck>("modernize-use-using"); + CheckFactories.registerCheck<MakeFunctionToDirectCheck>( + "modernize-make-direct"); } }; diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index f050391110385e..c904b420688d5c 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -118,6 +118,12 @@ Improvements to clang-tidy New checks ^^^^^^^^^^ +- New :doc:`modernize-make-direct <clang-tidy/checks/modernize/make-direct>` check. + + Converts std::make_* function calls to direct constructor calls using CTAD. + Transforms make_optional, make_unique, make_shared and make_pair into equivalent + direct constructor calls using C++17's class template argument deduction. + - New :doc:`bugprone-bitwise-pointer-cast <clang-tidy/checks/bugprone/bitwise-pointer-cast>` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/modernize-make-direct.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/modernize-make-direct.rst new file mode 100644 index 00000000000000..6860ff39c514c0 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/modernize-make-direct.rst @@ -0,0 +1,17 @@ +.. title:: clang-tidy - modernize-make-direct + +modernize-make-direct +==================== + +Replaces ``std::make_*`` function calls with direct constructor calls using class template +argument deduction (CTAD). + +================================== ==================================== + Before After +---------------------------------- ------------------------------------ +``std::make_optional<int>(42)`` ``std::optional(42)`` +``std::make_unique<Widget>(1)`` ``std::unique_ptr(new Widget(1))`` +``std::make_shared<Widget>(2)`` ``std::shared_ptr(new Widget(2))`` +``std::make_pair(1, "test")`` ``std::pair(1, "test")`` +================================== ==================================== + diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/make-direct-check.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/make-direct-check.cpp new file mode 100644 index 00000000000000..db3e4fe1cb4ffb --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/make-direct-check.cpp @@ -0,0 +1,66 @@ +// RUN: %check_clang_tidy %s modernize-make-direct %t + +namespace std { +template<typename T> +struct optional { + optional(const T&) {} +}; + +template<typename T> +optional<T> make_optional(const T& t) { return optional<T>(t); } + +template<typename T> +struct unique_ptr { + explicit unique_ptr(T*) {} +}; + +template<typename T, typename... Args> +unique_ptr<T> make_unique(Args&&... args) { + return unique_ptr<T>(new T(args...)); +} + +template<typename T> +struct shared_ptr { + shared_ptr(T*) {} +}; + +template<typename T, typename... Args> +shared_ptr<T> make_shared(Args&&... args) { + return shared_ptr<T>(new T(args...)); +} + +template<typename T, typename U> +struct pair { + T first; + U second; + pair(const T& x, const U& y) : first(x), second(y) {} +}; + +template<typename T, typename U> +pair<T,U> make_pair(T&& t, U&& u) { + return pair<T,U>(t, u); +} +} + +struct Widget { + Widget(int x) {} +}; + + +void basic_tests() { + auto o1 = std::make_optional<int>(42); + // CHECK-MESSAGES: warning: use class template argument deduction (CTAD) instead of make_optional [modernize-make-direct] + // CHECK-FIXES: auto o1 = std::optional(42); + + auto u1 = std::make_unique<Widget>(1); + // CHECK-MESSAGES: warning: use class template argument deduction (CTAD) instead of make_unique [modernize-make-direct] + // CHECK-FIXES: auto u1 = std::unique_ptr(new Widget(1)); + + auto s1 = std::make_shared<Widget>(2); + // CHECK-MESSAGES: warning: use class template argument deduction (CTAD) instead of make_shared + // CHECK-FIXES: auto s1 = std::shared_ptr(new Widget(2)); + + auto p1 = std::make_pair(1, "test"); + // CHECK-MESSAGES: warning: use class template argument deduction (CTAD) instead of make_pair + // CHECK-FIXES: auto p1 = std::pair(1, "test"); +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits