[clang-tools-extra] [clang-tidy] Add modernize-make-direct check (PR #118120)
Helmut Januschka via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 10 14:31:59 PDT 2025
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 <helmut at 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 <helmut at 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 <helmut at 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).
More information about the cfe-commits
mailing list