diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp index 9ab024a03fbd7..54f231f2d7488 100644 --- a/clang/lib/Format/ContinuationIndenter.cpp +++ b/clang/lib/Format/ContinuationIndenter.cpp @@ -240,6 +240,45 @@ RawStringFormatStyleManager::getEnclosingFunctionStyle( return It->second; } +IndentationAndAlignment +IndentationAndAlignment::addPadding(unsigned Spaces) const { + return IndentationAndAlignment(Total + Spaces, IndentedFrom); +} + +IndentationAndAlignment +IndentationAndAlignment::operator+(unsigned Spaces) const { + return IndentationAndAlignment(Total + Spaces, Total); +} + +IndentationAndAlignment +IndentationAndAlignment::operator-(unsigned Spaces) const { + return IndentationAndAlignment(Total - Spaces, Total); +} + +IndentationAndAlignment &IndentationAndAlignment::operator+=(unsigned Spaces) { + *this = *this + Spaces; + return *this; +} + +IndentationAndAlignment::IndentationAndAlignment(unsigned Total, + unsigned IndentedFrom) + : Total(Total), IndentedFrom(IndentedFrom) {} + +IndentationAndAlignment::IndentationAndAlignment(unsigned Spaces) + : Total(Spaces), IndentedFrom(Spaces) {} + +bool IndentationAndAlignment::operator<( + const IndentationAndAlignment &Other) const { + if (Total != Other.Total) + return Total < Other.Total; + // The sign to use here was decided arbitrarily. This operator is mostly used + // when a line's indentation should be the max of 2 things. Using this sign + // here makes the program prefer alignment over continuation indentation. That + // is, it makes the alignment step that follows prefer to move the line when + // aligning the previous line. + return IndentedFrom > Other.IndentedFrom; +} + ContinuationIndenter::ContinuationIndenter(const FormatStyle &Style, const AdditionalKeywords &Keywords, const SourceManager &SourceMgr, @@ -491,7 +530,7 @@ bool ContinuationIndenter::mustBreak(const LineState &State) { return true; } - unsigned NewLineColumn = getNewLineColumn(State); + unsigned NewLineColumn = getNewLineColumn(State).Total; if (Current.isMemberAccess() && Style.ColumnLimit != 0 && State.Column + getLengthToNextOperator(Current) > Style.ColumnLimit && (State.Column > NewLineColumn || @@ -819,8 +858,9 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun, } if (Current.is(TT_SelectorName) && !CurrentState.ObjCSelectorNameFound) { - unsigned MinIndent = std::max( - State.FirstIndent + Style.ContinuationIndentWidth, CurrentState.Indent); + unsigned MinIndent = + std::max(State.FirstIndent + Style.ContinuationIndentWidth, + CurrentState.Indent.Total); unsigned FirstColonPos = State.Column + Spaces + Current.ColumnWidth; if (Current.LongestObjCSelectorName == 0) CurrentState.AlignColons = false; @@ -910,7 +950,8 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun, return !Next || Next->isMemberAccess() || Next->is(TT_FunctionDeclarationLParen) || IsFunctionCallParen(*Next); }; - if (IsOpeningBracket(Previous) && State.Column > getNewLineColumn(State) && + if (IsOpeningBracket(Previous) && + State.Column > getNewLineColumn(State).Total && // Don't do this for simple (no expressions) one-argument function calls // as that feels like needlessly wasting whitespace, e.g.: // @@ -955,7 +996,7 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun, CurrentState.NoLineBreak = true; if (startsSegmentOfBuilderTypeCall(Current) && - State.Column > getNewLineColumn(State)) { + State.Column > getNewLineColumn(State).Total) { CurrentState.ContainsUnwrappedBuilder = true; } @@ -1086,7 +1127,8 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State, Penalty += Style.PenaltyBreakFirstLessLess; } - State.Column = getNewLineColumn(State); + const auto [TotalColumn, IndentedFromColumn] = getNewLineColumn(State); + State.Column = TotalColumn; // Add Penalty proportional to amount of whitespace away from FirstColumn // This tends to penalize several lines that are far-right indented, @@ -1132,9 +1174,9 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State, } else { CurrentState.ColonPos = (shouldIndentWrappedSelectorName(Style, State.Line->Type) - ? std::max(CurrentState.Indent, + ? std::max(CurrentState.Indent.Total, State.FirstIndent + Style.ContinuationIndentWidth) - : CurrentState.Indent) + + : CurrentState.Indent.Total) + std::max(NextNonComment->LongestObjCSelectorName, NextNonComment->ColumnWidth); } @@ -1155,7 +1197,7 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State, // when we consume all of the "}"'s FakeRParens at the "{". if (State.Stack.size() > 1) { State.Stack[State.Stack.size() - 2].LastSpace = - std::max(CurrentState.LastSpace, CurrentState.Indent) + + std::max(CurrentState.LastSpace, CurrentState.Indent.Total) + Style.ContinuationIndentWidth; } } @@ -1196,7 +1238,8 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State, State.Line->Type != LT_ImportStatement && Current.isNot(TT_LineComment); Whitespaces.replaceWhitespace(Current, Newlines, State.Column, State.Column, - CurrentState.IsAligned, ContinuePPDirective); + CurrentState.IsAligned, ContinuePPDirective, + IndentedFromColumn); } if (!Current.isTrailingComment()) @@ -1340,7 +1383,8 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State, return Penalty; } -unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { +IndentationAndAlignment +ContinuationIndenter::getNewLineColumn(const LineState &State) { if (!State.NextToken || !State.NextToken->Previous) return 0; @@ -1354,8 +1398,9 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { const FormatToken &Previous = *Current.Previous; // If we are continuing an expression, we want to use the continuation indent. - unsigned ContinuationIndent = - std::max(CurrentState.LastSpace, CurrentState.Indent) + + const auto ContinuationIndent = + std::max(IndentationAndAlignment(CurrentState.LastSpace), + CurrentState.Indent) + Style.ContinuationIndentWidth; const FormatToken *PreviousNonComment = Current.getPreviousNonComment(); const FormatToken *NextNonComment = Previous.getNextNonComment(); @@ -1365,7 +1410,7 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { // Java specific bits. if (Style.isJava() && Current.isOneOf(Keywords.kw_implements, Keywords.kw_extends)) { - return std::max(CurrentState.LastSpace, + return std::max(IndentationAndAlignment(CurrentState.LastSpace), CurrentState.Indent + Style.ContinuationIndentWidth); } @@ -1378,7 +1423,8 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { if (Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths && State.Line->First->is(tok::kw_enum)) { - return (Style.IndentWidth * State.Line->First->IndentLevel) + + return IndentationAndAlignment(Style.IndentWidth * + State.Line->First->IndentLevel) + Style.IndentWidth; } @@ -1497,7 +1543,7 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { // * not remove the 'lead' ContinuationIndentWidth // * always un-indent by the operator when // BreakBeforeTernaryOperators=true - unsigned Indent = CurrentState.Indent; + unsigned Indent = CurrentState.Indent.Total; if (Style.AlignOperands != FormatStyle::OAS_DontAlign) Indent -= Style.ContinuationIndentWidth; if (Style.BreakBeforeTernaryOperators && CurrentState.UnindentOperator) @@ -1537,14 +1583,16 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { TT_LeadingJavaAnnotation))) || (!Style.IndentWrappedFunctionNames && NextNonComment->isOneOf(tok::kw_operator, TT_FunctionDeclarationName))) { - return std::max(CurrentState.LastSpace, CurrentState.Indent); + return std::max(IndentationAndAlignment(CurrentState.LastSpace), + CurrentState.Indent); } if (NextNonComment->is(TT_SelectorName)) { if (!CurrentState.ObjCSelectorNameFound) { - unsigned MinIndent = CurrentState.Indent; + auto MinIndent = CurrentState.Indent; if (shouldIndentWrappedSelectorName(Style, State.Line->Type)) { - MinIndent = std::max(MinIndent, - State.FirstIndent + Style.ContinuationIndentWidth); + MinIndent = + std::max(MinIndent, IndentationAndAlignment(State.FirstIndent) + + Style.ContinuationIndentWidth); } // If LongestObjCSelectorName is 0, we are indenting the first // part of an ObjC selector (or a selector component which is @@ -1555,10 +1603,10 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { // component of the ObjC selector. // // In either case, we want to respect Style.IndentWrappedFunctionNames. - return MinIndent + - std::max(NextNonComment->LongestObjCSelectorName, - NextNonComment->ColumnWidth) - - NextNonComment->ColumnWidth; + return MinIndent.addPadding( + std::max(NextNonComment->LongestObjCSelectorName, + NextNonComment->ColumnWidth) - + NextNonComment->ColumnWidth); } if (!CurrentState.AlignColons) return CurrentState.Indent; @@ -1628,7 +1676,7 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { return CurrentState.Indent - NextNonComment->Tok.getLength() - NextNonComment->SpacesRequiredBefore; } - if (CurrentState.Indent == State.FirstIndent && PreviousNonComment && + if (CurrentState.Indent.Total == State.FirstIndent && PreviousNonComment && PreviousNonComment->isNoneOf(tok::r_brace, TT_CtorInitializerComma)) { // Ensure that we fall back to the continuation indent width instead of // just flushing continuations left. @@ -1718,7 +1766,7 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State, FormatStyle::BCIS_BeforeComma ? 0 : 2); - CurrentState.NestedBlockIndent = CurrentState.Indent; + CurrentState.NestedBlockIndent = CurrentState.Indent.Total; if (Style.PackConstructorInitializers > FormatStyle::PCIS_BinPack) { CurrentState.AvoidBinPacking = true; CurrentState.BreakBeforeParameter = @@ -1733,7 +1781,7 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State, Style.BreakConstructorInitializers == FormatStyle::BCIS_AfterColon) { CurrentState.Indent = State.FirstIndent + Style.ConstructorInitializerIndentWidth; - CurrentState.NestedBlockIndent = CurrentState.Indent; + CurrentState.NestedBlockIndent = CurrentState.Indent.Total; if (Style.PackConstructorInitializers > FormatStyle::PCIS_BinPack) CurrentState.AvoidBinPacking = true; else @@ -1878,7 +1926,8 @@ void ContinuationIndenter::moveStatePastFakeLParens(LineState &State, (Previous && Previous->isOneOf(TT_TableGenDAGArgListComma, TT_TableGenDAGArgListCommaToBreak)))) { NewParenState.Indent = std::max( - std::max(State.Column, NewParenState.Indent), CurrentState.LastSpace); + std::max(IndentationAndAlignment(State.Column), NewParenState.Indent), + IndentationAndAlignment(CurrentState.LastSpace)); } // Special case for generic selection expressions, its comma-separated @@ -1986,7 +2035,7 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State, return Prev->is(tok::comma); }(Current.MatchingParen); - unsigned NewIndent; + IndentationAndAlignment NewIndent = 0; unsigned LastSpace = CurrentState.LastSpace; bool AvoidBinPacking; bool BreakBeforeParameter = false; @@ -1999,7 +2048,7 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State, std::min(State.Column, CurrentState.NestedBlockIndent); } else if (Current.is(tok::l_brace)) { const auto Width = Style.BracedInitializerIndentWidth; - NewIndent = CurrentState.LastSpace + + NewIndent = IndentationAndAlignment(CurrentState.LastSpace) + (Width < 0 ? Style.ContinuationIndentWidth : Width); } else { NewIndent = CurrentState.LastSpace + Style.ContinuationIndentWidth; @@ -2014,9 +2063,9 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State, if (Current.ParameterCount > 1) NestedBlockIndent = std::max(NestedBlockIndent, State.Column + 1); } else { - NewIndent = - Style.ContinuationIndentWidth + - std::max(CurrentState.LastSpace, CurrentState.StartOfFunctionCall); + NewIndent = IndentationAndAlignment(std::max( + CurrentState.LastSpace, CurrentState.StartOfFunctionCall)) + + Style.ContinuationIndentWidth; if (Style.isTableGen() && Current.is(TT_TableGenDAGArgOpenerToBreak) && Style.TableGenBreakInsideDAGArg == FormatStyle::DAS_BreakElements) { @@ -2035,7 +2084,7 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State, // FIXME: We likely want to do this for more combinations of brackets. if (Current.is(tok::less) && Current.ParentBracket == tok::l_paren) { NewIndent = std::max(NewIndent, CurrentState.Indent); - LastSpace = std::max(LastSpace, CurrentState.Indent); + LastSpace = std::max(LastSpace, CurrentState.Indent.Total); } // If ObjCBinPackProtocolList is unspecified, fall back to BinPackParameters @@ -2281,7 +2330,7 @@ unsigned ContinuationIndenter::reformatRawStringLiteral( unsigned CurrentIndent = (!Newline && Current.Next && Current.Next->is(tok::r_paren)) ? State.Stack.back().NestedBlockIndent - : State.Stack.back().Indent; + : State.Stack.back().Indent.Total; unsigned NextStartColumn = ContentStartsOnNewline ? CurrentIndent + Style.IndentWidth : FirstStartColumn; diff --git a/clang/lib/Format/ContinuationIndenter.h b/clang/lib/Format/ContinuationIndenter.h index fe957cf43721a..1554fb441dff0 100644 --- a/clang/lib/Format/ContinuationIndenter.h +++ b/clang/lib/Format/ContinuationIndenter.h @@ -43,6 +43,41 @@ struct RawStringFormatStyleManager { getEnclosingFunctionStyle(StringRef EnclosingFunction) const; }; +/// Represents the spaces at the start of a line, keeping track of what the +/// spaces are for. +struct IndentationAndAlignment { + unsigned Total; + + /// The column that the position of the start of the line is calculated + /// from. It can be more than Total. + unsigned IndentedFrom; + + /// Add spaces for right-justifying the token. The IndentedFrom field does not + /// change. + /// + /// This example in Objective-C shows why the field should not change. The + /// token `xx` is right-justified with this method to align the `:` + /// symbols. The `:` symbols should remain aligned through the step that + /// aligns assignments. That step uses the IndentedFrom field to tell what + /// lines to move. Not changing the field in this method ensures that the 2 + /// lines move together. + /// + /// [x // + /// xxxx:0 + /// xx:0]; + IndentationAndAlignment addPadding(unsigned Spaces) const; + /// Adding indentation is more common than padding. So the operator does that. + IndentationAndAlignment operator+(unsigned Spaces) const; + IndentationAndAlignment operator-(unsigned Spaces) const; + IndentationAndAlignment &operator+=(unsigned Spaces); + + IndentationAndAlignment(unsigned Total, unsigned IndentedFrom); + + IndentationAndAlignment(unsigned Spaces); + + bool operator<(const IndentationAndAlignment &Other) const; +}; + class ContinuationIndenter { public: /// Constructs a \c ContinuationIndenter to format \p Line starting in @@ -168,7 +203,7 @@ class ContinuationIndenter { unsigned addTokenOnNewLine(LineState &State, bool DryRun); /// Calculate the new column for a line wrap before the next token. - unsigned getNewLineColumn(const LineState &State); + IndentationAndAlignment getNewLineColumn(const LineState &State); /// Adds a multiline token to the \p State. /// @@ -195,10 +230,10 @@ class ContinuationIndenter { }; struct ParenState { - ParenState(const FormatToken *Tok, unsigned Indent, unsigned LastSpace, - bool AvoidBinPacking, bool NoLineBreak) + ParenState(const FormatToken *Tok, IndentationAndAlignment Indent, + unsigned LastSpace, bool AvoidBinPacking, bool NoLineBreak) : Tok(Tok), Indent(Indent), LastSpace(LastSpace), - NestedBlockIndent(Indent), IsAligned(false), + NestedBlockIndent(Indent.Total), IsAligned(false), BreakBeforeClosingBrace(false), BreakBeforeClosingParen(false), BreakBeforeClosingAngle(false), AvoidBinPacking(AvoidBinPacking), BreakBeforeParameter(false), NoLineBreak(NoLineBreak), @@ -219,7 +254,7 @@ struct ParenState { /// The position to which a specific parenthesis level needs to be /// indented. - unsigned Indent; + IndentationAndAlignment Indent; /// The position of the last space on each level. /// @@ -356,8 +391,8 @@ struct ParenState { bool UnindentOperator : 1; bool operator<(const ParenState &Other) const { - if (Indent != Other.Indent) - return Indent < Other.Indent; + if (Indent.Total != Other.Indent.Total) + return Indent.Total < Other.Indent.Total; if (LastSpace != Other.LastSpace) return LastSpace < Other.LastSpace; if (NestedBlockIndent != Other.NestedBlockIndent) @@ -406,7 +441,7 @@ struct ParenState { return IsWrappedConditional; if (UnindentOperator != Other.UnindentOperator) return UnindentOperator; - return false; + return Indent < Other.Indent; } }; diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index d31d656a63fc5..913789afd9919 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -1052,8 +1052,8 @@ static void markFinalized(FormatToken *Tok) { static void printLineState(const LineState &State) { llvm::dbgs() << "State: "; for (const ParenState &P : State.Stack) { - llvm::dbgs() << (P.Tok ? P.Tok->TokenText : "F") << "|" << P.Indent << "|" - << P.LastSpace << "|" << P.NestedBlockIndent << " "; + llvm::dbgs() << (P.Tok ? P.Tok->TokenText : "F") << "|" << P.Indent.Total + << "|" << P.LastSpace << "|" << P.NestedBlockIndent << " "; } llvm::dbgs() << State.NextToken->TokenText << "\n"; } @@ -1111,7 +1111,7 @@ class LineFormatter { const ParenState &P = State.Stack.back(); int AdditionalIndent = - P.Indent - Previous.Children[0]->Level * Style.IndentWidth; + P.Indent.Total - Previous.Children[0]->Level * Style.IndentWidth; Penalty += BlockFormatter->format(Previous.Children, DryRun, AdditionalIndent, /*FixBadIndentation=*/true); diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp index 94ccf9eb7842a..805bb78f5c90e 100644 --- a/clang/lib/Format/WhitespaceManager.cpp +++ b/clang/lib/Format/WhitespaceManager.cpp @@ -35,13 +35,15 @@ WhitespaceManager::Change::Change(const FormatToken &Tok, bool CreateReplacement, SourceRange OriginalWhitespaceRange, int Spaces, unsigned StartOfTokenColumn, + unsigned IndentedFromColumn, unsigned NewlinesBefore, StringRef PreviousLinePostfix, StringRef CurrentLinePrefix, bool IsAligned, bool ContinuesPPDirective, bool IsInsideToken) : Tok(&Tok), CreateReplacement(CreateReplacement), OriginalWhitespaceRange(OriginalWhitespaceRange), - StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore), + StartOfTokenColumn(StartOfTokenColumn), + IndentedFromColumn(IndentedFromColumn), NewlinesBefore(NewlinesBefore), PreviousLinePostfix(PreviousLinePostfix), CurrentLinePrefix(CurrentLinePrefix), IsAligned(IsAligned), ContinuesPPDirective(ContinuesPPDirective), Spaces(Spaces), @@ -53,13 +55,15 @@ WhitespaceManager::Change::Change(const FormatToken &Tok, void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines, unsigned Spaces, unsigned StartOfTokenColumn, - bool IsAligned, bool InPPDirective) { + bool IsAligned, bool InPPDirective, + unsigned IndentedFromColumn) { if (Tok.Finalized || (Tok.MacroCtx && Tok.MacroCtx->Role == MR_ExpandedArg)) return; Tok.setDecision((Newlines > 0) ? FD_Break : FD_Continue); Changes.push_back(Change(Tok, /*CreateReplacement=*/true, Tok.WhitespaceRange, - Spaces, StartOfTokenColumn, Newlines, "", "", - IsAligned, InPPDirective && !Tok.IsFirst, + Spaces, StartOfTokenColumn, IndentedFromColumn, + Newlines, "", "", IsAligned, + InPPDirective && !Tok.IsFirst, /*IsInsideToken=*/false)); } @@ -67,11 +71,11 @@ void WhitespaceManager::addUntouchableToken(const FormatToken &Tok, bool InPPDirective) { if (Tok.Finalized || (Tok.MacroCtx && Tok.MacroCtx->Role == MR_ExpandedArg)) return; - Changes.push_back(Change(Tok, /*CreateReplacement=*/false, - Tok.WhitespaceRange, /*Spaces=*/0, - Tok.OriginalColumn, Tok.NewlinesBefore, "", "", - /*IsAligned=*/false, InPPDirective && !Tok.IsFirst, - /*IsInsideToken=*/false)); + Changes.push_back(Change( + Tok, /*CreateReplacement=*/false, Tok.WhitespaceRange, /*Spaces=*/0, + Tok.OriginalColumn, /*IndentedFromColumn=*/0, Tok.NewlinesBefore, "", "", + /*IsAligned=*/false, InPPDirective && !Tok.IsFirst, + /*IsInsideToken=*/false)); } llvm::Error @@ -95,7 +99,8 @@ void WhitespaceManager::replaceWhitespaceInToken( Changes.push_back( Change(Tok, /*CreateReplacement=*/true, SourceRange(Start, Start.getLocWithOffset(ReplaceChars)), Spaces, - std::max(0, Spaces), Newlines, PreviousPostfix, CurrentPrefix, + std::max(0, Spaces), /*IndentedFromColumn=*/0, Newlines, + PreviousPostfix, CurrentPrefix, /*IsAligned=*/true, InPPDirective && !Tok.IsFirst, /*IsInsideToken=*/true)); } @@ -287,6 +292,7 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End, unsigned Column, bool RightJustify, ArrayRef Matches, SmallVector &Changes) { + unsigned OriginalMatchColumn = 0; int Shift = 0; // Set when the shift is applied anywhere in the line. Cleared when the line // ends. @@ -330,21 +336,19 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End, // Keep track of the level that should not move with the aligned token. if (ScopeStack.size() == 1u && CurrentChange.NewlinesBefore != 0u && CurrentChange.indentAndNestingLevel() > ScopeStack[0] && - !CurrentChange.IsAligned) { + CurrentChange.IndentedFromColumn < OriginalMatchColumn) { ScopeStack.push_back(CurrentChange.indentAndNestingLevel()); } bool InsideNestedScope = !ScopeStack.empty() && - CurrentChange.indentAndNestingLevel() > ScopeStack[0]; - bool ContinuedStringLiteral = i > Start && - CurrentChange.Tok->is(tok::string_literal) && - Changes[i - 1].Tok->is(tok::string_literal); - bool SkipMatchCheck = InsideNestedScope || ContinuedStringLiteral; + (CurrentChange.indentAndNestingLevel() > ScopeStack[0] || + (CurrentChange.indentAndNestingLevel() == ScopeStack[0] && + CurrentChange.IndentedFromColumn >= OriginalMatchColumn)); if (CurrentChange.NewlinesBefore > 0) { LineShifted = false; - if (!SkipMatchCheck) + if (!InsideNestedScope) Shift = 0; } @@ -352,6 +356,7 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End, // spaces it has to be shifted, so the rest of the changes on the line are // shifted by the same amount if (!Matches.empty() && Matches[0] == i) { + OriginalMatchColumn = CurrentChange.StartOfTokenColumn; Shift = Column - (RightJustify ? CurrentChange.TokenLength : 0) - CurrentChange.StartOfTokenColumn; ScopeStack = {CurrentChange.indentAndNestingLevel()}; @@ -365,7 +370,7 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End, // not in a scope that should not move. if ((!Matches.empty() && Matches[0] == i) || (ScopeStack.size() == 1u && CurrentChange.NewlinesBefore > 0 && - (ContinuedStringLiteral || InsideNestedScope))) { + InsideNestedScope)) { LineShifted = true; CurrentChange.Spaces += Shift; } @@ -623,7 +628,7 @@ static unsigned AlignTokens(const FormatStyle &Style, F &&Matches, // int k = bar( | We still want to align the = | int k = bar( // argument1, | here, even if we can't move | argument1, // argument2); | the following lines. | argument2); - if (static_cast(Change.Spaces) < ChangeWidthStart) + if (Change.IndentedFromColumn < ChangeWidthStart) break; CurrentChangeWidthRight = Change.Spaces - ChangeWidthStart; } else { diff --git a/clang/lib/Format/WhitespaceManager.h b/clang/lib/Format/WhitespaceManager.h index 6d18db74cd2e4..3e6fa9dc32978 100644 --- a/clang/lib/Format/WhitespaceManager.h +++ b/clang/lib/Format/WhitespaceManager.h @@ -49,9 +49,15 @@ class WhitespaceManager { /// \p StartOfTokenColumn is the column at which the token will start after /// this replacement. It is needed for determining how \p Spaces is turned /// into tabs and spaces for some format styles. + /// + /// \p IndentedFromColumn is only used when the replacement starts a new + /// line. It should be the column that the position of the line is derived + /// from. It is used for determining what lines the alignment process should + /// move. void replaceWhitespace(FormatToken &Tok, unsigned Newlines, unsigned Spaces, unsigned StartOfTokenColumn, bool IsAligned = false, - bool InPPDirective = false); + bool InPPDirective = false, + unsigned IndentedFromColumn = 0); /// Adds information about an unchangeable token's whitespace. /// @@ -104,13 +110,15 @@ class WhitespaceManager { /// \p PreviousLinePostfix, \p NewlinesBefore line breaks, \p Spaces spaces /// and \p CurrentLinePrefix. /// - /// \p StartOfTokenColumn and \p InPPDirective will be used to lay out - /// trailing comments and escaped newlines. + /// \p StartOfTokenColumn and \p ContinuesPPDirective will be used to lay + /// out trailing comments and escaped newlines. \p IndentedFromColumn will + /// be used to continue aligned lines. Change(const FormatToken &Tok, bool CreateReplacement, SourceRange OriginalWhitespaceRange, int Spaces, - unsigned StartOfTokenColumn, unsigned NewlinesBefore, - StringRef PreviousLinePostfix, StringRef CurrentLinePrefix, - bool IsAligned, bool ContinuesPPDirective, bool IsInsideToken); + unsigned StartOfTokenColumn, unsigned IndentedFromColumn, + unsigned NewlinesBefore, StringRef PreviousLinePostfix, + StringRef CurrentLinePrefix, bool IsAligned, + bool ContinuesPPDirective, bool IsInsideToken); // The kind of the token whose whitespace this change replaces, or in which // this change inserts whitespace. @@ -123,6 +131,11 @@ class WhitespaceManager { // FormatToken around to query its information. SourceRange OriginalWhitespaceRange; unsigned StartOfTokenColumn; + // Only used when the token is at the start of a line. The column that the + // position of the line is derived from. The alignment procedure moves the + // line when it moves a token in the same unwrapped line that is to the left + // of said column. + unsigned IndentedFromColumn; unsigned NewlinesBefore; std::string PreviousLinePostfix; std::string CurrentLinePrefix; diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 81fa7d1d11aa4..0e4ef2a276adb 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -20689,6 +20689,16 @@ TEST_F(FormatTest, AlignWithLineBreaks) { "}", Style); + verifyFormat("auto someLongName = 3;\n" + "auto x = someLongExpression //\n" + " | ranges::views::values;", + Style); + verifyFormat( + "veryverylongvariablename = somethingelse;\n" + "shortervariablename = anotherverylonglonglongvariablename + //\n" + " somevariablethatwastoolongtofitonthesamerow;", + Style); + // clang-format off verifyFormat("void foo() {\n" " const int capacityBefore = Entries.capacity();\n" @@ -20762,6 +20772,42 @@ TEST_F(FormatTest, AlignWithLineBreaks) { Style); // clang-format on + // The start of the closure is indented from the start of the line. It should + // not move with the equal sign. + Style.ContinuationIndentWidth = 6; + Style.IndentWidth = 8; + Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.BeforeLambdaBody = true; + Style.BraceWrapping.IndentBraces = true; + Style.ColumnLimit = 32; + verifyFormat("auto aaaaaaaaaaa = {};\n" + "b = []() constexpr\n" + " -> aaaaaaaaaaaaaaaaaaaaaaa\n" + " {\n" + " return {}; //\n" + " };", + Style); + verifyFormat("auto aaaaaaaaaaaaaaaaaaaaa = {};\n" + "b = []()\n" + " {\n" + " return; //\n" + " };", + Style); + Style.ColumnLimit = 33; + verifyFormat("auto aaaaaaaaaaa = {};\n" + "b = []() constexpr\n" + " -> aaaaaaaaaaaaaaaaaaaaaaa\n" + " {\n" + " return {}; //\n" + " };", + Style); + verifyFormat("auto aaaaaaaaaaaaaaaaaaaaa = {};\n" + "b = []()\n" + " {\n" + " return; //\n" + " };", + Style); + Style = getLLVMStyleWithColumns(20); Style.AlignConsecutiveAssignments.Enabled = true; Style.IndentWidth = 4; @@ -20826,6 +20872,13 @@ TEST_F(FormatTest, AlignWithLineBreaks) { Style); Style.AlignConsecutiveAssignments.Enabled = true; + verifyFormat("float i2 = 0;\n" + "auto v = false ? type{}\n" + " : type{\n" + " 1,\n" + " };", + Style); + Style.ColumnLimit = 15; verifyFormat("int i1 = 1;\n" "k = bar(\n" @@ -20886,6 +20939,12 @@ TEST_F(FormatTest, AlignWithInitializerPeriods) { "});", Style); + verifyFormat("auto aaaaaaaaaaaaaaaaaaaaa = {};\n" + "auto b = {.a = {\n" + " .a = 0,\n" + " }};", + Style); + Style.AlignConsecutiveAssignments.Enabled = false; Style.AlignConsecutiveDeclarations.Enabled = true; verifyFormat("void foo3(void) {\n" diff --git a/clang/unittests/Format/FormatTestObjC.cpp b/clang/unittests/Format/FormatTestObjC.cpp index cf8143ace7b45..c685c5554f9b5 100644 --- a/clang/unittests/Format/FormatTestObjC.cpp +++ b/clang/unittests/Format/FormatTestObjC.cpp @@ -876,6 +876,32 @@ TEST_F(FormatTestObjC, FormatObjCMethodExpr) { verifyFormat("aaaaaa = [aa aa:aa\n" " aa:aa];"); + Style.AlignConsecutiveAssignments.Enabled = true; + // When the method name and parameters are on their own lines, their positions + // only depend on the continuation indentation configuration, not where the + // square bracket is. Thus they should not move with the square bracket in the + // alignment step. + verifyFormat("aaaaaa = [aa aa:aa\n" + " aa:aa];\n" + "a = [a //\n" + " aaaaaaa:aa];"); + verifyFormat("aaaaaa = [aa aa:aa\n" + " aa:aa];\n" + "aaaaa = [a //\n" + " a:aa\n" + " aaaaaaa:aa];"); + // When the method name is on the same line as the square bracket, the + // positions of the parameters depend on where the square bracket is. Thus + // they should move with the square bracket in the alignment step. + verifyFormat("aaaaa = [a aa:aa\n" + " aa:aa];\n" + "aaaaaa = [aa aa:aa\n" + " aa:aa];\n" + "aaaaa = [a aa:aa\n" + " aaaaaaaaaa:aa];"); + + Style.AlignConsecutiveAssignments.Enabled = false; + // Message receiver taking multiple lines. // Non-corner case. verifyFormat("[[object block:^{\n"