https://github.com/hjanuschka updated https://github.com/llvm/llvm-project/pull/118120
>From c741fffc1aa978e39946bb34efc455dc9333f993 Mon Sep 17 00:00:00 2001 From: Helmut Januschka <hel...@januschka.com> Date: Fri, 29 Nov 2024 19:17:36 +0100 Subject: [PATCH 1/3] [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 bab1167fb15ff..56273f914178b 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -50,6 +50,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 0000000000000..0262f77f4992c --- /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 0000000000000..0fb786e827a71 --- /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 e872759856f3c..e38340bb66789 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" @@ -125,6 +126,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 882ee0015df17..e5c1d4d107aba 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -124,6 +124,12 @@ New checks pointer and store it as class members without handle the copy and move constructors and the assignments. +- 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-misleading-setter-of-reference <clang-tidy/checks/bugprone/misleading-setter-of-reference>` 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 0000000000000..6860ff39c514c --- /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 0000000000000..db3e4fe1cb4ff --- /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"); +} >From ccfe7ffc48b7efa779ab305f738343444b62cf94 Mon Sep 17 00:00:00 2001 From: Helmut Januschka <hel...@januschka.com> Date: Tue, 10 Jun 2025 21:43:22 +0200 Subject: [PATCH 2/3] Address PR feedback and refactor to TransformerClangTidyCheck - Refactor implementation to use TransformerClangTidyCheck for cleaner code - Remove make_unique and make_shared transformations to avoid conflicts and new usage - Add configuration options: CheckMakePair, CheckMakeOptional, CheckMakeTuple - Add support for std::make_tuple transformations - Update documentation with options and warnings about smart pointer functions - Fix clang-format issues in ModernizeTidyModule.cpp - Add C++17 requirement enforcement with proper test setup - All tests passing with correct CTAD transformations --- .../modernize/MakeFunctionToDirectCheck.cpp | 151 +++++++----------- .../modernize/MakeFunctionToDirectCheck.h | 41 +++-- .../modernize/ModernizeTidyModule.cpp | 2 +- clang-tools-extra/docs/ReleaseNotes.rst | 2 +- .../modernize/modernize-make-direct.rst | 33 +++- .../checkers/modernize/make-direct-check.cpp | 32 ++-- 6 files changed, 143 insertions(+), 118 deletions(-) diff --git a/clang-tools-extra/clang-tidy/modernize/MakeFunctionToDirectCheck.cpp b/clang-tools-extra/clang-tidy/modernize/MakeFunctionToDirectCheck.cpp index 0262f77f4992c..395a9a500edd1 100644 --- a/clang-tools-extra/clang-tidy/modernize/MakeFunctionToDirectCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/MakeFunctionToDirectCheck.cpp @@ -1,108 +1,75 @@ +//===--- MakeFunctionToDirectCheck.cpp - clang-tidy ----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + #include "MakeFunctionToDirectCheck.h" -#include "clang/AST/ASTContext.h" -#include "clang/AST/Type.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "../utils/TransformerClangTidyCheck.h" #include "clang/ASTMatchers/ASTMatchers.h" -#include "clang/Lex/Lexer.h" +#include "clang/Tooling/Transformer/RangeSelector.h" +#include "clang/Tooling/Transformer/RewriteRule.h" +#include "clang/Tooling/Transformer/Stencil.h" -using namespace clang::ast_matchers; +using namespace ::clang::ast_matchers; +using namespace ::clang::transformer; 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())); +namespace { + +RewriteRuleWith<std::string> makeFunctionToDirectCheckImpl( + bool CheckMakePair, bool CheckMakeOptional, bool CheckMakeTuple) { + std::vector<RewriteRuleWith<std::string>> Rules; + + // Helper to create a rule for a specific make_* function + auto createRule = [](StringRef MakeFunction, StringRef DirectType) { + auto WarningMessage = cat("use class template argument deduction (CTAD) " + "instead of ", MakeFunction); + + return makeRule( + callExpr(callee(functionDecl(hasName(MakeFunction))), + unless(hasParent(implicitCastExpr( + hasImplicitDestinationType(qualType(hasCanonicalType( + qualType(asString("void"))))))))) + .bind("make_call"), + changeTo(node("make_call"), cat(DirectType, "(", callArgs("make_call"), ")")), + WarningMessage); + }; + + if (CheckMakeOptional) { + Rules.push_back(createRule("std::make_optional", "std::optional")); } - std::string Replacement; - if (FuncName == "make_unique" || FuncName == "make_shared") { - const auto *TemplateArgs = FuncDecl->getTemplateSpecializationArgs(); - if (!TemplateArgs || TemplateArgs->size() == 0) - return; + if (CheckMakePair) { + Rules.push_back(createRule("std::make_pair", "std::pair")); + } - QualType Type = TemplateArgs->get(0).getAsType(); - PrintingPolicy Policy(Result.Context->getLangOpts()); - Policy.SuppressTagKeyword = true; - std::string TypeStr = Type.getAsString(Policy); + if (CheckMakeTuple) { + Rules.push_back(createRule("std::make_tuple", "std::tuple")); + } - 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"; + return applyFirst(Rules); +} - if (TemplateType.empty()) - return; +} // namespace - Replacement = TemplateType + "(" + Args + ")"; - } +MakeFunctionToDirectCheck::MakeFunctionToDirectCheck(StringRef Name, + ClangTidyContext *Context) + : utils::TransformerClangTidyCheck(Name, Context), + CheckMakePair(Options.get("CheckMakePair", true)), + CheckMakeOptional(Options.get("CheckMakeOptional", true)), + CheckMakeTuple(Options.get("CheckMakeTuple", true)) { + setRule(makeFunctionToDirectCheckImpl(CheckMakePair, CheckMakeOptional, CheckMakeTuple)); +} - if (!Replacement.empty()) { - diag(Call->getBeginLoc(), - "use class template argument deduction (CTAD) instead of %0") - << FuncName - << FixItHint::CreateReplacement( - CharSourceRange::getTokenRange(Call->getSourceRange()), - Replacement); - } +void MakeFunctionToDirectCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "CheckMakePair", CheckMakePair); + Options.store(Opts, "CheckMakeOptional", CheckMakeOptional); + Options.store(Opts, "CheckMakeTuple", CheckMakeTuple); } } // 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 index 0fb786e827a71..c3637b2bc00dc 100644 --- a/clang-tools-extra/clang-tidy/modernize/MakeFunctionToDirectCheck.h +++ b/clang-tools-extra/clang-tidy/modernize/MakeFunctionToDirectCheck.h @@ -1,20 +1,39 @@ -#include "../ClangTidyCheck.h" +//===--- MakeFunctionToDirectCheck.h - clang-tidy --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKEFUNCTIONTODIRECTCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKEFUNCTIONTODIRECTCHECK_H + +#include "../utils/TransformerClangTidyCheck.h" namespace clang::tidy::modernize { -class MakeFunctionToDirectCheck : public ClangTidyCheck { +/// Converts std::make_* function calls to direct constructor calls using +/// class template argument deduction (CTAD). +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/make-direct.html +class MakeFunctionToDirectCheck : public utils::TransformerClangTidyCheck { public: - MakeFunctionToDirectCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} + MakeFunctionToDirectCheck(StringRef Name, ClangTidyContext *Context); - void registerMatchers(ast_matchers::MatchFinder *Finder) override; - void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus17; + } + + void storeOptions(ClangTidyOptions::OptionMap &Opts) 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; + const bool CheckMakePair; + const bool CheckMakeOptional; + const bool CheckMakeTuple; }; -} // namespace clang::tidy::modernize \ No newline at end of file +} // namespace clang::tidy::modernize + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKEFUNCTIONTODIRECTCHECK_H \ 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 e38340bb66789..4c6964a729346 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -127,7 +127,7 @@ class ModernizeModule : public ClangTidyModule { "modernize-use-uncaught-exceptions"); CheckFactories.registerCheck<UseUsingCheck>("modernize-use-using"); CheckFactories.registerCheck<MakeFunctionToDirectCheck>( - "modernize-make-direct"); + "modernize-make-direct"); } }; diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index e5c1d4d107aba..8917627b13ea0 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -127,7 +127,7 @@ 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 + Transforms make_optional, make_pair, and make_tuple into equivalent direct constructor calls using C++17's class template argument deduction. - New :doc:`bugprone-misleading-setter-of-reference 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 index 6860ff39c514c..c11a9b66bdb22 100644 --- 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 @@ -10,8 +10,37 @@ 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")`` +``std::make_tuple(1, 2.0, "hi")`` ``std::tuple(1, 2.0, "hi")`` ================================== ==================================== +.. note:: + + This check does not transform ``std::make_unique`` or ``std::make_shared`` because: + + 1. These smart pointer types cannot be constructed using CTAD from raw pointers. + 2. ``std::make_shared`` provides performance benefits (single allocation) and + exception safety that would be lost with direct construction. + 3. Direct use of ``new`` is discouraged in modern C++ code. + + Use the dedicated ``modernize-make-unique`` and ``modernize-make-shared`` checks + for transforming these functions. + +Options +------- + +.. option:: CheckMakePair + + When `true`, transforms ``std::make_pair`` calls to direct constructor calls. + Default is `true`. + +.. option:: CheckMakeOptional + + When `true`, transforms ``std::make_optional`` calls to direct constructor calls. + Default is `true`. + +.. option:: CheckMakeTuple + + When `true`, transforms ``std::make_tuple`` calls to direct constructor calls. + Default is `true`. + 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 index db3e4fe1cb4ff..2a1fe4713ecb9 100644 --- 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 @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s modernize-make-direct %t +// RUN: %check_clang_tidy -std=c++17 %s modernize-make-direct %t namespace std { template<typename T> @@ -40,6 +40,16 @@ template<typename T, typename U> pair<T,U> make_pair(T&& t, U&& u) { return pair<T,U>(t, u); } + +template<typename... T> +struct tuple { + tuple(const T&... args) {} +}; + +template<typename... T> +tuple<T...> make_tuple(T&&... t) { + return tuple<T...>(t...); +} } struct Widget { @@ -48,19 +58,19 @@ struct Widget { 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 o1 = std::make_optional<int>(42); + // CHECK-MESSAGES: warning: use class template argument deduction (CTAD) instead of std::make_optional [modernize-make-direct] + // CHECK-FIXES: auto o1 = std::optional(42); + // make_unique and make_shared are not transformed by this check 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 s1 = std::make_shared<Widget>(2); auto p1 = std::make_pair(1, "test"); - // CHECK-MESSAGES: warning: use class template argument deduction (CTAD) instead of make_pair + // CHECK-MESSAGES: warning: use class template argument deduction (CTAD) instead of std::make_pair [modernize-make-direct] // CHECK-FIXES: auto p1 = std::pair(1, "test"); + + auto t1 = std::make_tuple(1, 2.0, "hi"); + // CHECK-MESSAGES: warning: use class template argument deduction (CTAD) instead of std::make_tuple [modernize-make-direct] + // CHECK-FIXES: auto t1 = std::tuple(1, 2.0, "hi"); } >From 837b5973366f69746a987dfd80dc4de6a7886377 Mon Sep 17 00:00:00 2001 From: Helmut Januschka <hel...@januschka.com> Date: Tue, 10 Jun 2025 23:29:49 +0200 Subject: [PATCH 3/3] Fix CI failures: clang-format and RST title underline - Apply clang-format fixes to MakeFunctionToDirectCheck.cpp - Fix RST title underline length in modernize-make-direct.rst documentation - All tests passing after fixes --- .../modernize/MakeFunctionToDirectCheck.cpp | 19 +++++++++++-------- .../modernize/modernize-make-direct.rst | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/clang-tools-extra/clang-tidy/modernize/MakeFunctionToDirectCheck.cpp b/clang-tools-extra/clang-tidy/modernize/MakeFunctionToDirectCheck.cpp index 395a9a500edd1..f9eabbaefb46f 100644 --- a/clang-tools-extra/clang-tidy/modernize/MakeFunctionToDirectCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/MakeFunctionToDirectCheck.cpp @@ -20,22 +20,24 @@ namespace clang::tidy::modernize { namespace { -RewriteRuleWith<std::string> makeFunctionToDirectCheckImpl( - bool CheckMakePair, bool CheckMakeOptional, bool CheckMakeTuple) { +RewriteRuleWith<std::string> +makeFunctionToDirectCheckImpl(bool CheckMakePair, bool CheckMakeOptional, + bool CheckMakeTuple) { std::vector<RewriteRuleWith<std::string>> Rules; // Helper to create a rule for a specific make_* function auto createRule = [](StringRef MakeFunction, StringRef DirectType) { auto WarningMessage = cat("use class template argument deduction (CTAD) " - "instead of ", MakeFunction); + "instead of ", + MakeFunction); return makeRule( callExpr(callee(functionDecl(hasName(MakeFunction))), - unless(hasParent(implicitCastExpr( - hasImplicitDestinationType(qualType(hasCanonicalType( - qualType(asString("void"))))))))) + unless(hasParent(implicitCastExpr(hasImplicitDestinationType( + qualType(hasCanonicalType(qualType(asString("void"))))))))) .bind("make_call"), - changeTo(node("make_call"), cat(DirectType, "(", callArgs("make_call"), ")")), + changeTo(node("make_call"), + cat(DirectType, "(", callArgs("make_call"), ")")), WarningMessage); }; @@ -62,7 +64,8 @@ MakeFunctionToDirectCheck::MakeFunctionToDirectCheck(StringRef Name, CheckMakePair(Options.get("CheckMakePair", true)), CheckMakeOptional(Options.get("CheckMakeOptional", true)), CheckMakeTuple(Options.get("CheckMakeTuple", true)) { - setRule(makeFunctionToDirectCheckImpl(CheckMakePair, CheckMakeOptional, CheckMakeTuple)); + setRule(makeFunctionToDirectCheckImpl(CheckMakePair, CheckMakeOptional, + CheckMakeTuple)); } void MakeFunctionToDirectCheck::storeOptions( 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 index c11a9b66bdb22..b856c150f28cf 100644 --- 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 @@ -1,7 +1,7 @@ .. title:: clang-tidy - modernize-make-direct modernize-make-direct -==================== +===================== Replaces ``std::make_*`` function calls with direct constructor calls using class template argument deduction (CTAD). _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits