diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -1829,6 +1829,41 @@ } +.. _BracedInitializerIndentWidth: + +**BracedInitializerIndentWidth** (``Unsigned``) :versionbadge:`clang-format 17` :ref:`¶ ` + The number of columns to use to indent the contents of braced init lists. + If unset, ``ContinuationIndentWidth`` is used. + + .. code-block:: c++ + + AlignAfterOpenBracket: AlwaysBreak + BracedInitializerIndentWidth: 2 + + void f() { + SomeClass c{ + "foo", + "bar", + "baz", + }; + auto s = SomeStruct{ + .foo = "foo", + .bar = "bar", + .baz = "baz", + }; + SomeArrayT a[3] = { + { + foo, + bar, + }, + { + foo, + bar, + }, + SomeArrayT{}, + }; + } + .. _BreakAfterAttributes: **BreakAfterAttributes** (``AttributeBreakingStyle``) :versionbadge:`clang-format 16` :ref:`¶ ` diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -513,6 +513,8 @@ - Add additional Qualifier Ordering support for special cases such as templates, requires clauses, long qualified names. - Fix all known issues associated with ``LambdaBodyIndentation: OuterScope``. +- Add ``BracedInitializerIndentWidth`` which can be used to configure + the indentation level of the contents of braced init lists. libclang -------- diff --git a/clang/docs/tools/dump_format_style.py b/clang/docs/tools/dump_format_style.py --- a/clang/docs/tools/dump_format_style.py +++ b/clang/docs/tools/dump_format_style.py @@ -69,9 +69,13 @@ elif typestr == 'std::string': return 'String' - subtype, napplied = re.subn(r'^std::vector<(.*)>$', r'\1', typestr) - if napplied == 1: - return 'List of ' + pluralize(to_yaml_type(subtype)) + match = re.match(r'std::vector<(.*)>$', typestr) + if match: + return 'List of ' + pluralize(to_yaml_type(match.group(1))) + + match = re.match(r'std::optional<(.*)>$', typestr) + if match: + return to_yaml_type(match.group(1)) return typestr @@ -331,7 +335,8 @@ if option.type not in ['bool', 'unsigned', 'int', 'std::string', 'std::vector', 'std::vector', - 'std::vector']: + 'std::vector', + 'std::optional']: if option.type in enums: option.enum = enums[option.type] elif option.type in nested_structs: diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -944,6 +944,39 @@ /// \version 12 BitFieldColonSpacingStyle BitFieldColonSpacing; + /// The number of columns to use to indent the contents of braced init lists. + /// If unset, ``ContinuationIndentWidth`` is used. + /// \code + /// AlignAfterOpenBracket: AlwaysBreak + /// BracedInitializerIndentWidth: 2 + /// + /// void f() { + /// SomeClass c{ + /// "foo", + /// "bar", + /// "baz", + /// }; + /// auto s = SomeStruct{ + /// .foo = "foo", + /// .bar = "bar", + /// .baz = "baz", + /// }; + /// SomeArrayT a[3] = { + /// { + /// foo, + /// bar, + /// }, + /// { + /// foo, + /// bar, + /// }, + /// SomeArrayT{}, + /// }; + /// } + /// \endcode + /// \version 17 + std::optional BracedInitializerIndentWidth; + /// Different ways to wrap braces after control statements. enum BraceWrappingAfterControlStatementStyle : int8_t { /// Never wrap braces after a control statement. @@ -4286,6 +4319,7 @@ BinPackArguments == R.BinPackArguments && BinPackParameters == R.BinPackParameters && BitFieldColonSpacing == R.BitFieldColonSpacing && + BracedInitializerIndentWidth == R.BracedInitializerIndentWidth && BreakAfterAttributes == R.BreakAfterAttributes && BreakAfterJavaFieldAnnotations == R.BreakAfterJavaFieldAnnotations && BreakArrays == R.BreakArrays && diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp --- a/clang/lib/Format/ContinuationIndenter.cpp +++ b/clang/lib/Format/ContinuationIndenter.cpp @@ -1665,10 +1665,14 @@ if (Current.opensBlockOrBlockTypeList(Style)) { NewIndent = Style.IndentWidth + std::min(State.Column, CurrentState.NestedBlockIndent); + } else if (Current.is(tok::l_brace)) { + NewIndent = + CurrentState.LastSpace + Style.BracedInitializerIndentWidth.value_or( + Style.ContinuationIndentWidth); } else { NewIndent = CurrentState.LastSpace + Style.ContinuationIndentWidth; } - const FormatToken *NextNoComment = Current.getNextNonComment(); + const FormatToken *NextNonComment = Current.getNextNonComment(); bool EndsInComma = Current.MatchingParen && Current.MatchingParen->Previous && Current.MatchingParen->Previous->is(tok::comma); @@ -1676,9 +1680,9 @@ Style.Language == FormatStyle::LK_Proto || Style.Language == FormatStyle::LK_TextProto || !Style.BinPackArguments || - (NextNoComment && - NextNoComment->isOneOf(TT_DesignatedInitializerPeriod, - TT_DesignatedInitializerLSquare)); + (NextNonComment && NextNonComment->isOneOf( + TT_DesignatedInitializerPeriod, + TT_DesignatedInitializerLSquare)); BreakBeforeParameter = EndsInComma; if (Current.ParameterCount > 1) NestedBlockIndent = std::max(NestedBlockIndent, State.Column + 1); diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -879,6 +879,8 @@ IO.mapOptional("BinPackArguments", Style.BinPackArguments); IO.mapOptional("BinPackParameters", Style.BinPackParameters); IO.mapOptional("BitFieldColonSpacing", Style.BitFieldColonSpacing); + IO.mapOptional("BracedInitializerIndentWidth", + Style.BracedInitializerIndentWidth); IO.mapOptional("BraceWrapping", Style.BraceWrapping); IO.mapOptional("BreakAfterAttributes", Style.BreakAfterAttributes); IO.mapOptional("BreakAfterJavaFieldAnnotations", @@ -1337,6 +1339,7 @@ LLVMStyle.BitFieldColonSpacing = FormatStyle::BFCS_Both; LLVMStyle.BinPackArguments = true; LLVMStyle.BinPackParameters = true; + LLVMStyle.BracedInitializerIndentWidth = std::nullopt; LLVMStyle.BraceWrapping = {/*AfterCaseLabel=*/false, /*AfterClass=*/false, /*AfterControlStatement=*/FormatStyle::BWACS_Never, diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -247,6 +247,8 @@ SpacesBeforeTrailingComments, 1234u); CHECK_PARSE("IndentWidth: 32", IndentWidth, 32u); CHECK_PARSE("ContinuationIndentWidth: 11", ContinuationIndentWidth, 11u); + CHECK_PARSE("BracedInitializerIndentWidth: 34", BracedInitializerIndentWidth, + 34); CHECK_PARSE("CommentPragmas: '// abc$'", CommentPragmas, "// abc$"); Style.QualifierAlignment = FormatStyle::QAS_Right; diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -4855,6 +4855,191 @@ " [5] = eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee};"); } +TEST_F(FormatTest, BracedInitializerIndentWidth) { + auto Style = getLLVMStyleWithColumns(60); + Style.BinPackArguments = true; + Style.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak; + Style.BracedInitializerIndentWidth = 6; + + // Non-initializing braces are unaffected by BracedInitializerIndentWidth. + verifyFormat("enum class {\n" + " One,\n" + " Two,\n" + "};\n", + Style); + verifyFormat("class Foo {\n" + " Foo() {}\n" + " void bar();\n" + "};\n", + Style); + verifyFormat("void foo() {\n" + " auto bar = baz;\n" + " return baz;\n" + "};\n", + Style); + verifyFormat("auto foo = [&] {\n" + " auto bar = baz;\n" + " return baz;\n" + "};\n", + Style); + verifyFormat("{\n" + " auto bar = baz;\n" + " return baz;\n" + "};\n", + Style); + // Non-brace initialization is unaffected by BracedInitializerIndentWidth. + verifyFormat("SomeClass clazz(\n" + " \"xxxxxxxxxxxxxxxxxx\", \"yyyyyyyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzzzzzzz\");\n", + Style); + + // The following types of initialization are all affected by + // BracedInitializerIndentWidth. Aggregate initialization. + verifyFormat("int LooooooooooooooooooooooooongVariable[2] = {\n" + " 10000000, 20000000};", + Style); + verifyFormat("SomeStruct s{\n" + " \"xxxxxxxxxxxxxxxx\", \"yyyyyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzzzzz\"};\n", + Style); + // Designated initializers. + verifyFormat("int LooooooooooooooooooooooooongVariable[1] = {\n" + " [0] = 10000000, [1] = 20000000};", + Style); + verifyFormat("SomeStruct s{\n" + " .foo = \"xxxxxxxxxxxxx\",\n" + " .bar = \"yyyyyyyyyyyyy\",\n" + " .baz = \"zzzzzzzzzzzzz\"};\n", + Style); + // List initialization. + verifyFormat("SomeStruct s{\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + "};\n", + Style); + verifyFormat("SomeStruct{\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + "};\n", + Style); + verifyFormat("new SomeStruct{\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + "};\n", + Style); + // Member initializer. + verifyFormat("class SomeClass {\n" + " SomeStruct s{\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + " };\n" + "};\n", + Style); + // Constructor member initializer. + verifyFormat("SomeClass::SomeClass : strct{\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + " } {}\n", + Style); + // Copy initialization. + verifyFormat("SomeStruct s = SomeStruct{\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + "};\n", + Style); + // Copy list initialization. + verifyFormat("SomeStruct s = {\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + "};\n", + Style); + // Assignment operand initialization. + verifyFormat("s = {\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + "};\n", + Style); + // Returned object initialization. + verifyFormat("return {\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + "};\n", + Style); + // Initializer list. + verifyFormat("auto initializerList = {\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + "};\n", + Style); + // Function parameter initialization. + verifyFormat("func({\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + "});\n", + Style); + // Nested init lists. + verifyFormat("SomeStruct s = {\n" + " {{init1, init2, init3, init4, init5},\n" + " {init1, init2, init3, init4, init5}}};\n", + Style); + verifyFormat("SomeStruct s = {\n" + " {{\n" + " .init1 = 1,\n" + " .init2 = 2,\n" + " .init3 = 3,\n" + " .init4 = 4,\n" + " .init5 = 5,\n" + " },\n" + " {init1, init2, init3, init4, init5}}};\n", + Style); + verifyFormat("SomeArrayT a[3] = {\n" + " {\n" + " foo,\n" + " bar,\n" + " },\n" + " {\n" + " foo,\n" + " bar,\n" + " },\n" + " SomeArrayT{},\n" + "}\n", + Style); + verifyFormat("SomeArrayT a[3] = {\n" + " {foo},\n" + " {\n" + " {\n" + " init1,\n" + " init2,\n" + " init3,\n" + " },\n" + " {\n" + " init1,\n" + " init2,\n" + " init3,\n" + " },\n" + " },\n" + " {baz},\n" + "}\n", + Style); + + // Aligning after open braces unaffected by BracedInitializerIndentWidth. + Style.AlignAfterOpenBracket = FormatStyle::BAS_Align; + verifyFormat("SomeStruct s{\"xxxxxxxxxxxxx\", \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\"};\n", + Style); +} + TEST_F(FormatTest, NestedStaticInitializers) { verifyFormat("static A x = {{{}}};\n"); verifyFormat("static A x = {{{init1, init2, init3, init4},\n"