Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 41 additions & 7 deletions clang/lib/Format/ContinuationIndenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,12 +304,15 @@ bool ContinuationIndenter::canBreak(const LineState &State) {
Current.closesBlockOrBlockTypeList(Style))) {
return false;
}

// The opening "{" of a braced list has to be on the same line as the first
// element if it is nested in another braced init list or function call.
// element if it is nested in another braced init list or function call,
// unless it is an array initializer that needs to be aligned.
if (!Current.MustBreakBefore && Previous.is(tok::l_brace) &&
Previous.isNot(TT_DictLiteral) && Previous.is(BK_BracedInit) &&
Previous.Previous &&
Previous.Previous->isOneOf(tok::l_brace, tok::l_paren, tok::comma)) {
Previous.Previous->isOneOf(tok::l_brace, tok::l_paren, tok::comma) &&
!Previous.opensAlignedArrayInitializer(Style)) {
return false;
}
// This prevents breaks like:
Expand Down Expand Up @@ -411,6 +414,7 @@ bool ContinuationIndenter::mustBreak(const LineState &State) {
}
if (CurrentState.BreakBeforeClosingBrace &&
(Current.closesBlockOrBlockTypeList(Style) ||
Current.closesAlignedArrayInitializer(Style) ||
(Current.is(tok::r_brace) &&
Current.isBlockIndentedInitRBrace(Style)))) {
return true;
Expand Down Expand Up @@ -805,6 +809,13 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
CurrentState.NoLineBreak = true;
}

// If this is the first element of an array initializer that needs to have
// it's columns aligned, do not allow any further elements to break the line.
// If we need to break the line for some reason, then this token must be
// placed on its own line as well, and the current state should be discarded.
if (Previous.opensAlignedArrayInitializer(Style) && Style.ColumnLimit > 0)
CurrentState.NoLineBreak = true;

if (Current.is(TT_SelectorName) && !CurrentState.ObjCSelectorNameFound) {
unsigned MinIndent = std::max(
State.FirstIndent + Style.ContinuationIndentWidth, CurrentState.Indent);
Expand Down Expand Up @@ -1959,12 +1970,35 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State,
NewIndent = CurrentState.LastSpace + Style.ContinuationIndentWidth;
}
const FormatToken *NextNonComment = Current.getNextNonComment();
AvoidBinPacking = EndsInComma || Current.is(TT_DictLiteral) ||
Style.isProto() || !Style.BinPackArguments ||
(NextNonComment && NextNonComment->isOneOf(
TT_DesignatedInitializerPeriod,
TT_DesignatedInitializerLSquare));

bool AlignedArrayInitializer = Current.opensAlignedArrayInitializer(Style);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bool AlignedArrayInitializer = Current.opensAlignedArrayInitializer(Style);
const bool AlignedArrayInitializer = Current.opensAlignedArrayInitializer(Style);


AvoidBinPacking =
EndsInComma || Current.is(TT_DictLiteral) || Style.isProto() ||
!Style.BinPackArguments ||
(NextNonComment &&
NextNonComment->isOneOf(TT_DesignatedInitializerPeriod,
TT_DesignatedInitializerLSquare)) ||
(Style.AlignArrayOfStructures != FormatStyle::AIAS_None &&
State.Line->Type == LineType::LT_ArrayOfStructInitializer) ||
AlignedArrayInitializer;

BreakBeforeParameter = EndsInComma;

// If this is an array initializer that will have it's columns aligned, and
// the value is too long to fit on a line, we break before each parameter
// because trying to align columns across multiple lines has too many corner
// cases to do properly. This way, either we align columns all on the same
// line, or we don't align columns at all because they are all on their own
// line.
if (AlignedArrayInitializer && Style.ColumnLimit) {
const unsigned LengthToMatchingParen =
getLengthToMatchingParen(Current, State.Stack) + State.Column;

if (LengthToMatchingParen > getColumnLimit(State))
BreakBeforeParameter = true;
}

