https://github.com/RiverDave updated 
https://github.com/llvm/llvm-project/pull/131969

>From 9972aa8e1720f7e6378c63551c853ee504193000 Mon Sep 17 00:00:00 2001
From: David Rivera <davidriv...@gmail.com>
Date: Sun, 16 Mar 2025 16:20:16 -0400
Subject: [PATCH] [clang-tidy] Add support for Initialization Forwarding in
 Nested Objects within modernize-use-emplace

---
 .../clang-tidy/modernize/UseEmplaceCheck.cpp  | 105 +++++++++++++++---
 clang-tools-extra/docs/ReleaseNotes.rst       |   4 +
 .../checkers/modernize/use-emplace.cpp        |  48 ++++++--
 3 files changed, 132 insertions(+), 25 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.cpp 
b/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.cpp
index 430455a38f395..56c5a73a0c8b1 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.cpp
@@ -98,8 +98,8 @@ auto hasWantedType(llvm::ArrayRef<StringRef> TypeNames) {
 
 // Matches member call expressions of the named method on the listed container
 // types.
-auto cxxMemberCallExprOnContainer(
-    StringRef MethodName, llvm::ArrayRef<StringRef> ContainerNames) {
+auto cxxMemberCallExprOnContainer(StringRef MethodName,
+                                  llvm::ArrayRef<StringRef> ContainerNames) {
   return cxxMemberCallExpr(
       hasDeclaration(functionDecl(hasName(MethodName))),
       on(hasTypeOrPointeeType(hasWantedType(ContainerNames))));
@@ -174,19 +174,19 @@ void UseEmplaceCheck::registerMatchers(MatchFinder 
*Finder) {
   // passed pointer because smart pointer won't be constructed
   // (and destructed) as in push_back case.
   auto IsCtorOfSmartPtr =
-      hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(SmartPointers))));
+      cxxConstructorDecl(ofClass(hasAnyName(SmartPointers)));
 
   // Bitfields binds only to consts and emplace_back take it by universal ref.
-  auto BitFieldAsArgument = hasAnyArgument(
-      ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(isBitField())))));
+  auto BitFieldAsArgument =
+      ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(isBitField()))));
 
   // Initializer list can't be passed to universal reference.
-  auto InitializerListAsArgument = hasAnyArgument(
+  auto InitializerListAsArgument =
       ignoringImplicit(allOf(cxxConstructExpr(isListInitialization()),
-                             unless(cxxTemporaryObjectExpr()))));
+                             unless(cxxTemporaryObjectExpr())));
 
   // We could have leak of resource.
-  auto NewExprAsArgument = hasAnyArgument(ignoringImplicit(cxxNewExpr()));
+  auto NewExprAsArgument = ignoringImplicit(cxxNewExpr());
   // We would call another constructor.
   auto ConstructingDerived =
       hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase)));
@@ -202,11 +202,26 @@ void UseEmplaceCheck::registerMatchers(MatchFinder 
*Finder) {
   // overloaded functions and template names.
   auto SoughtConstructExpr =
       cxxConstructExpr(
-          unless(anyOf(IsCtorOfSmartPtr, HasInitList, BitFieldAsArgument,
-                       InitializerListAsArgument, NewExprAsArgument,
-                       ConstructingDerived, IsPrivateOrProtectedCtor)))
+          unless(anyOf(hasDeclaration(IsCtorOfSmartPtr), HasInitList,
+                       hasAnyArgument(BitFieldAsArgument),
+                       hasAnyArgument(InitializerListAsArgument),
+                       hasAnyArgument(NewExprAsArgument), ConstructingDerived,
+                       IsPrivateOrProtectedCtor)))
           .bind("ctor");
