Skip to content

Commit

Permalink
[clang-format] Support block indenting array/struct list initializers
Browse files Browse the repository at this point in the history
C89 and C99 list initializers are treated differently than Cpp11 braced
initializers. This patch identifies the C array/struct initializer lists by
finding the preceding equal sign before a left brace, and applies formatting
rules for BracketAlignmentStyle.BlockIndent to those list initializers.

Fixes #57878.

Differential Revision: https://reviews.llvm.org/D153205
  • Loading branch information
gedare authored and owenca committed Jul 6, 2023
1 parent ad14659 commit 413a7cb
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 9 deletions.
3 changes: 2 additions & 1 deletion clang/docs/ClangFormatStyleOptions.rst
Expand Up @@ -241,7 +241,8 @@ the configuration (without a prefix: ``Auto``).

.. warning::

Note: This currently only applies to parentheses.
This currently only applies to braced initializer lists (when
``Cpp11BracedListStyle`` is ``true``) and parentheses.



Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Format/Format.h
Expand Up @@ -93,7 +93,8 @@ struct FormatStyle {
/// \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,
};
Expand Down
9 changes: 7 additions & 2 deletions clang/lib/Format/ContinuationIndenter.cpp
Expand Up @@ -362,7 +362,9 @@ bool ContinuationIndenter::mustBreak(const LineState &State) {
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))
Expand Down Expand Up @@ -1168,7 +1170,10 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
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())
Expand Down
15 changes: 15 additions & 0 deletions clang/lib/Format/FormatToken.cpp
Expand Up @@ -75,6 +75,21 @@ bool FormatToken::isTypeOrIdentifier() const {
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())
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Format/FormatToken.h
Expand Up @@ -774,6 +774,9 @@ struct FormatToken {
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;
Expand Down
6 changes: 4 additions & 2 deletions clang/lib/Format/TokenAnnotator.cpp
Expand Up @@ -5482,8 +5482,10 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,

// 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)) {
Expand Down
155 changes: 152 additions & 3 deletions clang/unittests/Format/FormatTest.cpp
Expand Up @@ -4963,7 +4963,7 @@ TEST_F(FormatTest, BracedInitializerIndentWidth) {
" \"zzzzzzzzzzzzzzzz\"};\n",
Style);
// Designated initializers.
verifyFormat("int LooooooooooooooooooooooooongVariable[1] = {\n"
verifyFormat("int LooooooooooooooooooooooooongVariable[2] = {\n"
" [0] = 10000000, [1] = 20000000};",
Style);
verifyFormat("SomeStruct s{\n"
Expand Down Expand Up @@ -5073,7 +5073,7 @@ TEST_F(FormatTest, BracedInitializerIndentWidth) {
" bar,\n"
" },\n"
" SomeArrayT{},\n"
"}\n",
"};",
Style);
verifyFormat("SomeArrayT a[3] = {\n"
" {foo},\n"
Expand All @@ -5090,7 +5090,7 @@ TEST_F(FormatTest, BracedInitializerIndentWidth) {
" },\n"
" },\n"
" {baz},\n"
"}\n",
"};",
Style);

// Aligning after open braces unaffected by BracedInitializerIndentWidth.
Expand Down Expand Up @@ -25532,6 +25532,155 @@ TEST_F(FormatTest, AlignAfterOpenBracketBlockIndentForStatement) {
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] = <%%>;");
Expand Down

0 comments on commit 413a7cb

Please sign in to comment.