https://github.com/grigorypas updated 
https://github.com/llvm/llvm-project/pull/205440

>From cc759bc43123ea733fab22dccbdcb6a5ce83208d Mon Sep 17 00:00:00 2001
From: Grigory Pastukhov <[email protected]>
Date: Tue, 23 Jun 2026 14:23:58 -0700
Subject: [PATCH] modernize-return-braced-init-list: skip types with an
 initializer_list constructor

---
 .../modernize/ReturnBracedInitListCheck.cpp   | 30 ++++++++++
 clang-tools-extra/docs/ReleaseNotes.rst       |  5 +-
 .../modernize/return-braced-init-list.rst     |  5 ++
 .../modernize/return-braced-init-list.cpp     | 55 ++++++++++++++++++-
 4 files changed, 93 insertions(+), 2 deletions(-)

diff --git 
a/clang-tools-extra/clang-tidy/modernize/ReturnBracedInitListCheck.cpp 
b/clang-tools-extra/clang-tidy/modernize/ReturnBracedInitListCheck.cpp
index 65da5b1a2b403..0731707d7126b 100644
--- a/clang-tools-extra/clang-tidy/modernize/ReturnBracedInitListCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ReturnBracedInitListCheck.cpp
@@ -7,7 +7,9 @@
 
//===----------------------------------------------------------------------===//
 
 #include "ReturnBracedInitListCheck.h"
+#include "../utils/TypeTraits.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclTemplate.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Lex/Lexer.h"
@@ -16,6 +18,27 @@ using namespace clang::ast_matchers;
 
 namespace clang::tidy::modernize {
 
+namespace {
+bool hasInitListConstructor(const CXXRecordDecl *RD) {
+  if (RD == nullptr || !RD->hasDefinition())
+    return false;
+  auto IsInitListCtor = [](const CXXConstructorDecl *Ctor) {
+    return Ctor->hasOneParamOrDefaultArgs() &&
+           utils::type_traits::isStdInitializerList(
+               Ctor->getParamDecl(0)->getType().getNonReferenceType());
+  };
+  return llvm::any_of(RD->decls(), [&](const Decl *D) {
+    if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(D))
+      return IsInitListCtor(Ctor);
+    if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
+      if (const auto *Ctor =
+              dyn_cast<CXXConstructorDecl>(FTD->getTemplatedDecl()))
+        return IsInitListCtor(Ctor);
+    return false;
+  });
+}
+} // namespace
+
 void ReturnBracedInitListCheck::registerMatchers(MatchFinder *Finder) {
   auto SemanticallyDifferentContainer = allOf(
       hasDeclaration(
@@ -66,6 +89,13 @@ void ReturnBracedInitListCheck::check(const 
MatchFinder::MatchResult &Result) {
   if (ReturnType != ConstructType)
     return;
 
+  // Rewriting `T(args)` to a braced-init-list changes overload resolution when
+  // `T` has a std::initializer_list constructor: list-initialization prefers
+  // the initializer_list overload, so the braced form may silently select a
+  // different constructor than the parenthesized call.
+  if (hasInitListConstructor(ConstructType->getAsCXXRecordDecl()))
+    return;
+
   auto Diag = diag(Loc, "avoid repeating the return type from the "
                         "declaration; use a braced initializer list instead");
 
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index 81e5de4e0a868..d0782e79ad04a 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -625,7 +625,10 @@ Changes in existing checks
 
 - Improved :doc:`modernize-return-braced-init-list
   <clang-tidy/checks/modernize/return-braced-init-list>` check to apply fix-it
-  when type qualifiers and/or reference modifiers are used with parameters.
+  when type qualifiers and/or reference modifiers are used with parameters, and
+  to no longer rewrite the return value when the constructed type has a
+  ``std::initializer_list`` constructor, as the braced form could select a
+  different constructor.
 
 - Improved :doc:`modernize-type-traits
   <clang-tidy/checks/modernize/type-traits>` check to suggest usage of
diff --git 
a/clang-tools-extra/docs/clang-tidy/checks/modernize/return-braced-init-list.rst
 
b/clang-tools-extra/docs/clang-tidy/checks/modernize/return-braced-init-list.rst
index 2631d9056e851..c2feb31830f2c 100644
--- 
a/clang-tools-extra/docs/clang-tidy/checks/modernize/return-braced-init-list.rst
+++ 
b/clang-tools-extra/docs/clang-tidy/checks/modernize/return-braced-init-list.rst
@@ -20,3 +20,8 @@ function definition and the return statement.
     Baz baz;
     return {baz};
   }
+
+The check is not applied when the constructed type has a
+``std::initializer_list`` constructor, since list-initialization would prefer
+that constructor and the braced form could therefore select a different
+constructor than the original call.
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/modernize/return-braced-init-list.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/modernize/return-braced-init-list.cpp
index 5b0e7e7fb97f3..d8e5caa6f3c87 100644
--- 
a/clang-tools-extra/test/clang-tidy/checkers/modernize/return-braced-init-list.cpp
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/modernize/return-braced-init-list.cpp
@@ -66,8 +66,10 @@ Foo f6() {
 
 std::vector<int> vectorWithOneParameter() {
   int i7 = 1;
+  // No warning: std::vector has a std::initializer_list constructor, so
+  // `std::vector<int>{i7}` would select it (a 1-element vector) instead of
+  // `std::vector<int>(i7)` (an i7-element vector). Rewriting is unsound.
   return std::vector<int>(i7);
-  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
 }
 
 std::vector<int> vectorIntWithTwoParameter() {
@@ -84,6 +86,57 @@ std::vector<A> vectorRecordWithTwoParameter() {
 }
 
 
+struct WithInitList {
+  WithInitList(Bar) {}
+  WithInitList(std::initializer_list<WithInitList>) {}
+};
+
+// No warning: braces would select the std::initializer_list constructor
+// instead of WithInitList(Bar), changing overload resolution.
+WithInitList initListCtorNoRewrite() {
+  Bar b;
+  return WithInitList(b);
+}
+
+WithInitList initListCtorNoRewriteArg(Bar b) {
+  return WithInitList(b);
+}
+
+// `std::initializer_list` as the first parameter but with a non-defaulted
+// trailing parameter is NOT an initializer-list constructor, so braces cannot
+// select it. A warning is expected.
+struct NotInitListCtor {
+  NotInitListCtor(std::initializer_list<int>, int);
+  NotInitListCtor(Bar) {}
+};
+
+NotInitListCtor notInitListCtorRewrite(Bar b) {
+  return NotInitListCtor(b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
+  // CHECK-FIXES: return {b};
+}
+
+struct TemplatedInitListCtor {
+  TemplatedInitListCtor(Bar) {}
+  template <typename T>
+  TemplatedInitListCtor(std::initializer_list<T>) {}
+};
+
+TemplatedInitListCtor templatedInitListCtorNoRewrite(Bar b) {
+  return TemplatedInitListCtor(b);
+}
+
+// An initializer-list constructor may take the list by (cv-qualified)
+// reference; braces still prefer it, so no rewrite.
+struct RefInitListCtor {
+  RefInitListCtor(Bar) {}
+  RefInitListCtor(const std::initializer_list<RefInitListCtor> &) {}
+};
+
+RefInitListCtor refInitListCtorNoRewrite(Bar b) {
+  return RefInitListCtor(b);
+}
+
 Bar f8() {
   return {};
 }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to