-  auto HasConstructExpr = has(ignoringImplicit(SoughtConstructExpr));
+
+  auto IsPrimitiveType = hasType(builtinType());
+
+  auto AggregateInitExpr =
+      getLangOpts().CPlusPlus20
+          ? initListExpr(unless(anyOf(HasInitList, has(IsCtorOfSmartPtr),
+                                      has(BitFieldAsArgument),
+                                      has(InitializerListAsArgument),
+                                      has(NewExprAsArgument), 
IsPrimitiveType)))
+                .bind("agg_init")
+          : unless(anything());
+
+  auto HasConstructExpr =
+      has(ignoringImplicit(anyOf(SoughtConstructExpr, AggregateInitExpr)));
 
   // allow for T{} to be replaced, even if no CTOR is declared
   auto HasConstructInitListExpr = has(initListExpr(
@@ -305,6 +320,36 @@ void UseEmplaceCheck::registerMatchers(MatchFinder 
*Finder) {
       this);
 }
 
+static const CXXConstructExpr *unwrapConstructorExpr(const Expr *E) {
+
+  while (E) {
+
+    if (const auto *ConstructorExpr = llvm::dyn_cast<CXXConstructExpr>(E)) {
+      return ConstructorExpr;
+    }
+
+    if (const auto *BindTemp = llvm::dyn_cast<CXXBindTemporaryExpr>(E)) {
+      E = BindTemp->getSubExpr();
+      continue;
+    }
+
+    if (const auto *MaterialTemp =
+            llvm::dyn_cast<MaterializeTemporaryExpr>(E)) {
+      E = MaterialTemp->getSubExpr();
+      continue;
+    }
+
+    if (const auto *Cast = llvm::dyn_cast<ImplicitCastExpr>(E)) {
+      E = Cast->getSubExpr();
+      continue;
+    }
+
+    break;
+  }
+
+  return nullptr; // No relevant sub-expression found
+}
+
 void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
   const auto *PushBackCall =
       Result.Nodes.getNodeAs<CXXMemberCallExpr>("push_back_call");
@@ -313,7 +358,8 @@ void UseEmplaceCheck::check(const MatchFinder::MatchResult 
&Result) {
       Result.Nodes.getNodeAs<CXXMemberCallExpr>("push_front_call");
   const auto *EmplacyCall =
       Result.Nodes.getNodeAs<CXXMemberCallExpr>("emplacy_call");
-  const auto *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctor");
+  auto *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctor");
+  auto *AggInitCall = Result.Nodes.getNodeAs<InitListExpr>("agg_init");
   const auto *MakeCall = Result.Nodes.getNodeAs<CallExpr>("make");
   const auto *TemporaryExpr =
       Result.Nodes.getNodeAs<MaterializeTemporaryExpr>("temporary_expr");
@@ -332,12 +378,34 @@ void UseEmplaceCheck::check(const 
MatchFinder::MatchResult &Result) {
   }();
 
   assert(Call && "No call matched");
-  assert((CtorCall || MakeCall) && "No push_back parameter matched");
+  assert((CtorCall || MakeCall || AggInitCall) &&
+         "No push_back parameter matched");
 
   if (IgnoreImplicitConstructors && CtorCall && CtorCall->getNumArgs() >= 1 &&
       CtorCall->getArg(0)->getSourceRange() == CtorCall->getSourceRange())
     return;
 
+  if (IgnoreImplicitConstructors && AggInitCall &&
+      AggInitCall->getNumInits() >= 1 &&
+      AggInitCall->getInit(0)->getSourceRange() ==
+          AggInitCall->getSourceRange())
+    return;
+
+  if (getLangOpts().LangStd >= LangStandard::lang_cxx20 && AggInitCall) {
+    for (const auto *Init : AggInitCall->inits()) {
+      if (const auto *InnerConstructorExpr = unwrapConstructorExpr(Init)) {
+        // consume all args if it's an empty constructor call so that we can ->
+        // T{} -> emplace_back()
+        if (InnerConstructorExpr && InnerConstructorExpr->getNumArgs() == 0) {
+
+          CtorCall = InnerConstructorExpr;
+          AggInitCall = nullptr;
+          break;
+        }
+      }
+    }
+  }
+
   const auto FunctionNameSourceRange = CharSourceRange::getCharRange(
       Call->getExprLoc(), Call->getArg(0)->getExprLoc());
 
@@ -345,6 +413,7 @@ void UseEmplaceCheck::check(const MatchFinder::MatchResult 
&Result) {
       EmplacyCall
           ? diag(TemporaryExpr ? TemporaryExpr->getBeginLoc()
                  : CtorCall    ? CtorCall->getBeginLoc()
+                 : AggInitCall ? AggInitCall->getBeginLoc()
                                : MakeCall->getBeginLoc(),
                  "unnecessary temporary object created while calling %0")
           : diag(Call->getExprLoc(), "use emplace%select{|_back|_front}0 "
@@ -376,9 +445,10 @@ void UseEmplaceCheck::check(const MatchFinder::MatchResult 
&Result) {
   }
 
   const SourceRange CallParensRange =
-      MakeCall ? SourceRange(MakeCall->getCallee()->getEndLoc(),
-                             MakeCall->getRParenLoc())
-               : CtorCall->getParenOrBraceRange();
+      MakeCall   ? SourceRange(MakeCall->getCallee()->getEndLoc(),
+                               MakeCall->getRParenLoc())
+      : CtorCall ? CtorCall->getParenOrBraceRange()
+                 : AggInitCall->getSourceRange();
 
   // Finish if there is no explicit constructor call.
   if (CallParensRange.getBegin().isInvalid())
@@ -387,6 +457,7 @@ void UseEmplaceCheck::check(const MatchFinder::MatchResult 
&Result) {
   // FIXME: Will there ever be a CtorCall, if there is no TemporaryExpr?
   const SourceLocation ExprBegin = TemporaryExpr ? TemporaryExpr->getExprLoc()
                                    : CtorCall    ? CtorCall->getExprLoc()
+                                   : AggInitCall ? AggInitCall->getExprLoc()
                                                  : MakeCall->getExprLoc();
 
   // Range for constructor name and opening brace.
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index 72aa05eb4dcd1..8e0c71225ff58 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -171,6 +171,10 @@ Changes in existing checks
   ``constexpr`` and ``static``` values on member initialization and by 
detecting
   explicit casting of built-in types within member list initialization.
 
+- Improved :doc:`modernize-use-emplace
+  <clang-tidy/checks/modernize/use-emplace>` check by adding support for C++20
+  Argument forwarding inside ``struct`` type objects.
+
 - Improved :doc:`modernize-use-ranges
   <clang-tidy/checks/modernize/use-ranges>` check by updating suppress 
   warnings logic for ``nullptr`` in ``std::find``.
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace.cpp 
b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace.cpp
index e6562cd18dbab..fdb7b54f63acd 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace.cpp
@@ -1,4 +1,12 @@
-// RUN: %check_clang_tidy %s modernize-use-emplace %t -- \
+// RUN: %check_clang_tidy %s -std=c++11 modernize-use-emplace %t -- \
+// RUN:   -config="{CheckOptions: \
+// RUN:             {modernize-use-emplace.ContainersWithPushBack: \
+// RUN:                '::std::vector; ::std::list; ::std::deque; 
llvm::LikeASmallVector', \
+// RUN:              modernize-use-emplace.TupleTypes: \
+// RUN:                '::std::pair; std::tuple; ::test::Single', \
+// RUN:              modernize-use-emplace.TupleMakeFunctions: \
+// RUN:                '::std::make_pair; ::std::make_tuple; 
::test::MakeSingle'}}"
+// RUN: %check_clang_tidy -std=c++20 %s modernize-use-emplace 
-check-suffixes=,CPP20 %t  -- \
 // RUN:   -config="{CheckOptions: \
 // RUN:             {modernize-use-emplace.ContainersWithPushBack: \
 // RUN:                '::std::vector; ::std::list; ::std::deque; 
llvm::LikeASmallVector', \
@@ -1237,10 +1245,6 @@ void testBracedInitTemporaries() {
 
   // These should not be noticed or fixed; after the correction, the code won't
   // compile.
-  v1.push_back(NonTrivialNoCtor{""});
-  v1.push_back({""});
-  v1.push_back(NonTrivialNoCtor{InnerType{""}});
-  v1.push_back({InnerType{""}});
   v1.emplace_back(NonTrivialNoCtor{""});
 
   std::vector<NonTrivialWithVector> v2;
@@ -1285,11 +1289,9 @@ void testBracedInitTemporaries() {
 
 
   // These should not be noticed or fixed; after the correction, the code won't
-  // compile.
+  // compile in version previous to C++20.
   v2.push_back(NonTrivialWithVector{{0}});
   v2.push_back({{0}});
-  v2.push_back(NonTrivialWithVector{std::vector<int>{0}});
-  v2.push_back({std::vector<int>{0}});
   v2.emplace_back(NonTrivialWithVector{std::vector<int>{0}});
 
   std::vector<NonTrivialWithCtor> v3;
@@ -1434,3 +1436,33 @@ void testWithPointerTypes() {
   // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: unnecessary temporary object 
created while calling emplace
   // CHECK-FIXES: sp->emplace();
 }
+
+namespace GH1225740 {
+
+void CXX20testBracedInitTemporaries(){
+
+  std::vector<NonTrivialNoCtor> v1;
+
+  v1.push_back(NonTrivialNoCtor{""});
+  // CHECK-MESSAGES-CPP20: :[[@LINE-1]]:6: warning: use emplace_back instead 
of push_back
+  // CHECK-FIXES-CPP20: v1.emplace_back("");
+  v1.push_back({""});
+  // CHECK-MESSAGES-CPP20: :[[@LINE-1]]:6: warning: use emplace_back instead 
of push_back
+  // CHECK-FIXES-CPP20: v1.emplace_back("");
+  v1.push_back(NonTrivialNoCtor{InnerType{""}});
+  // CHECK-MESSAGES-CPP20: :[[@LINE-1]]:6: warning: use emplace_back instead 
of push_back
+  // CHECK-FIXES-CPP20: v1.emplace_back(InnerType{""});
+  v1.push_back({InnerType{""}});
+  // CHECK-MESSAGES-CPP20: :[[@LINE-1]]:6: warning: use emplace_back instead 
of push_back
+  // CHECK-FIXES-CPP20: v1.emplace_back(InnerType{""});
+
+  std::vector<NonTrivialWithVector> v2;
+
+  v2.push_back(NonTrivialWithVector{std::vector<int>{0}});
+  // CHECK-MESSAGES-CPP20: :[[@LINE-1]]:6: warning: use emplace_back instead 
of push_back
+  // CHECK-FIXES-CPP20: v2.emplace_back(std::vector<int>{0});
+  v2.push_back({std::vector<int>{0}});
+  // CHECK-MESSAGES-CPP20: :[[@LINE-1]]:6: warning: use emplace_back instead 
of push_back
+  // CHECK-FIXES-CPP20: v2.emplace_back(std::vector<int>{0});
+  }
+}

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to