[clang-tools-extra] [clang-tidy] Add modernize-make-direct check (PR #118120)
Helmut Januschka via cfe-commits
cfe-commits at lists.llvm.org
Fri Nov 29 10:18:29 PST 2024
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 <helmut at 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");
+}
More information about the cfe-commits
mailing list