if (Current.ParameterCount > 1)
NestedBlockIndent = std::max(NestedBlockIndent, State.Column + 1);
} else {
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/Format/FormatToken.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@ bool FormatToken::opensBlockOrBlockTypeList(const FormatStyle &Style) const {
(is(tok::less) && Style.isProto());
}

bool FormatToken::opensAlignedArrayInitializer(const FormatStyle &Style) const {
if (isNot(tok::l_brace) ||
Style.AlignArrayOfStructures == FormatStyle::AIAS_None) {
return false;
}

const FormatToken *Next = getNextNonComment();
return Next != nullptr && Next->StartsColumn;
}

TokenRole::~TokenRole() {}

void TokenRole::precomputeFormattingInfos(const FormatToken *Token) {}
Expand Down
13 changes: 10 additions & 3 deletions clang/lib/Format/FormatToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -580,9 +580,6 @@ struct FormatToken {
/// The first token in set of column elements.
bool StartsColumn = false;

/// This notes the start of the line of an array initializer.
bool ArrayInitializerLineStart = false;

/// This starts an array initializer.
bool IsArrayInitializer = false;

Expand Down Expand Up @@ -859,6 +856,11 @@ struct FormatToken {
/// list that should be indented with a block indent.
[[nodiscard]] bool opensBlockOrBlockTypeList(const FormatStyle &Style) const;

/// Returns \c true if this tokens starts an array initializer that needs to
/// have it's elements be aligned
[[nodiscard]] bool
opensAlignedArrayInitializer(const FormatStyle &Style) const;

/// Returns whether the token is the left square bracket of a C++
/// structured binding declaration.
bool isCppStructuredBinding(bool IsCpp) const {
Expand All @@ -879,6 +881,11 @@ struct FormatToken {
return MatchingParen && MatchingParen->opensBlockOrBlockTypeList(Style);
}

/// Same as opensAlignedArrayInitializer, but for the closing token.
bool closesAlignedArrayInitializer(const FormatStyle &Style) const {
return MatchingParen && MatchingParen->opensAlignedArrayInitializer(Style);
}

/// Return the actual namespace token, if this token starts a namespace
/// block.
const FormatToken *getNamespaceToken() const {
Expand Down
40 changes: 37 additions & 3 deletions clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4227,13 +4227,17 @@ void TokenAnnotator::calculateArrayInitializerColumnList(
if (Line.First == Line.Last)
return;
auto *CurrentToken = Line.First;
CurrentToken->ArrayInitializerLineStart = true;
unsigned Depth = 0;
while (CurrentToken && CurrentToken != Line.Last) {
if (CurrentToken->is(tok::l_brace)) {
CurrentToken->IsArrayInitializer = true;
if (CurrentToken->Next)
CurrentToken->Next->MustBreakBefore = true;

// Ensure the end brace of the outer array is on its own line
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Ensure the end brace of the outer array is on its own line
// Ensure the end brace of the outer array is on its own line.

if (CurrentToken->MatchingParen)
CurrentToken->MatchingParen->MustBreakBefore = true;

CurrentToken =
calculateInitializerColumnList(Line, CurrentToken->Next, Depth + 1);
} else {
Expand All @@ -4249,11 +4253,40 @@ FormatToken *TokenAnnotator::calculateInitializerColumnList(
++Depth;
else if (CurrentToken->is(tok::r_brace))
--Depth;

// Ensure each outer array element starts on its own line
if (Depth == 1 && CurrentToken->is(tok::comma)) {
auto *NextNonComment = CurrentToken->getNextNonComment();
if (NextNonComment)
Comment on lines +4259 to +4260
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
auto *NextNonComment = CurrentToken->getNextNonComment();
if (NextNonComment)
if (auto *NextNonComment = CurrentToken->getNextNonComment())

NextNonComment->MustBreakBefore = true;
}

if (Depth == 2 && CurrentToken->isOneOf(tok::l_brace, tok::comma)) {
CurrentToken = CurrentToken->Next;
if (!CurrentToken)
break;
CurrentToken->StartsColumn = true;

// Right (closing) braces should not count as starting a column because
// they are aligned using separate logic.

// Note: This uses startsSequence() so that trailing comments are skipped
// when checking if the token after a comma/l-brace is a r_brace. We can't
// just ignore comments in general, because an inline comment with
// something else after it should still count as starting a column.
// IE:
//
// { // a
// 4
// }
//
// vs.
//
// { /* a */ 4 }
//
Comment on lines +4277 to +4285
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe put this next to each other, to not waste as much vertical space.

// In the first case, the comment does not start a column, but in the
// second it does.
CurrentToken->StartsColumn = !CurrentToken->startsSequence(tok::r_brace);

CurrentToken = CurrentToken->Previous;
}
CurrentToken = CurrentToken->Next;
Expand Down Expand Up @@ -6199,7 +6232,8 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,
// block-indented initialization list.
if (Right.is(tok::r_brace)) {
return Right.MatchingParen && (Right.MatchingParen->is(BK_Block) ||
(Right.isBlockIndentedInitRBrace(Style)));
Right.isBlockIndentedInitRBrace(Style) ||
Right.closesAlignedArrayInitializer(Style));
}

// We only break before r_paren if we're in a block indented context.
Expand Down
Loading