diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -241,7 +241,8 @@ .. warning:: - Note: This currently only applies to parentheses. + This currently only applies to braced initializer lists (when + ``Cpp11BracedListStyle`` is ``true``) and parentheses. 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 @@ -93,7 +93,8 @@ /// \endcode /// /// \warning - /// Note: This currently only applies to parentheses. + /// This currently only applies to braced initializer lists (when + /// ``Cpp11BracedListStyle`` is ``true``) and parentheses. /// \endwarning BAS_BlockIndent, }; 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 @@ -362,7 +362,9 @@ return true; } if (CurrentState.BreakBeforeClosingBrace && - Current.closesBlockOrBlockTypeList(Style)) { + (Current.closesBlockOrBlockTypeList(Style) || + (Current.is(tok::r_brace) && + Current.isBlockIndentedInitRBrace(Style)))) { return true; } if (CurrentState.BreakBeforeClosingParen && Current.is(tok::r_paren)) @@ -1168,7 +1170,10 @@ return State.Stack[State.Stack.size() - 2].LastSpace; } if (Style.AlignAfterOpenBracket == FormatStyle::BAS_BlockIndent && - Current.is(tok::r_paren) && State.Stack.size() > 1) { + (Current.is(tok::r_paren) || + (Current.is(tok::r_brace) && + Current.MatchingParen->is(BK_BracedInit))) && + State.Stack.size() > 1) { return State.Stack[State.Stack.size() - 2].LastSpace; } if (NextNonComment->is(TT_TemplateString) && NextNonComment->closesScope()) diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -774,6 +774,9 @@ return Tok; } + /// Returns \c true if this token ends a block indented initializer list. + [[nodiscard]] bool isBlockIndentedInitRBrace(const FormatStyle &Style) const; + /// Returns \c true if this tokens starts a block-type list, i.e. a /// list that should be indented with a block indent. [[nodiscard]] bool opensBlockOrBlockTypeList(const FormatStyle &Style) const; diff --git a/clang/lib/Format/FormatToken.cpp b/clang/lib/Format/FormatToken.cpp --- a/clang/lib/Format/FormatToken.cpp +++ b/clang/lib/Format/FormatToken.cpp @@ -75,6 +75,21 @@ return isSimpleTypeSpecifier() || Tok.isOneOf(tok::kw_auto, tok::identifier); } +bool FormatToken::isBlockIndentedInitRBrace(const FormatStyle &Style) const { + assert(is(tok::r_brace)); + if (!Style.Cpp11BracedListStyle || + Style.AlignAfterOpenBracket != FormatStyle::BAS_BlockIndent) { + return false; + } + const auto *LBrace = MatchingParen; + assert(LBrace && LBrace->is(tok::l_brace)); + if (LBrace->is(BK_BracedInit)) + return true; + if (LBrace->Previous && LBrace->Previous->is(tok::equal)) + return true; + return false; +} + bool FormatToken::opensBlockOrBlockTypeList(const FormatStyle &Style) const { // C# Does not indent object initialisers as continuations. if (is(tok::l_brace) && getBlockKind() == BK_BracedInit && Style.isCSharp()) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -5482,8 +5482,10 @@ // We only break before r_brace if there was a corresponding break before // the l_brace, which is tracked by BreakBeforeClosingBrace. - if (Right.is(tok::r_brace)) - return Right.MatchingParen && Right.MatchingParen->is(BK_Block); + if (Right.is(tok::r_brace)) { + return Right.MatchingParen && (Right.MatchingParen->is(BK_Block) || + (Right.isBlockIndentedInitRBrace(Style))); + } // We only break before r_paren if we're in a block indented context. if (Right.is(tok::r_paren)) { 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 @@ -4944,7 +4944,7 @@ " \"zzzzzzzzzzzzzzzz\"};\n", Style); // Designated initializers. - verifyFormat("int LooooooooooooooooooooooooongVariable[1] = {\n" + verifyFormat("int LooooooooooooooooooooooooongVariable[2] = {\n" " [0] = 10000000, [1] = 20000000};", Style); verifyFormat("SomeStruct s{\n" @@ -5054,7 +5054,7 @@ " bar,\n" " },\n" " SomeArrayT{},\n" - "}\n", + "};", Style); verifyFormat("SomeArrayT a[3] = {\n" " {foo},\n" @@ -5071,7 +5071,7 @@ " },\n" " },\n" " {baz},\n" - "}\n", + "};", Style); // Aligning after open braces unaffected by BracedInitializerIndentWidth. @@ -25513,6 +25513,155 @@ Style); } +TEST_F(FormatTest, AlignAfterOpenBracketBlockIndentInitializers) { + auto Style = getLLVMStyleWithColumns(60); + Style.AlignAfterOpenBracket = FormatStyle::BAS_BlockIndent; + // Aggregate initialization. + verifyFormat("int LooooooooooooooooooooooooongVariable[2] = {\n" + " 10000000, 20000000\n" + "};", + Style); + verifyFormat("SomeStruct s{\n" + " \"xxxxxxxxxxxxxxxx\", \"yyyyyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzzzzz\"\n" + "};", + Style); + // Designated initializers. + verifyFormat("int LooooooooooooooooooooooooongVariable[2] = {\n" + " [0] = 10000000, [1] = 20000000\n" + "};", + 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" + "};", + Style); + verifyFormat("SomeStruct{\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + "};", + Style); + verifyFormat("new SomeStruct{\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + "};", + Style); + // Member initializer. + verifyFormat("class SomeClass {\n" + " SomeStruct s{\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + " };\n" + "};", + Style); + // Constructor member initializer. + verifyFormat("SomeClass::SomeClass : strct{\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + " } {}", + Style); + // Copy initialization. + verifyFormat("SomeStruct s = SomeStruct{\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + "};", + Style); + // Copy list initialization. + verifyFormat("SomeStruct s = {\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + "};", + Style); + // Assignment operand initialization. + verifyFormat("s = {\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + "};", + Style); + // Returned object initialization. + verifyFormat("return {\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + "};", + Style); + // Initializer list. + verifyFormat("auto initializerList = {\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\n" + "};", + Style); + // Function parameter initialization. + verifyFormat("func({\n" + " \"xxxxxxxxxxxxx\",\n" + " \"yyyyyyyyyyyyy\",\n" + " \"zzzzzzzzzzzzz\",\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" + "};", + 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" + "};", + Style); +} + TEST_F(FormatTest, UnderstandsDigraphs) { verifyFormat("int arr<:5:> = {};"); verifyFormat("int arr[5] = <%%>;");