[clang-tools-extra] [clang-tidy] Add new check misc-use-braced-initialization (PR #184009)
Baranov Victor via cfe-commits
cfe-commits at lists.llvm.org
Mon May 11 02:32:05 PDT 2026
https://github.com/vbvictor updated https://github.com/llvm/llvm-project/pull/184009
>From d3930322800d4c9bad5e75a6df20d36dbc30bd40 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Mon, 11 May 2026 12:31:44 +0300
Subject: [PATCH] ~
---
.../CppCoreGuidelinesTidyModule.cpp | 3 +
.../clang-tidy/misc/CMakeLists.txt | 1 +
.../clang-tidy/misc/MiscTidyModule.cpp | 3 +
.../misc/UseBracedInitializationCheck.cpp | 431 ++++++++
.../misc/UseBracedInitializationCheck.h | 37 +
.../clang-tidy/utils/CMakeLists.txt | 1 +
.../clang-tidy/utils/LexerUtils.h | 7 +
.../clang-tidy/utils/NarrowingConversions.cpp | 107 ++
.../clang-tidy/utils/NarrowingConversions.h | 23 +
clang-tools-extra/docs/ReleaseNotes.rst | 11 +
.../use-braced-initialization.rst | 15 +
.../docs/clang-tidy/checks/list.rst | 2 +
.../checks/misc/use-braced-initialization.rst | 81 ++
.../checkers/Inputs/Headers/std/string | 4 +
.../misc/use-braced-initialization-cxx17.cpp | 41 +
.../misc/use-braced-initialization-cxx20.cpp | 284 +++++
.../misc/use-braced-initialization-cxx23.cpp | 15 +
.../misc/use-braced-initialization.cpp | 977 ++++++++++++++++++
18 files changed, 2043 insertions(+)
create mode 100644 clang-tools-extra/clang-tidy/misc/UseBracedInitializationCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/misc/UseBracedInitializationCheck.h
create mode 100644 clang-tools-extra/clang-tidy/utils/NarrowingConversions.cpp
create mode 100644 clang-tools-extra/clang-tidy/utils/NarrowingConversions.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/use-braced-initialization.rst
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/misc/use-braced-initialization.rst
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/use-braced-initialization-cxx17.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/use-braced-initialization-cxx20.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/use-braced-initialization-cxx23.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/use-braced-initialization.cpp
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
index 402579adfb5d3..d1a21a10d740c 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
@@ -12,6 +12,7 @@
#include "../misc/ExplicitConstructorCheck.h"
#include "../misc/NonPrivateMemberVariablesInClassesCheck.h"
#include "../misc/UnconventionalAssignOperatorCheck.h"
+#include "../misc/UseBracedInitializationCheck.h"
#include "../modernize/AvoidCArraysCheck.h"
#include "../modernize/MacroToEnumCheck.h"
#include "../modernize/UseDefaultMemberInitCheck.h"
@@ -136,6 +137,8 @@ class CppCoreGuidelinesModule : public ClangTidyModule {
CheckFactories.registerCheck<SpecialMemberFunctionsCheck>(
"cppcoreguidelines-special-member-functions");
CheckFactories.registerCheck<SlicingCheck>("cppcoreguidelines-slicing");
+ CheckFactories.registerCheck<misc::UseBracedInitializationCheck>(
+ "cppcoreguidelines-use-braced-initialization");
CheckFactories.registerCheck<modernize::UseDefaultMemberInitCheck>(
"cppcoreguidelines-use-default-member-init");
CheckFactories.registerCheck<UseEnumClassCheck>(
diff --git a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt
index 83a23b65f86db..8d65c551e42c9 100644
--- a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt
@@ -46,6 +46,7 @@ add_clang_library(clangTidyMiscModule STATIC
UnusedParametersCheck.cpp
UnusedUsingDeclsCheck.cpp
UseAnonymousNamespaceCheck.cpp
+ UseBracedInitializationCheck.cpp
UseInternalLinkageCheck.cpp
LINK_LIBS
diff --git a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
index 5a716606495db..e99104fd39669 100644
--- a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
@@ -35,6 +35,7 @@
#include "UnusedParametersCheck.h"
#include "UnusedUsingDeclsCheck.h"
#include "UseAnonymousNamespaceCheck.h"
+#include "UseBracedInitializationCheck.h"
#include "UseInternalLinkageCheck.h"
namespace clang::tidy {
@@ -93,6 +94,8 @@ class MiscModule : public ClangTidyModule {
"misc-unused-using-decls");
CheckFactories.registerCheck<UseAnonymousNamespaceCheck>(
"misc-use-anonymous-namespace");
+ CheckFactories.registerCheck<UseBracedInitializationCheck>(
+ "misc-use-braced-initialization");
CheckFactories.registerCheck<UseInternalLinkageCheck>(
"misc-use-internal-linkage");
}
diff --git a/clang-tools-extra/clang-tidy/misc/UseBracedInitializationCheck.cpp b/clang-tools-extra/clang-tidy/misc/UseBracedInitializationCheck.cpp
new file mode 100644
index 0000000000000..3e8f72e7e7983
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/misc/UseBracedInitializationCheck.cpp
@@ -0,0 +1,431 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "UseBracedInitializationCheck.h"
+#include "../utils/LexerUtils.h"
+#include "../utils/NarrowingConversions.h"
+#include "clang/AST/ASTContext.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::misc {
+
+/// Returns \c true if \p From may be implicitly converted to \p To.
+static bool mayConvertImplicitly(QualType From, QualType To) {
+ From = From.getNonReferenceType().getCanonicalType();
+ To = To.getNonReferenceType().getCanonicalType();
+ if (From == To)
+ return true;
+
+ if ((From->isPointerType() || From->isArrayType()) &&
+ To->isArithmeticType() && !To->isBooleanType())
+ return false;
+
+ if (const auto *FromEnum = From->getAs<EnumType>())
+ if (FromEnum->getDecl()->isScoped())
+ return false;
+
+ if (const auto *FromRecord = From->getAsCXXRecordDecl();
+ FromRecord && !To->isRecordType()) {
+ if (!FromRecord->hasDefinition())
+ return true;
+ return !FromRecord->getVisibleConversionFunctions().empty();
+ }
+
+ return true;
+}
+
+static SmallVector<const Expr *>
+collectExplicitArgs(const CXXConstructExpr &Ctor) {
+ SmallVector<const Expr *> ExplicitArgs;
+ for (unsigned I = 0; I < Ctor.getNumArgs(); ++I)
+ if (!isa<CXXDefaultArgExpr>(Ctor.getArg(I)))
+ ExplicitArgs.push_back(Ctor.getArg(I));
+ return ExplicitArgs;
+}
+
+static bool hasInitListCtor(const CXXRecordDecl *RD,
+ ArrayRef<const Expr *> ExplicitArgs) {
+ if (!RD || !RD->hasDefinition())
+ return false;
+
+ for (const CXXConstructorDecl *CD : RD->ctors()) {
+ if (CD->getNumParams() == 0)
+ continue;
+ const QualType FirstParam =
+ CD->getParamDecl(0)->getType().getNonReferenceType();
+ const auto *Init = FirstParam->getAsCXXRecordDecl();
+ if (!Init || !Init->getDeclName().isIdentifier() ||
+ Init->getName() != "initializer_list" || !Init->isInStdNamespace())
+ continue;
+ // [dcl.init.list] p2: all other params must have defaults.
+ bool OthersDefaulted = true;
+ for (unsigned I = 1; I < CD->getNumParams(); ++I)
+ if (!CD->getParamDecl(I)->hasDefaultArg()) {
+ OthersDefaulted = false;
+ break;
+ }
+ if (!OthersDefaulted)
+ continue;
+ const auto *InitSpec = dyn_cast<ClassTemplateSpecializationDecl>(Init);
+ if (!InitSpec || InitSpec->getTemplateArgs().size() < 1)
+ return true;
+ const QualType InitType = InitSpec->getTemplateArgs()[0].getAsType();
+
+ if (llvm::all_of(ExplicitArgs, [&InitType](const Expr *Arg) {
+ return mayConvertImplicitly(Arg->getType(), InitType);
+ }))
+ return true;
+ }
+
+ return false;
+}
+
+namespace {
+
+AST_MATCHER_P(VarDecl, hasInitStyle, VarDecl::InitializationStyle, Style) {
+ return Node.getInitStyle() == Style;
+}
+
+AST_MATCHER(Type, isDependentType) { return Node.isDependentType(); }
+
+AST_MATCHER(CXXConstructExpr, noMacroParens) {
+ const SourceRange Range = Node.getParenOrBraceRange();
+ return Range.isValid() && !Range.getBegin().isMacroID() &&
+ !Range.getEnd().isMacroID();
+}
+
+AST_MATCHER_P(CXXFunctionalCastExpr, hasSubExpr,
+ ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
+ return InnerMatcher.matches(*Node.getSubExpr(), Finder, Builder);
+}
+
+const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt,
+ CXXParenListInitExpr>
+ CxxParenListInitExpr;
+
+/// Matches 'CXXConstructExpr' whose target class has any constructor taking
+/// 'std::initializer_list<Type>' where all arguments of the current call could
+/// be converted to 'Type'.
+AST_MATCHER(CXXConstructExpr, canOverlapWithInitListCtor) {
+ const CXXRecordDecl *RD = Node.getConstructor()->getParent();
+ assert(RD && "CXXConstructExpr must have a parent CXXRecordDecl");
+ const SmallVector<const Expr *> ExplicitArgs = collectExplicitArgs(Node);
+ return hasInitListCtor(RD, ExplicitArgs);
+}
+
+AST_POLYMORPHIC_MATCHER(isListInit,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(CXXFunctionalCastExpr,
+ CXXConstructExpr)) {
+ return Node.isListInitialization();
+}
+
+AST_MATCHER(CXXNewExpr, isParenInit) {
+ return Node.getInitializationStyle() == CXXNewInitializationStyle::Parens;
+}
+
+AST_MATCHER(CXXNewExpr, allocatesAutoType) {
+ return Node.getAllocatedTypeSourceInfo()->getType()->getContainedAutoType() !=
+ nullptr;
+}
+
+AST_MATCHER(CXXNewExpr, allocatesRecordType) {
+ return Node.getType()->getPointeeType()->isRecordType();
+}
+
+struct ParenRange {
+ SourceLocation DiagLoc;
+ SourceLocation LParen;
+ SourceLocation RParen;
+};
+
+struct NarrowingInfo {
+ SourceLocation Loc;
+ QualType From;
+ QualType To;
+};
+
+} // namespace
+
+static std::optional<NarrowingInfo>
+checkNarrowing(const Expr *Init, QualType TargetType, const ASTContext &Ctx) {
+ const Expr *OrigInit = Init->IgnoreImpCasts();
+ const QualType From = OrigInit->getType();
+ const QualType To = TargetType.getNonReferenceType();
+ if (utils::isNarrowingConversion(From, To, OrigInit, Ctx))
+ return NarrowingInfo{OrigInit->getBeginLoc(), From, To};
+ return std::nullopt;
+}
+
+/// Check whether a scalar initialization expression is narrowing.
+static std::optional<NarrowingInfo> isScalarNarrowing(const Expr *Init,
+ QualType TargetType,
+ const ASTContext &Ctx) {
+ return checkNarrowing(Init, TargetType, Ctx);
+}
+
+/// Returns a NarrowingInfo for every argument of \p Ctor that would narrow
+/// under braced initialization. Empty if no argument narrows.
+static SmallVector<NarrowingInfo> isCtorNarrowing(const CXXConstructExpr *Ctor,
+ const ASTContext &Ctx) {
+ SmallVector<NarrowingInfo> Result;
+ const CXXConstructorDecl *CD = Ctor->getConstructor();
+ for (unsigned I = 0; I < Ctor->getNumArgs(); ++I) {
+ const Expr *Arg = Ctor->getArg(I);
+ if (isa<CXXDefaultArgExpr>(Arg))
+ continue;
+ if (I >= CD->getNumParams())
+ break;
+ if (auto Info = checkNarrowing(Arg, CD->getParamDecl(I)->getType(), Ctx))
+ Result.push_back(*Info);
+ }
+ return Result;
+}
+
+/// Returns a NarrowingInfo for every user-specified initializer in \p PLE
+/// that would narrow. Empty if none narrow.
+static SmallVector<NarrowingInfo>
+isPLENarrowing(const CXXParenListInitExpr *PLE, const ASTContext &Ctx) {
+ SmallVector<NarrowingInfo> Result;
+ for (const Expr *Init : PLE->getUserSpecifiedInitExprs())
+ if (auto Info = checkNarrowing(Init, Init->getType(), Ctx))
+ Result.push_back(*Info);
+ return Result;
+}
+
+static std::optional<ParenRange>
+handleMemInit(const CXXCtorInitializer *MemInit, const CXXConstructExpr *Ctor,
+ const SourceManager &SM, const LangOptions &LangOpts) {
+ if (!Ctor) {
+ // CXXCtorInitializer stores both '(' and '{' locations in the same
+ // fields. Only transform parenthesized initialization.
+ const SourceLocation LParen = MemInit->getLParenLoc();
+ if (!LParen.isValid() || !MemInit->getRParenLoc().isValid())
+ return std::nullopt;
+ Token Tok;
+ if (Lexer::getRawToken(LParen, Tok, SM, LangOpts) ||
+ Tok.isNot(tok::l_paren))
+ return std::nullopt;
+ }
+ return ParenRange{MemInit->getSourceLocation(), MemInit->getLParenLoc(),
+ MemInit->getRParenLoc()};
+}
+
+static std::optional<ParenRange> handleScalarVar(const VarDecl *Var,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ const Expr *Init = Var->getInit();
+ SourceLocation InitBegin = Init->getBeginLoc();
+ SourceLocation InitEnd = Init->getEndLoc();
+ if (isa<DecompositionDecl>(Var))
+ if (const auto *Ctor = dyn_cast<CXXConstructExpr>(Init);
+ Ctor && Ctor->getNumArgs() == 1 && InitBegin == Var->getLocation()) {
+ const Expr *Arg = Ctor->getArg(0);
+ InitBegin = Arg->getBeginLoc();
+ InitEnd = Arg->getEndLoc();
+ }
+ const std::optional<Token> LTok =
+ utils::lexer::findPreviousTokenSkippingComments(InitBegin, SM, LangOpts);
+ if (!LTok || LTok->isNot(tok::l_paren) || LTok->getLocation().isMacroID())
+ return std::nullopt;
+ const std::optional<Token> RTok =
+ utils::lexer::findNextTokenSkippingComments(InitEnd, SM, LangOpts);
+ if (!RTok || RTok->isNot(tok::r_paren) || RTok->getLocation().isMacroID())
+ return std::nullopt;
+ return ParenRange{Var->getLocation(), LTok->getLocation(),
+ RTok->getLocation()};
+}
+
+static std::optional<ParenRange>
+handleScalarCast(const CXXFunctionalCastExpr *Cast) {
+ const SourceLocation LParen = Cast->getLParenLoc();
+ const SourceLocation RParen = Cast->getRParenLoc();
+ if (!LParen.isValid() || !RParen.isValid())
+ return std::nullopt;
+ return ParenRange{Cast->getBeginLoc(), LParen, RParen};
+}
+
+static std::optional<ParenRange> handleScalarNew(const CXXNewExpr *New) {
+ const SourceRange InitRange = New->getDirectInitRange();
+ if (!InitRange.isValid())
+ return std::nullopt;
+ return ParenRange{New->getBeginLoc(), InitRange.getBegin(),
+ InitRange.getEnd()};
+}
+
+static std::optional<ParenRange>
+handlePLE(const CXXParenListInitExpr *PLE,
+ const MatchFinder::MatchResult &Result) {
+ SourceLocation DiagLoc;
+ if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var_ple"))
+ DiagLoc = Var->getLocation();
+ else if (const auto *Cast =
+ Result.Nodes.getNodeAs<CXXFunctionalCastExpr>("cast_ple"))
+ DiagLoc = Cast->getBeginLoc();
+ else if (const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>("new_ple"))
+ DiagLoc = New->getBeginLoc();
+ else
+ llvm_unreachable("No context for CXXParenListInitExpr");
+ return ParenRange{DiagLoc, PLE->getBeginLoc(), PLE->getEndLoc()};
+}
+
+void UseBracedInitializationCheck::registerMatchers(MatchFinder *Finder) {
+ const auto GoodCtor =
+ allOf(noMacroParens(), unless(canOverlapWithInitListCtor()),
+ unless(isListInitialization()));
+ const auto GoodCtorExpr = cxxConstructExpr(GoodCtor).bind("ctor");
+ const auto GoodVar =
+ allOf(unless(hasType(isDependentType())), unless(hasType(autoType())));
+ const auto HasGoodCtorOrIsScalar =
+ anyOf(hasInitializer(ignoringImplicit(GoodCtorExpr)),
+ unless(hasInitializer(ignoringImplicit(cxxConstructExpr()))));
+
+ Finder->addMatcher(varDecl(unless(decompositionDecl()),
+ hasInitStyle(VarDecl::CallInit), GoodVar,
+ HasGoodCtorOrIsScalar)
+ .bind("var"),
+ this);
+ Finder->addMatcher(decompositionDecl(hasInitStyle(VarDecl::CallInit),
+ unless(hasType(isDependentType())))
+ .bind("var"),
+ this);
+ Finder->addMatcher(cxxTemporaryObjectExpr(GoodCtor).bind("ctor"), this);
+ Finder->addMatcher(
+ traverse(
+ TK_AsIs,
+ cxxFunctionalCastExpr(
+ unless(hasType(autoType())), unless(hasType(isDependentType())),
+ unless(isInTemplateInstantiation()), unless(isListInit()),
+ anyOf(allOf(hasType(hasUnqualifiedDesugaredType(recordType())),
+ hasSubExpr(ignoringImplicit(cxxConstructExpr(
+ unless(isListInit()),
+ unless(canOverlapWithInitListCtor()))))),
+ unless(hasType(hasUnqualifiedDesugaredType(recordType())))))
+ .bind("func_cast")),
+ this);
+ Finder->addMatcher(
+ cxxNewExpr(unless(hasType(isDependentType())),
+ anyOf(has(ignoringImplicit(GoodCtorExpr)),
+ allOf(unless(allocatesRecordType()), isParenInit(),
+ unless(allocatesAutoType()))))
+ .bind("new_expr"),
+ this);
+ Finder->addMatcher(
+ cxxCtorInitializer(
+ isWritten(),
+ anyOf(withInitializer(ignoringImplicit(GoodCtorExpr)),
+ unless(withInitializer(ignoringImplicit(cxxConstructExpr())))))
+ .bind("ctor_init"),
+ this);
+
+ if (getLangOpts().CPlusPlus20) {
+ auto GoodPLE = CxxParenListInitExpr().bind("ple");
+ Finder->addMatcher(varDecl(hasInitStyle(VarDecl::ParenListInit),
+ hasInitializer(GoodPLE), GoodVar)
+ .bind("var_ple"),
+ this);
+ Finder->addMatcher(cxxFunctionalCastExpr(has(GoodPLE)).bind("cast_ple"),
+ this);
+ Finder->addMatcher(cxxNewExpr(has(GoodPLE)).bind("new_ple"), this);
+ }
+}
+
+namespace {
+struct MatchAnalysis {
+ std::optional<ParenRange> Range;
+ SmallVector<NarrowingInfo> Narrowings;
+};
+} // namespace
+
+static MatchAnalysis analyzeMatch(const MatchFinder::MatchResult &Result,
+ const ASTContext &Ctx,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ const auto ScalarNarrowing =
+ [&Ctx](const Expr *Init, QualType Target) -> SmallVector<NarrowingInfo> {
+ if (!Init)
+ return {};
+ if (auto Info = isScalarNarrowing(Init, Target, Ctx))
+ return {*Info};
+ return {};
+ };
+
+ MatchAnalysis Res;
+ if (const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructExpr>("ctor")) {
+ // A 'ctor' binding can be standalone or nested inside Var/New/MemInit;
+ // it always carries the parens and arguments we need.
+ const auto *CtorInit =
+ Result.Nodes.getNodeAs<CXXCtorInitializer>("ctor_init");
+ Res.Range =
+ CtorInit
+ ? handleMemInit(CtorInit, Ctor, SM, LangOpts)
+ : std::optional<ParenRange>(ParenRange{
+ Ctor->getBeginLoc(), Ctor->getParenOrBraceRange().getBegin(),
+ Ctor->getParenOrBraceRange().getEnd()});
+ Res.Narrowings = isCtorNarrowing(Ctor, Ctx);
+ } else if (const auto *PLE =
+ Result.Nodes.getNodeAs<CXXParenListInitExpr>("ple")) {
+ Res.Range = handlePLE(PLE, Result);
+ Res.Narrowings = isPLENarrowing(PLE, Ctx);
+ } else if (const auto *CtorInit =
+ Result.Nodes.getNodeAs<CXXCtorInitializer>("ctor_init")) {
+ Res.Range = handleMemInit(CtorInit, /*Ctor=*/nullptr, SM, LangOpts);
+ if (CtorInit->isMemberInitializer())
+ Res.Narrowings = ScalarNarrowing(CtorInit->getInit(),
+ CtorInit->getMember()->getType());
+ } else if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var")) {
+ Res.Range = handleScalarVar(Var, SM, LangOpts);
+ Res.Narrowings = ScalarNarrowing(Var->getInit(), Var->getType());
+ } else if (const auto *Cast =
+ Result.Nodes.getNodeAs<CXXFunctionalCastExpr>("func_cast")) {
+ Res.Range = handleScalarCast(Cast);
+ Res.Narrowings = ScalarNarrowing(Cast->getSubExpr(), Cast->getType());
+ } else if (const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>("new_expr")) {
+ Res.Range = handleScalarNew(New);
+ Res.Narrowings =
+ ScalarNarrowing(New->getInitializer(), New->getAllocatedType());
+ } else {
+ llvm_unreachable("No matches found");
+ }
+ return Res;
+}
+
+void UseBracedInitializationCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const SourceManager &SM = *Result.SourceManager;
+ const LangOptions &LangOpts = Result.Context->getLangOpts();
+ const ASTContext &Ctx = *Result.Context;
+
+ const auto [Range, Narrowings] = analyzeMatch(Result, Ctx, SM, LangOpts);
+
+ if (!Range || Range->LParen.isMacroID() || Range->RParen.isMacroID())
+ return;
+
+ if (Narrowings.empty()) {
+ diag(Range->DiagLoc, "use braced initialization instead of parenthesized "
+ "initialization")
+ << FixItHint::CreateReplacement(Range->LParen, "{")
+ << FixItHint::CreateReplacement(Range->RParen, "}");
+ return;
+ }
+
+ diag(Range->DiagLoc, "use braced initialization instead of parenthesized "
+ "initialization");
+ for (const auto &[I, N] : llvm::enumerate(Narrowings)) {
+ auto Note = diag(N.Loc,
+ "narrowing conversion from %0 to %1 will be ill-formed in "
+ "braced initialization",
+ DiagnosticIDs::Note);
+ Note << N.From << N.To;
+ if (I == 0)
+ Note << FixItHint::CreateReplacement(Range->LParen, "{")
+ << FixItHint::CreateReplacement(Range->RParen, "}");
+ }
+}
+
+} // namespace clang::tidy::misc
diff --git a/clang-tools-extra/clang-tidy/misc/UseBracedInitializationCheck.h b/clang-tools-extra/clang-tidy/misc/UseBracedInitializationCheck.h
new file mode 100644
index 0000000000000..143658df58edc
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/misc/UseBracedInitializationCheck.h
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_MISC_USEBRACEDINITIALIZATIONCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEBRACEDINITIALIZATIONCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::misc {
+
+/// Suggests replacing parenthesized initialization with braced
+/// initialization.
+///
+/// For the user-facing documentation see:
+/// https://clang.llvm.org/extra/clang-tidy/checks/misc/use-braced-initialization.html
+class UseBracedInitializationCheck : public ClangTidyCheck {
+public:
+ UseBracedInitializationCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus11;
+ }
+};
+
+} // namespace clang::tidy::misc
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEBRACEDINITIALIZATIONCHECK_H
diff --git a/clang-tools-extra/clang-tidy/utils/CMakeLists.txt b/clang-tools-extra/clang-tidy/utils/CMakeLists.txt
index b83a1e9a77182..02201b6eb83b0 100644
--- a/clang-tools-extra/clang-tidy/utils/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/utils/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangTidyUtils STATIC
LexerUtils.cpp
Matchers.cpp
NamespaceAliaser.cpp
+ NarrowingConversions.cpp
OptionsUtils.cpp
RenamerClangTidyCheck.cpp
TransformerClangTidyCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/utils/LexerUtils.h b/clang-tools-extra/clang-tidy/utils/LexerUtils.h
index 9b2daf74965e4..98858f105c191 100644
--- a/clang-tools-extra/clang-tidy/utils/LexerUtils.h
+++ b/clang-tools-extra/clang-tidy/utils/LexerUtils.h
@@ -94,6 +94,13 @@ SourceLocation findNextAnyTokenKind(SourceLocation Start,
}
}
+/// Finds previous token that's not a comment.
+inline std::optional<Token>
+findPreviousTokenSkippingComments(SourceLocation Start, const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ return Lexer::findPreviousToken(Start, SM, LangOpts, false);
+}
+
// Finds next token, possibly a comment.
inline std::optional<Token>
findNextTokenIncludingComments(SourceLocation Start, const SourceManager &SM,
diff --git a/clang-tools-extra/clang-tidy/utils/NarrowingConversions.cpp b/clang-tools-extra/clang-tidy/utils/NarrowingConversions.cpp
new file mode 100644
index 0000000000000..a47c2c1d745fa
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/utils/NarrowingConversions.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "NarrowingConversions.h"
+
+namespace clang::tidy::utils {
+
+// [dcl.init.list]/p7.2: long double -> double or float, or double -> float;
+// unless the source is a constant expression whose value fits in the target
+// representation range.
+static bool isFloatToFloatNarrowing(QualType From, QualType To,
+ const Expr *Init, const ASTContext &Ctx) {
+ if (Ctx.getFloatingTypeOrder(From, To) <= 0)
+ return false;
+ if (Init->isValueDependent())
+ return true;
+ Expr::EvalResult EvalResult;
+ if (!Init->EvaluateAsRValue(EvalResult, Ctx) || !EvalResult.Val.isFloat())
+ return true;
+ llvm::APFloat Value = EvalResult.Val.getFloat();
+ bool LosesInfo = false;
+ const llvm::APFloat::opStatus Status =
+ Value.convert(Ctx.getFloatTypeSemantics(To),
+ llvm::APFloat::rmNearestTiesToEven, &LosesInfo);
+ return (Status & llvm::APFloat::opOverflow) != 0;
+}
+
+// [dcl.init.list]/p7.3: integer or unscoped enum to floating-point; unless
+// the source is a constant expression that fits and round-trips exactly.
+static bool isIntToFloatNarrowing(const Expr *Init, QualType To,
+ const ASTContext &Ctx) {
+ if (Init->isValueDependent())
+ return true;
+ std::optional<llvm::APSInt> OptVal = Init->getIntegerConstantExpr(Ctx);
+ if (!OptVal)
+ return true;
+ llvm::APFloat FPVal(Ctx.getFloatTypeSemantics(To));
+ const llvm::APFloat::opStatus Status = FPVal.convertFromAPInt(
+ *OptVal, OptVal->isSigned(), llvm::APFloat::rmNearestTiesToEven);
+ if (Status != llvm::APFloat::opOK)
+ return true;
+ llvm::APSInt RoundTrip(OptVal->getBitWidth(), !OptVal->isSigned());
+ bool IsExact = false;
+ FPVal.convertToInteger(RoundTrip, llvm::APFloat::rmTowardZero, &IsExact);
+ return !IsExact || RoundTrip != *OptVal;
+}
+
+// [dcl.init.list]/p7.4: integer or unscoped enum to integer that cannot
+// represent all values; unless source is a constant expression that fits.
+static bool isIntToIntNarrowing(QualType From, QualType To, const Expr *Init,
+ const ASTContext &Ctx) {
+ const bool FromSigned = From->isSignedIntegerOrEnumerationType();
+ const unsigned FromWidth = Ctx.getIntWidth(From);
+ const bool ToSigned = To->isSignedIntegerOrEnumerationType();
+ const unsigned ToWidth = Ctx.getIntWidth(To);
+
+ if ((FromWidth < ToWidth + (FromSigned == ToSigned)) &&
+ !(FromSigned && !ToSigned))
+ return false;
+
+ if (Init->isValueDependent())
+ return true;
+ std::optional<llvm::APSInt> OptVal = Init->getIntegerConstantExpr(Ctx);
+ if (!OptVal)
+ return true;
+ const llvm::APSInt Val = OptVal->extend(OptVal->getBitWidth() + 1);
+ llvm::APSInt Converted = Val;
+ Converted = Converted.trunc(ToWidth);
+ Converted.setIsSigned(ToSigned);
+ Converted = Converted.extend(Val.getBitWidth());
+ Converted.setIsSigned(Val.isSigned());
+ return Converted != Val;
+}
+
+bool isNarrowingConversion(QualType From, QualType To, const Expr *Init,
+ const ASTContext &Ctx) {
+ From = From.getCanonicalType().getUnqualifiedType();
+ To = To.getCanonicalType().getUnqualifiedType();
+ if (From == To)
+ return false;
+
+ if (To->isBooleanType() &&
+ (From->isPointerType() || From->isMemberPointerType()))
+ return true;
+
+ if (From->isRealFloatingType() && To->isIntegralOrEnumerationType())
+ return true;
+
+ if (From->isRealFloatingType() && To->isRealFloatingType())
+ return isFloatToFloatNarrowing(From, To, Init, Ctx);
+
+ if (From->isIntegralOrUnscopedEnumerationType() && To->isRealFloatingType())
+ return isIntToFloatNarrowing(Init, To, Ctx);
+
+ if (From->isIntegralOrUnscopedEnumerationType() &&
+ To->isIntegralOrUnscopedEnumerationType())
+ return isIntToIntNarrowing(From, To, Init, Ctx);
+
+ return false;
+}
+
+} // namespace clang::tidy::utils
diff --git a/clang-tools-extra/clang-tidy/utils/NarrowingConversions.h b/clang-tools-extra/clang-tidy/utils/NarrowingConversions.h
new file mode 100644
index 0000000000000..6fac569da4398
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/utils/NarrowingConversions.h
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_UTILS_NARROWINGCONVERSIONS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_NARROWINGCONVERSIONS_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ExprCXX.h"
+
+namespace clang::tidy::utils {
+
+/// Returns \c true if converting \p Init from \p From to \p To is narrowing
+bool isNarrowingConversion(QualType From, QualType To, const Expr *Init,
+ const ASTContext &Ctx);
+
+} // namespace clang::tidy::utils
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_NARROWINGCONVERSIONS_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index c5a6857c7077f..eb0278f8f1e48 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -181,6 +181,12 @@ New checks
``llvm::to_vector(llvm::make_filter_range(...))`` that can be replaced with
``llvm::map_to_vector`` and ``llvm::filter_to_vector``.
+- New :doc:`misc-use-braced-initialization
+ <clang-tidy/checks/misc/use-braced-initialization>` check.
+
+ Suggests replacing parenthesized initialization with braced
+ initialization.
+
- New :doc:`modernize-use-std-bit
<clang-tidy/checks/modernize/use-std-bit>` check.
@@ -241,6 +247,11 @@ New check aliases
<clang-tidy/checks/misc/explicit-constructor>`. The
`cppcoreguidelines-explicit-constructor` name is kept as an alias.
+- New :doc:`cppcoreguidelines-use-braced-initialization
+ <clang-tidy/checks/cppcoreguidelines/use-braced-initialization>` check
+ alias for :doc:`misc-use-braced-initialization
+ <clang-tidy/checks/misc/use-braced-initialization>`.
+
- Renamed :doc:`google-explicit-constructor
<clang-tidy/checks/google/explicit-constructor>`
to :doc:`misc-explicit-constructor
diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/use-braced-initialization.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/use-braced-initialization.rst
new file mode 100644
index 0000000000000..bc7b9c3084557
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/use-braced-initialization.rst
@@ -0,0 +1,15 @@
+.. title:: clang-tidy - cppcoreguidelines-use-braced-initialization
+.. meta::
+ :http-equiv=refresh: 5;URL=../misc/use-braced-initialization.html
+
+cppcoreguidelines-use-braced-initialization
+===========================================
+
+This check implements `ES.23
+<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es23-prefer-the--initializer-syntax>`_
+from the C++ Core Guidelines.
+
+The `cppcoreguidelines-use-braced-initialization` check is an alias,
+please see
+:doc:`misc-use-braced-initialization <../misc/use-braced-initialization>`
+for more information.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index f193c0920ec1b..15c61f903a8e5 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -286,6 +286,7 @@ Clang-Tidy Checks
:doc:`misc-unused-parameters <misc/unused-parameters>`, "Yes"
:doc:`misc-unused-using-decls <misc/unused-using-decls>`, "Yes"
:doc:`misc-use-anonymous-namespace <misc/use-anonymous-namespace>`,
+ :doc:`misc-use-braced-initialization <misc/use-braced-initialization>`, "Yes"
:doc:`misc-use-internal-linkage <misc/use-internal-linkage>`, "Yes"
:doc:`modernize-avoid-bind <modernize/avoid-bind>`, "Yes"
:doc:`modernize-avoid-c-arrays <modernize/avoid-c-arrays>`,
@@ -594,6 +595,7 @@ Check aliases
:doc:`cppcoreguidelines-noexcept-move-operations <cppcoreguidelines/noexcept-move-operations>`, :doc:`performance-noexcept-move-constructor <performance/noexcept-move-constructor>`, "Yes"
:doc:`cppcoreguidelines-noexcept-swap <cppcoreguidelines/noexcept-swap>`, :doc:`performance-noexcept-swap <performance/noexcept-swap>`, "Yes"
:doc:`cppcoreguidelines-non-private-member-variables-in-classes <cppcoreguidelines/non-private-member-variables-in-classes>`, :doc:`misc-non-private-member-variables-in-classes <misc/non-private-member-variables-in-classes>`,
+ :doc:`cppcoreguidelines-use-braced-initialization <cppcoreguidelines/use-braced-initialization>`, :doc:`misc-use-braced-initialization <misc/use-braced-initialization>`, "Yes"
:doc:`cppcoreguidelines-use-default-member-init <cppcoreguidelines/use-default-member-init>`, :doc:`modernize-use-default-member-init <modernize/use-default-member-init>`, "Yes"
:doc:`fuchsia-header-anon-namespaces <fuchsia/header-anon-namespaces>`, :doc:`misc-anonymous-namespace-in-header <misc/anonymous-namespace-in-header>`,
:doc:`fuchsia-multiple-inheritance <fuchsia/multiple-inheritance>`, :doc:`misc-multiple-inheritance <misc/multiple-inheritance>`,
diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/use-braced-initialization.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/use-braced-initialization.rst
new file mode 100644
index 0000000000000..86102f915cd4b
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/misc/use-braced-initialization.rst
@@ -0,0 +1,81 @@
+.. title:: clang-tidy - misc-use-braced-initialization
+
+misc-use-braced-initialization
+==============================
+
+Suggests replacing parenthesized initialization with braced initialization.
+
+Braced initialization has several advantages:
+
+- **Catches narrowing conversions.** ``int x{3.14}`` is a compile-time error,
+ while ``int x(3.14)`` silently truncates.
+- **Uniform syntax.** Braces work consistently for aggregates, containers, and
+ constructors, giving a single initialization style across all types.
+
+For example:
+
+.. code-block:: c++
+
+ struct Matrix {
+ Matrix(int rows, int cols);
+ };
+
+ // Variable declarations:
+ Matrix m(3, 4); // -> Matrix m{3, 4};
+ int n(42); // -> int n{42};
+
+ // Copy initialization:
+ Matrix m = Matrix(3, 4); // -> Matrix m = Matrix{3, 4};
+
+ // Temporary objects:
+ use(Matrix(3, 4)); // -> use(Matrix{3, 4});
+
+ // New expressions:
+ auto *p = new Matrix(3, 4); // -> auto *p = new Matrix{3, 4};
+
+ // Member initializer lists:
+ struct Widget : Matrix {
+ int value;
+ Widget() : Matrix(3, 4), value(0) {}
+ // -> Widget() : Matrix{3, 4}, value{0} {}
+ };
+
+The check skips cases where changing from ``()`` to ``{}`` would alter program
+semantics:
+
+- Constructor calls where a ``std::initializer_list`` overload could be
+ preferred under braced initialization and all arguments are convertible
+ to the list's element type, which would silently change semantics.
+ For example, ``std::vector<int> v(5, 1)`` is skipped because
+ ``std::vector<int> v{5, 1}`` would create a two-element vector instead
+ of five ones, but ``std::string s("hello")`` is diagnosed because
+ ``const char*`` cannot implicitly convert to ``char``.
+- Direct-initialized ``auto`` variables, where deduction rules may differ
+ between C++ standards.
+- Expressions in macro expansions.
+
+Limitations
+-----------
+
+Braced initialization prohibits implicit narrowing conversions. When
+the check detects that changing ``()`` to ``{}`` would introduce a
+narrowing conversion, it emits the warning with a note and attaches
+the brace fix-it to the note rather than the warning. The fix is not
+applied by default, but can be opted into with ``--fix-notes``.
+
+.. code-block:: c++
+
+ struct Foo {
+ Foo(short n);
+ };
+
+ int n = 10;
+ Foo f(n); // warning + note: narrowing from 'int' to 'short'
+ // fix-it on the note, applied only with --fix-notes
+
+References
+----------
+
+This check corresponds to the C++ Core Guidelines rule
+`ES.23
+<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es23-prefer-the--initializer-syntax>`_.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/string b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/string
index dbebeaaa46514..399fd0116f318 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/string
+++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/string
@@ -4,6 +4,7 @@
// For size_t
#include "string.h"
#include "memory"
+#include "initializer_list"
typedef unsigned __INT16_TYPE__ char16;
typedef unsigned __INT32_TYPE__ char32;
@@ -26,6 +27,7 @@ struct basic_string {
basic_string(const C *p, size_type count);
basic_string(const C *b, const C *e);
basic_string(size_t, C);
+ basic_string(std::initializer_list<C>);
operator basic_string_view<C, T>() const;
~basic_string();
@@ -202,6 +204,7 @@ bool operator!=(const char*, const std::string_view&);
size_t strlen(const char* str);
+#if __cplusplus >= 201402L
inline namespace literals {
inline namespace string_literals {
string operator""s(const char *, size_t);
@@ -210,6 +213,7 @@ inline namespace string_view_literals {
string_view operator""sv(const char *, size_t);
}
}
+#endif // __cplusplus >= 201402L
}
#endif // _STRING_
diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/use-braced-initialization-cxx17.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/use-braced-initialization-cxx17.cpp
new file mode 100644
index 0000000000000..6ba61b416a527
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/misc/use-braced-initialization-cxx17.cpp
@@ -0,0 +1,41 @@
+// RUN: %check_clang_tidy -std=c++17-or-later %s misc-use-braced-initialization %t -- --fix-notes
+
+struct Agg {
+ int a, b;
+};
+
+void structured_binding_paren() {
+ auto [a, b](Agg{1, 2});
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use braced initialization
+ // CHECK-FIXES: auto [a, b]{Agg{1, 2}};
+}
+
+void structured_binding_braced() {
+ auto [a, b] = Agg{1, 2};
+}
+
+void structured_binding_paren_call() {
+ Agg make_agg();
+ auto [a, b](make_agg());
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use braced initialization
+ // CHECK-FIXES: auto [a, b]{make_agg()};
+}
+
+void structured_binding_paren_copy(const Agg &g) {
+ auto [a, b](g);
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use braced initialization
+ // CHECK-FIXES: auto [a, b]{g};
+}
+
+void structured_binding_paren_ref(Agg &g) {
+ auto &[a, b](g);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-FIXES: auto &[a, b]{g};
+}
+
+void structured_binding_paren_array() {
+ int arr[2] = {1, 2};
+ auto [a, b](arr);
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use braced initialization
+ // CHECK-FIXES: auto [a, b]{arr};
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/use-braced-initialization-cxx20.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/use-braced-initialization-cxx20.cpp
new file mode 100644
index 0000000000000..897996085ef87
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/misc/use-braced-initialization-cxx20.cpp
@@ -0,0 +1,284 @@
+// RUN: %check_clang_tidy -std=c++20-or-later %s misc-use-braced-initialization %t -- --fix-notes
+
+#include <vector>
+
+struct Agg {
+ int a, b;
+};
+
+struct AggDefault {
+ int a = 0;
+ int b;
+};
+
+struct Nested {
+ Agg x;
+ int y;
+};
+
+struct Takes {
+ Takes(Agg);
+};
+
+struct Simple {
+ Simple(int);
+};
+
+void basic_aggregate() {
+ Agg d(1, 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use braced initialization instead of parenthesized initialization [misc-use-braced-initialization]
+ // CHECK-FIXES: Agg d{1, 2};
+}
+
+void aggregate_default_member() {
+ AggDefault ad(1, 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use braced initialization
+ // CHECK-FIXES: AggDefault ad{1, 2};
+}
+
+void nested_aggregate_braced_inner() {
+ Nested n(Agg{1, 2}, 3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use braced initialization
+ // CHECK-FIXES: Nested n{Agg{1, 2}, 3};
+}
+
+void nested_aggregate_paren_inner() {
+ Nested n(Agg(1, 2), 3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: use braced initialization
+ // CHECK-FIXES: Nested n{Agg{1, 2}, 3};
+}
+
+void aggregate_multi_decl() {
+ Agg a(1, 2), b(3, 4);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: use braced initialization
+ // CHECK-FIXES: Agg a{1, 2}, b{3, 4};
+}
+
+void aggregate_temporary() {
+ Agg(1, 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use braced initialization
+ // CHECK-FIXES: Agg{1, 2};
+}
+
+void aggregate_temporary_cast_to_void() {
+ (void)Agg(1, 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-FIXES: (void)Agg{1, 2};
+}
+
+void aggregate_auto() {
+ auto d = Agg(1, 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use braced initialization
+ // CHECK-FIXES: auto d = Agg{1, 2};
+}
+
+Agg return_aggregate() {
+ return Agg(1, 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use braced initialization
+ // CHECK-FIXES: return Agg{1, 2};
+}
+
+void func_arg(Agg);
+void aggregate_as_argument() {
+ func_arg(Agg(1, 2));
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use braced initialization
+ // CHECK-FIXES: func_arg(Agg{1, 2});
+}
+
+void designated_as_arg() {
+ Takes t({.a = 1, .b = 2});
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-FIXES: Takes t{{[{][{]}}.a = 1, .b = 2{{[}][}]}};
+}
+
+void aggregate_new() {
+ Agg *p = new Agg(1, 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use braced initialization
+ // CHECK-FIXES: Agg *p = new Agg{1, 2};
+ (void)p;
+}
+
+struct L1 {
+ int a, b;
+};
+
+struct L2 {
+ L1 x;
+ int y;
+};
+
+struct L3 {
+ L2 m;
+ int z;
+};
+
+struct L4 {
+ L3 n;
+ int w;
+};
+
+void nested_agg_two_levels() {
+ L2 v(L1(1, 2), 3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: use braced initialization
+ // CHECK-FIXES: L2 v{L1{1, 2}, 3};
+}
+
+void nested_agg_three_levels() {
+ L3 v(L2(L1(1, 2), 3), 4);
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-3]]:11: warning: use braced initialization
+ // CHECK-FIXES: L3 v{L2{L1{1, 2}, 3}, 4};
+}
+
+void nested_agg_four_levels() {
+ L4 v(L3(L2(L1(1, 2), 3), 4), 5);
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-3]]:11: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-4]]:14: warning: use braced initialization
+ // CHECK-FIXES: L4 v{L3{L2{L1{1, 2}, 3}, 4}, 5};
+}
+
+void nested_agg_temporary() {
+ (void)L3(L2(L1(1, 2), 3), 4);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-3]]:15: warning: use braced initialization
+ // CHECK-FIXES: (void)L3{L2{L1{1, 2}, 3}, 4};
+}
+
+void nested_agg_new() {
+ L3 *p = new L3(L2(L1(1, 2), 3), 4);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:18: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-3]]:21: warning: use braced initialization
+ // CHECK-FIXES: L3 *p = new L3{L2{L1{1, 2}, 3}, 4};
+ (void)p;
+}
+
+// Mixed: some levels already braced, only paren levels get fixed.
+void nested_agg_mixed() {
+ L3 v(L2{L1(1, 2), 3}, 4);
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: use braced initialization
+ // CHECK-FIXES: L3 v{L2{L1{1, 2}, 3}, 4};
+}
+
+void nested_agg_mixed_inner_braced() {
+ L3 v(L2(L1{1, 2}, 3), 4);
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: use braced initialization
+ // CHECK-FIXES: L3 v{L2{L1{1, 2}, 3}, 4};
+}
+
+void already_braced() {
+ Agg d{1, 2};
+}
+
+void already_braced_temporary() {
+ Agg{1, 2};
+}
+
+void new_already_braced() {
+ Agg *p = new Agg{1, 2};
+ (void)p;
+}
+
+void copy_init() {
+ Agg d = {1, 2};
+}
+
+void designated_already_braced() {
+ Agg d{.a = 1, .b = 2};
+}
+
+void designated_copy_init() {
+ Agg d = {.a = 1, .b = 2};
+}
+
+struct MemberAgg {
+ Agg a;
+ MemberAgg() : a(1, 2)
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use braced initialization
+ // CHECK-FIXES: MemberAgg() : a{1, 2}
+ {}
+};
+
+struct MemberAggBraced {
+ Agg a;
+ MemberAggBraced() : a{1, 2} {}
+};
+
+// Narrowing conversions in aggregate paren init.
+struct AggFloat {
+ int x;
+ int y;
+};
+
+void narrowing_aggregate() {
+ AggFloat af(1, 3.14);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:18: note: narrowing conversion from 'double' to 'int'
+ // CHECK-FIXES: AggFloat af{1, 3.14};
+}
+
+void narrowing_aggregate_functional_cast() {
+ (void)AggFloat(1, 3.14);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:21: note: narrowing conversion from 'double' to 'int'
+ // CHECK-FIXES: (void)AggFloat{1, 3.14};
+}
+
+void narrowing_aggregate_multiple() {
+ AggFloat af(2.5, 3.14);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:15: note: narrowing conversion from 'double' to 'int'
+ // CHECK-MESSAGES: :[[@LINE-3]]:20: note: narrowing conversion from 'double' to 'int'
+ // CHECK-FIXES: AggFloat af{2.5, 3.14};
+}
+
+void no_narrowing_aggregate() {
+ Agg a(1, 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use braced initialization
+ // CHECK-FIXES: Agg a{1, 2};
+}
+
+void lambda_capture_init() {
+ auto f = [s = Simple(1)](){};
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use braced initialization
+ // CHECK-FIXES: auto f = [s = Simple{1}](){};
+}
+
+void lambda_capture_aggregate() {
+ auto f = [a = Agg(1, 2)](){};
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use braced initialization
+ // CHECK-FIXES: auto f = [a = Agg{1, 2}](){};
+}
+
+void array_paren_init() {
+ int a[3](1, 2, 3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use braced initialization
+ // CHECK-FIXES: int a[3]{1, 2, 3};
+}
+
+void array_paren_init_unsized() {
+ int a[](1, 2, 3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use braced initialization
+ // CHECK-FIXES: int a[]{1, 2, 3};
+}
+
+void range_for_with_init() {
+ int arr[] = {1, 2, 3};
+ for (Simple s(1); auto x : arr) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use braced initialization
+ // CHECK-FIXES: for (Simple s{1}; auto x : arr) {
+ }
+}
+
+void ctad_no_warn() {
+ std::vector v(5, 1);
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/use-braced-initialization-cxx23.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/use-braced-initialization-cxx23.cpp
new file mode 100644
index 0000000000000..18b42e7010b31
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/misc/use-braced-initialization-cxx23.cpp
@@ -0,0 +1,15 @@
+// RUN: %check_clang_tidy -std=c++23-or-later %s misc-use-braced-initialization %t
+
+struct Simple {
+ Simple(int);
+};
+
+void auto_functional_cast() {
+ auto x = auto(1);
+}
+
+void auto_functional_cast_class() {
+ auto x = auto(Simple(1));
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use braced initialization instead of parenthesized initialization [misc-use-braced-initialization]
+ // CHECK-FIXES: auto x = auto(Simple{1});
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/use-braced-initialization.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/use-braced-initialization.cpp
new file mode 100644
index 0000000000000..114696dff1df7
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/misc/use-braced-initialization.cpp
@@ -0,0 +1,977 @@
+// RUN: %check_clang_tidy -std=c++11-or-later %s misc-use-braced-initialization %t -- --fix-notes
+
+#include <string>
+#include <vector>
+
+struct Simple {
+ Simple(int);
+ Simple(int, double);
+ Simple(const Simple &);
+};
+
+struct Explicit {
+ explicit Explicit(int);
+};
+
+struct Aggregate {
+ int a, b;
+};
+
+struct Takes {
+ Takes(Aggregate);
+};
+
+struct TwoAggregates {
+ TwoAggregates(Aggregate, Aggregate);
+};
+
+struct WithDefault {
+ WithDefault(int, int = 0);
+};
+
+struct HasDefault {
+ HasDefault();
+};
+
+struct Outer {
+ struct Inner {
+ Inner(int);
+ };
+};
+
+namespace ns {
+struct Ns {
+ Ns(int);
+};
+} // namespace ns
+
+#define MAKE_SIMPLE(x) Simple w(x)
+#define WRAP_PARENS(x) (x)
+#define TYPE_ALIAS Simple
+
+void basic_single_arg() {
+ Simple w(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use braced initialization instead of parenthesized initialization [misc-use-braced-initialization]
+ // CHECK-FIXES: Simple w{1};
+}
+
+void explicit_ctor() {
+ Explicit e(42);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use braced initialization
+ // CHECK-FIXES: Explicit e{42};
+}
+
+void copy_construction() {
+ Simple w1(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use braced initialization
+ // CHECK-FIXES: Simple w1{1};
+ Simple w2(w1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use braced initialization
+ // CHECK-FIXES: Simple w2{w1};
+}
+
+void static_local() {
+ static Simple sw(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use braced initialization
+ // CHECK-FIXES: static Simple sw{1};
+}
+
+void const_variable() {
+ const Simple cw(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use braced initialization
+ // CHECK-FIXES: const Simple cw{1};
+}
+
+void default_args_ctor() {
+ WithDefault m(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use braced initialization
+ // CHECK-FIXES: WithDefault m{1};
+}
+
+void nested_type() {
+ Outer::Inner oi(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use braced initialization
+ // CHECK-FIXES: Outer::Inner oi{1};
+}
+
+void namespaced_type() {
+ ns::Ns g(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use braced initialization
+ // CHECK-FIXES: ns::Ns g{1};
+}
+
+void for_loop_init() {
+ for (Simple fw(1);;) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use braced initialization
+ // CHECK-FIXES: for (Simple fw{1};;) {
+ }
+}
+
+void if_init_statement() {
+ if (Simple s(1); true) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use braced initialization
+ // CHECK-FIXES: if (Simple s{1}; true) {
+ }
+}
+
+void switch_init_statement(int x) {
+ switch (Simple s(x); x) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use braced initialization
+ // CHECK-FIXES: switch (Simple s{x}; x) {
+ }
+}
+
+void ternary_arg(bool c) {
+ Simple s(c ? 1 : 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use braced initialization
+ // CHECK-FIXES: Simple s{c ? 1 : 2};
+}
+
+void multi_decl_class() {
+ Simple a(1), b(2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: use braced initialization
+ // CHECK-FIXES: Simple a{1}, b{2};
+}
+
+Simple global_simple(1);
+// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use braced initialization
+// CHECK-FIXES: Simple global_simple{1};
+
+namespace ns_scope {
+Simple ns_var(2);
+// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use braced initialization
+// CHECK-FIXES: Simple ns_var{2};
+} // namespace ns_scope
+
+// Macro wraps only the type name; parens are in user code, safe to fix.
+void macro_type_only() {
+ TYPE_ALIAS w(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use braced initialization
+ // CHECK-FIXES: TYPE_ALIAS w{1};
+}
+
+void scalar_int() {
+ int x(42);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use braced initialization
+ // CHECK-FIXES: int x{42};
+}
+
+void scalar_double() {
+ double d(3.14);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use braced initialization
+ // CHECK-FIXES: double d{3.14};
+}
+
+void scalar_expression(int a) {
+ int y(a + 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use braced initialization
+ // CHECK-FIXES: int y{a + 1};
+}
+
+void scalar_pointer() {
+ int *p(nullptr);
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use braced initialization
+ // CHECK-FIXES: int *p{nullptr};
+}
+
+void multi_decl_scalar() {
+ int a(1), b(2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:13: warning: use braced initialization
+ // CHECK-FIXES: int a{1}, b{2};
+}
+
+void temporary_single_arg() {
+ Simple(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use braced initialization
+ // CHECK-FIXES: Simple{1};
+}
+
+void temporary_multi_arg() {
+ Simple(1, 2.0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use braced initialization
+ // CHECK-FIXES: Simple{1, 2.0};
+}
+
+void copy_init_rhs() {
+ Simple w = Simple(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use braced initialization
+ // CHECK-FIXES: Simple w = Simple{1};
+}
+
+void auto_copy_init() {
+ auto w = Simple(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use braced initialization
+ // CHECK-FIXES: auto w = Simple{1};
+}
+
+Simple return_simple() {
+ return Simple(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use braced initialization
+ // CHECK-FIXES: return Simple{1};
+}
+
+void func_arg(Simple);
+void simple_as_argument() {
+ func_arg(Simple(1));
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use braced initialization
+ // CHECK-FIXES: func_arg(Simple{1});
+}
+
+void new_multi_arg() {
+ Simple *p = new Simple(1, 2.0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use braced initialization
+ // CHECK-FIXES: Simple *p = new Simple{1, 2.0};
+}
+
+void braced_arg() {
+ Takes tp({1, 2});
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-FIXES: Takes tp{{[{][{]}}1, 2{{[}][}]}};
+}
+
+void braced_constructed_arg() {
+ Takes tp(Aggregate{1, 2});
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-FIXES: Takes tp{Aggregate{1, 2}};
+}
+
+void multiple_braced_args() {
+ TwoAggregates t({1, 2}, {3, 4});
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use braced initialization
+ // CHECK-FIXES: TwoAggregates t{{[{][{]}}1, 2}, {3, 4{{[}][}]}};
+}
+
+void temporary_braced_arg() {
+ (void)Takes({1, 2});
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-FIXES: (void)Takes{{[{][{]}}1, 2{{[}][}]}};
+}
+
+void new_braced_arg() {
+ Takes *p = new Takes({1, 2});
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use braced initialization
+ // CHECK-FIXES: Takes *p = new Takes{{[{][{]}}1, 2{{[}][}]}};
+}
+
+void class_comment_inside_parens() {
+ Simple w(/*comment*/ 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use braced initialization
+ // CHECK-FIXES: Simple w{/*comment*/ 1};
+}
+
+void scalar_comment_before_parens() {
+ int x /*comment*/ (42);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use braced initialization
+ // CHECK-FIXES: int x /*comment*/ {42};
+}
+
+void scalar_comment_inside_parens() {
+ int x(/*comment*/ 42);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use braced initialization
+ // CHECK-FIXES: int x{/*comment*/ 42};
+}
+
+void scalar_comment_after_init() {
+ int x(42 /*comment*/);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use braced initialization
+ // CHECK-FIXES: int x{42 /*comment*/};
+}
+
+struct L1 {
+ int a, b;
+ L1(int, int);
+};
+
+struct L2 {
+ L1 x;
+ int y;
+ L2(L1, int);
+};
+
+struct L3 {
+ L2 m;
+ int z;
+ L3(L2, int);
+};
+
+struct L4 {
+ L3 n;
+ int w;
+ L4(L3, int);
+};
+
+void nested_ctors_two_levels() {
+ L2 v(L1(1, 2), 3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: use braced initialization
+ // CHECK-FIXES: L2 v{L1{1, 2}, 3};
+}
+
+void nested_ctors_three_levels() {
+ L3 v(L2(L1(1, 2), 3), 4);
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-3]]:11: warning: use braced initialization
+ // CHECK-FIXES: L3 v{L2{L1{1, 2}, 3}, 4};
+}
+
+void nested_ctors_four_levels() {
+ L4 v(L3(L2(L1(1, 2), 3), 4), 5);
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-3]]:11: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-4]]:14: warning: use braced initialization
+ // CHECK-FIXES: L4 v{L3{L2{L1{1, 2}, 3}, 4}, 5};
+}
+
+void nested_ctors_temporary() {
+ (void)L3(L2(L1(1, 2), 3), 4);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-3]]:15: warning: use braced initialization
+ // CHECK-FIXES: (void)L3{L2{L1{1, 2}, 3}, 4};
+}
+
+void nested_ctors_new() {
+ L3 *p = new L3(L2(L1(1, 2), 3), 4);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:18: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-3]]:21: warning: use braced initialization
+ // CHECK-FIXES: L3 *p = new L3{L2{L1{1, 2}, 3}, 4};
+}
+
+// Mixed: some levels already braced, only paren levels get fixed.
+void nested_ctors_mixed() {
+ L3 v(L2{L1(1, 2), 3}, 4);
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: use braced initialization
+ // CHECK-FIXES: L3 v{L2{L1{1, 2}, 3}, 4};
+}
+
+void nested_ctors_mixed_inner_braced() {
+ L3 v(L2(L1{1, 2}, 3), 4);
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: use braced initialization
+ // CHECK-FIXES: L3 v{L2{L1{1, 2}, 3}, 4};
+}
+
+void already_braced() {
+ Simple w{1};
+}
+
+void already_braced_temporary() {
+ Simple{1};
+}
+
+void new_already_braced() {
+ Simple *p = new Simple{1};
+ (void)p;
+}
+
+void scalar_already_braced() {
+ int x{42};
+}
+
+void direct_auto() {
+ auto w(1);
+}
+
+void scalar_auto() {
+ auto x(42);
+}
+
+void scalar_copy_init() {
+ int x = 42;
+}
+
+void default_construction() {
+ HasDefault d;
+}
+
+void macro_full_decl() {
+ MAKE_SIMPLE(1);
+}
+
+void macro_wraps_parens() {
+ Simple w WRAP_PARENS(1);
+}
+
+template <typename T>
+void template_dependent() {
+ T t(1);
+}
+
+template <typename T>
+void template_instantiated(int x) {
+ T t(x);
+}
+
+template <typename T>
+void template_instantiated2(T x) {
+ auto t(x);
+}
+
+template <typename T>
+void template_temporary_single() {
+ (void)T(1);
+}
+
+template <typename T>
+void template_temporary_multi() {
+ (void)T(1, 2.0);
+}
+
+template <typename T>
+T template_return() {
+ return T(1);
+}
+
+template <typename T>
+T *template_new_expr() {
+ return new T(1);
+}
+
+template <typename T, typename... Args>
+void template_variadic(Args... args) {
+ T t(args...);
+}
+
+void force_instantiation() {
+ template_instantiated<Simple>(1);
+ template_instantiated2<Simple>(1);
+ template_temporary_single<Simple>();
+ template_temporary_multi<Simple>();
+ (void)template_return<Simple>();
+ delete template_new_expr<Simple>();
+ template_variadic<Simple>(1);
+}
+
+struct InitListByValue {
+ InitListByValue(std::initializer_list<int>);
+ InitListByValue(int);
+ InitListByValue(int, int);
+};
+
+void il_by_value() {
+ InitListByValue x(1);
+}
+
+void il_by_value_multi() {
+ InitListByValue x(1, 2);
+}
+
+struct InitListConstRef {
+ InitListConstRef(const std::initializer_list<int> &);
+ InitListConstRef(int);
+};
+
+void il_const_ref() {
+ InitListConstRef x(1);
+}
+
+struct InitListConstVolatileRef {
+ InitListConstVolatileRef(const volatile std::initializer_list<int> &);
+ InitListConstVolatileRef(int);
+};
+
+void il_const_volatile_ref() {
+ InitListConstVolatileRef x(1);
+}
+
+struct InitListDefaults {
+ InitListDefaults(std::initializer_list<int>, int = 0, double = 1.0);
+ InitListDefaults(int);
+};
+
+void il_other_params_defaulted() {
+ InitListDefaults x(1);
+}
+
+void il_std_string() {
+ std::string s("hello");
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use braced initialization
+ // CHECK-FIXES: std::string s{"hello"};
+}
+
+void il_std_string_count_char() {
+ std::string s(3, 'a');
+}
+
+void il_std_vector() {
+ std::vector<int> v(5);
+}
+
+void il_std_vector_count_value() {
+ std::vector<int> v(5, 1);
+}
+
+void il_std_vector_temporary() {
+ std::vector<int>(5);
+}
+
+void il_std_vector_already_braced() {
+ std::vector<int> v{1, 2, 3};
+}
+
+void il_new_std_vector() {
+ std::vector<int> *p = new std::vector<int>(5);
+ (void)p;
+}
+
+void il_braced_arg() {
+ InitListByValue x({1, 2, 3});
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use braced initialization
+ // CHECK-FIXES: InitListByValue x{{[{][{]}}1, 2, 3{{[}][}]}};
+}
+
+struct InitListSecondParam {
+ InitListSecondParam(int, std::initializer_list<int>);
+ InitListSecondParam(int, int);
+};
+
+void il_not_first_param() {
+ InitListSecondParam x(1, 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: use braced initialization
+ // CHECK-FIXES: InitListSecondParam x{1, 2};
+}
+
+struct InitListPointer {
+ InitListPointer(std::initializer_list<int> *);
+ InitListPointer(int);
+};
+
+void il_pointer() {
+ InitListPointer x(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use braced initialization
+ // CHECK-FIXES: InitListPointer x{1};
+}
+
+struct InitListNoDefaults {
+ InitListNoDefaults(std::initializer_list<int>, int);
+ InitListNoDefaults(int);
+};
+
+void il_other_params_no_defaults() {
+ InitListNoDefaults x(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: use braced initialization
+ // CHECK-FIXES: InitListNoDefaults x{1};
+}
+
+struct TemplateCtor {
+ TemplateCtor(int);
+ template <class T> TemplateCtor(T);
+};
+
+void il_template_ctor() {
+ TemplateCtor x(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use braced initialization
+ // CHECK-FIXES: TemplateCtor x{1};
+}
+
+namespace custom {
+template <typename T> class initializer_list {};
+} // namespace custom
+
+struct HasCustomInitList {
+ HasCustomInitList(custom::initializer_list<int>);
+ HasCustomInitList(int);
+};
+
+void il_custom_namespace_not_std() {
+ // custom::initializer_list is not in std, so it doesn't block conversion.
+ HasCustomInitList x(42);
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use braced initialization
+ // CHECK-FIXES: HasCustomInitList x{42};
+}
+
+struct RecordArg {};
+
+struct InitListRecordToNonRecord {
+ InitListRecordToNonRecord(std::initializer_list<int>);
+ InitListRecordToNonRecord(RecordArg);
+};
+
+void il_record_arg_non_record_element() {
+ RecordArg r;
+ InitListRecordToNonRecord x(r);
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use braced initialization
+ // CHECK-FIXES: InitListRecordToNonRecord x{r};
+}
+
+struct InitListPointerArgArithmetic {
+ InitListPointerArgArithmetic(std::initializer_list<int>);
+ InitListPointerArgArithmetic(int *);
+};
+
+void il_pointer_arg_arithmetic_element() {
+ int v = 0;
+ InitListPointerArgArithmetic x(&v);
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: use braced initialization
+ // CHECK-FIXES: InitListPointerArgArithmetic x{&v};
+}
+
+struct InitListPointerArgBool {
+ InitListPointerArgBool(std::initializer_list<bool>);
+ InitListPointerArgBool(int *);
+};
+
+void il_pointer_arg_bool_element() {
+ int v = 0;
+ InitListPointerArgBool x(&v);
+}
+
+struct InitListMixedArgs {
+ InitListMixedArgs(std::initializer_list<int>);
+ InitListMixedArgs(int, RecordArg);
+};
+
+void il_mixed_args_second_no_convert() {
+ RecordArg r;
+ InitListMixedArgs x(1, r);
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use braced initialization
+ // CHECK-FIXES: InitListMixedArgs x{1, r};
+}
+
+enum class ScopedKey { A };
+struct InitListVsScopedEnum {
+ InitListVsScopedEnum(std::initializer_list<int>);
+ InitListVsScopedEnum(ScopedKey);
+};
+void il_scoped_enum_arg() {
+ InitListVsScopedEnum x(ScopedKey::A);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use braced initialization
+ // CHECK-FIXES: InitListVsScopedEnum x{ScopedKey::A};
+}
+
+struct NoConvOps {};
+struct InitListVsNoConvOps {
+ InitListVsNoConvOps(std::initializer_list<int>);
+ InitListVsNoConvOps(NoConvOps);
+};
+void il_noconv_class_arg() {
+ NoConvOps r;
+ InitListVsNoConvOps x(r);
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: use braced initialization
+ // CHECK-FIXES: InitListVsNoConvOps x{r};
+}
+
+struct ConvertsToInt {
+ operator int() const;
+};
+struct InitListVsConvClass {
+ InitListVsConvClass(std::initializer_list<int>);
+ InitListVsConvClass(ConvertsToInt);
+};
+void il_conv_class_arg() {
+ ConvertsToInt c;
+ InitListVsConvClass x(c);
+}
+
+struct OwnerOfSimple {
+ Simple s;
+ OwnerOfSimple() : s(1)
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use braced initialization
+ // CHECK-FIXES: OwnerOfSimple() : s{1}
+ {}
+};
+
+struct OwnerOfScalar {
+ int n;
+ OwnerOfScalar() : n(42)
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use braced initialization
+ // CHECK-FIXES: OwnerOfScalar() : n{42}
+ {}
+};
+
+struct OwnerAlreadyBraced {
+ Simple s;
+ OwnerAlreadyBraced() : s{1} {}
+};
+
+void scalar_functional_cast() {
+ (void)int(42);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-FIXES: (void)int{42};
+}
+
+int scalar_functional_cast_return(int x) {
+ return int(x + 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use braced initialization
+ // CHECK-FIXES: return int{x + 1};
+}
+
+void new_scalar() {
+ int *p = new int(42);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use braced initialization
+ // CHECK-FIXES: int *p = new int{42};
+}
+
+void lambda_body_init() {
+ auto f = [](int x) {
+ Simple s(x);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use braced initialization
+ // CHECK-FIXES: Simple s{x};
+ };
+}
+
+// Type aliases.
+typedef Simple SimpleTypedef;
+using SimpleUsing = Simple;
+
+void type_alias_typedef() {
+ SimpleTypedef s(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use braced initialization
+ // CHECK-FIXES: SimpleTypedef s{1};
+}
+
+void type_alias_using() {
+ SimpleUsing s(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use braced initialization
+ // CHECK-FIXES: SimpleUsing s{1};
+}
+
+// Constexpr variables.
+void constexpr_init() {
+ constexpr int x(42);
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use braced initialization
+ // CHECK-FIXES: constexpr int x{42};
+}
+
+// Narrowing conversions: warning emitted but no fix-it, with note.
+struct NarrowingTarget {
+ NarrowingTarget(short);
+};
+
+void narrowing_float_to_int() {
+ int x(3.14);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:9: note: narrowing conversion from 'double' to 'int'
+ // CHECK-FIXES: int x{3.14};
+}
+
+void narrowing_int_to_short(int n) {
+ short s(n);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:11: note: narrowing conversion from 'int' to 'short'
+ // CHECK-FIXES: short s{n};
+}
+
+void narrowing_int_to_short_constant() {
+ short s(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-FIXES: short s{1};
+}
+
+void narrowing_double_to_float(double d) {
+ float f(d);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:11: note: narrowing conversion from 'double' to 'float'
+ // CHECK-FIXES: float f{d};
+}
+
+void narrowing_double_to_float_constant() {
+ float f(1.0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-FIXES: float f{1.0};
+}
+
+void narrowing_double_to_float_constant_overflow() {
+ float f(1e300);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:11: note: narrowing conversion from 'double' to 'float'
+ // CHECK-FIXES: float f{1e300};
+}
+
+void narrowing_int_to_float(int n) {
+ float f(n);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:11: note: narrowing conversion from 'int' to 'float'
+ // CHECK-FIXES: float f{n};
+}
+
+void narrowing_int_to_float_constant() {
+ float f(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-FIXES: float f{1};
+}
+
+void narrowing_int_to_float_constant_inexact() {
+ float f(16777217); // not exactly representable in float
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:11: note: narrowing conversion from 'int' to 'float'
+ // CHECK-FIXES: float f{16777217}; // not exactly representable in float
+}
+
+void narrowing_ctor_arg(int n) {
+ NarrowingTarget t(n);
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:21: note: narrowing conversion from 'int' to 'short'
+ // CHECK-FIXES: NarrowingTarget t{n};
+}
+
+void narrowing_ctor_arg_constant() {
+ NarrowingTarget t(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use braced initialization
+ // CHECK-FIXES: NarrowingTarget t{1};
+}
+
+void narrowing_functional_cast() {
+ (void)int(3.14);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:13: note: narrowing conversion from 'double' to 'int'
+ // CHECK-FIXES: (void)int{3.14};
+}
+
+void narrowing_new_scalar() {
+ int *p = new int(3.14);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:20: note: narrowing conversion from 'double' to 'int'
+ // CHECK-FIXES: int *p = new int{3.14};
+}
+
+struct NarrowingMember {
+ short n;
+ NarrowingMember(int x) : n(x) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:30: note: narrowing conversion from 'int' to 'short'
+ // CHECK-FIXES: NarrowingMember(int x) : n{x} {}
+};
+
+void narrowing_ptr_to_bool() {
+ int *p = nullptr;
+ bool b(p);
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:10: note: narrowing conversion
+ // CHECK-FIXES: bool b{p};
+}
+
+struct BoolCtor {
+ BoolCtor(bool);
+};
+void narrowing_ptr_to_bool_ctor() {
+ int *p = nullptr;
+ BoolCtor bc(p);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:15: note: narrowing conversion
+ // CHECK-FIXES: BoolCtor bc{p};
+}
+
+void narrowing_signed_neg_to_unsigned() {
+ unsigned u(-1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:14: note: narrowing conversion from 'int' to 'unsigned int'
+ // CHECK-FIXES: unsigned u{-1};
+}
+
+void no_narrowing_int_to_bool() {
+ bool b(0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use braced initialization
+ // CHECK-FIXES: bool b{0};
+}
+
+struct TwoNarrowing {
+ TwoNarrowing(short, short);
+};
+void narrowing_multiple_args(int a, int b) {
+ TwoNarrowing t(a, b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:18: note: narrowing conversion from 'int' to 'short'
+ // CHECK-MESSAGES: :[[@LINE-3]]:21: note: narrowing conversion from 'int' to 'short'
+ // CHECK-FIXES: TwoNarrowing t{a, b};
+}
+
+void reference_init() {
+ int a = 1;
+ int &r(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use braced initialization
+ // CHECK-FIXES: int &r{a};
+}
+
+void volatile_variable() {
+ volatile int x(42);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use braced initialization
+ // CHECK-FIXES: volatile int x{42};
+}
+
+struct BaseClass {
+ BaseClass(int);
+};
+
+struct DerivedFromBase : BaseClass {
+ DerivedFromBase() : BaseClass(1)
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: use braced initialization
+ // CHECK-FIXES: DerivedFromBase() : BaseClass{1}
+ {}
+};
+
+struct DelegatingCtor {
+ DelegatingCtor(int);
+ DelegatingCtor() : DelegatingCtor(1)
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: use braced initialization
+ // CHECK-FIXES: DelegatingCtor() : DelegatingCtor{1}
+ {}
+};
+
+struct DelegatingNarrowing {
+ DelegatingNarrowing(short);
+ DelegatingNarrowing(int x, int) : DelegatingNarrowing(x)
+ // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: use braced initialization
+ // CHECK-MESSAGES: :[[@LINE-2]]:57: note: narrowing conversion from 'int' to 'short'
+ // CHECK-FIXES: DelegatingNarrowing(int x, int) : DelegatingNarrowing{x}
+ {}
+};
+
+enum Color { Red, Green, Blue };
+void enum_functional_cast() {
+ (void)Color(0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-FIXES: (void)Color{0};
+}
+
+enum class ScopedColor { R, G, B };
+void scoped_enum_cast() {
+ (void)ScopedColor(0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-FIXES: (void)ScopedColor{0};
+}
+
+struct WithStatic {
+ static Simple member;
+};
+Simple WithStatic::member(1);
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use braced initialization
+// CHECK-FIXES: Simple WithStatic::member{1};
+
+void zero_arg_temporary() {
+ (void)Simple(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use braced initialization
+ // CHECK-FIXES: (void)Simple{1};
+}
+
+struct BaseWithIL {
+ BaseWithIL(std::initializer_list<int>);
+ BaseWithIL(int, int);
+};
+struct DerivedNoUsing : BaseWithIL {
+ DerivedNoUsing(int a, int b) : BaseWithIL(a, b) {}
+};
+
+struct BaseNoIL {
+ BaseNoIL(int, int);
+};
+struct DerivedFromNoIL : BaseNoIL {
+ DerivedFromNoIL(int a, int b) : BaseNoIL(a, b) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: use braced initialization
+ // CHECK-FIXES: DerivedFromNoIL(int a, int b) : BaseNoIL{a, b} {}
+};
+
+struct BitfieldStruct {
+ int x : 3;
+ BitfieldStruct(int n) : x(n)
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: use braced initialization
+ // CHECK-FIXES: BitfieldStruct(int n) : x{n}
+ {}
+};
+
More information about the cfe-commits
mailing list