diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index c75684b3b5a7..10d07df90f30 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -372,28 +372,148 @@ private: class UnwrappedLineFormatter { public: - UnwrappedLineFormatter(ContinuationIndenter *Indenter, + UnwrappedLineFormatter(SourceManager &SourceMgr, + SmallVectorImpl &Ranges, + ContinuationIndenter *Indenter, WhitespaceManager *Whitespaces, - const FormatStyle &Style, const AnnotatedLine &Line) - : Indenter(Indenter), Whitespaces(Whitespaces), Style(Style), Line(Line), - Count(0) {} + const FormatStyle &Style) + : SourceMgr(SourceMgr), Ranges(Ranges), Indenter(Indenter), + Whitespaces(Whitespaces), Style(Style) {} - /// \brief Formats an \c UnwrappedLine and returns the penalty. + unsigned format(SmallVectorImpl &Lines, bool DryRun, + int AdditionalIndent = 0) { + assert(!Lines.empty()); + unsigned Penalty = 0; + std::vector IndentForLevel; + for (unsigned i = 0, e = Lines[0]->Level; i != e; ++i) + IndentForLevel.push_back(Style.IndentWidth * i + AdditionalIndent); + bool PreviousLineWasTouched = false; + const AnnotatedLine *PreviousLine = NULL; + bool FormatPPDirective = false; + for (SmallVectorImpl::iterator I = Lines.begin(), + E = Lines.end(); + I != E; ++I) { + const AnnotatedLine &TheLine = **I; + const FormatToken *FirstTok = TheLine.First; + int Offset = getIndentOffset(*FirstTok); + + // Check whether this line is part of a formatted preprocessor directive. + if (FirstTok->HasUnescapedNewline) + FormatPPDirective = false; + if (!FormatPPDirective && TheLine.InPPDirective && + (touchesLine(TheLine) || touchesPPDirective(I + 1, E))) + FormatPPDirective = true; + + // Determine indent and try to merge multiple unwrapped lines. + while (IndentForLevel.size() <= TheLine.Level) + IndentForLevel.push_back(-1); + IndentForLevel.resize(TheLine.Level + 1); + unsigned Indent = getIndent(IndentForLevel, TheLine.Level); + if (static_cast(Indent) + Offset >= 0) + Indent += Offset; + if (!DryRun) + tryFitMultipleLinesInOne(Indent, I, E); + + bool WasMoved = PreviousLineWasTouched && FirstTok->NewlinesBefore == 0; + if (TheLine.First->is(tok::eof)) { + if (PreviousLineWasTouched && !DryRun) { + unsigned Newlines = std::min(FirstTok->NewlinesBefore, 1u); + Whitespaces->replaceWhitespace(*TheLine.First, Newlines, + /*IndentLevel=*/0, /*Spaces=*/0, + /*TargetColumn=*/0); + } + } else if (TheLine.Type != LT_Invalid && + (WasMoved || FormatPPDirective || touchesLine(TheLine))) { + unsigned LevelIndent = + getIndent(IndentForLevel, TheLine.Level); + if (FirstTok->WhitespaceRange.isValid()) { + if (!DryRun) + formatFirstToken(*TheLine.First, PreviousLine, TheLine.Level, + Indent, TheLine.InPPDirective); + } else { + Indent = LevelIndent = FirstTok->OriginalColumn; + } + + // If everything fits on a single line, just put it there. + unsigned ColumnLimit = Style.ColumnLimit; + if (I + 1 != E) { + AnnotatedLine *NextLine = *(I + 1); + if (NextLine->InPPDirective && !NextLine->First->HasUnescapedNewline) + ColumnLimit = getColumnLimit(TheLine.InPPDirective); + } + + if (TheLine.Last->TotalLength + Indent <= ColumnLimit) { + LineState State = Indenter->getInitialState(Indent, &TheLine, DryRun); + while (State.NextToken != NULL) + Indenter->addTokenToState(State, /*Newline=*/false, DryRun); + } else if (Style.ColumnLimit == 0) { + NoColumnLimitFormatter Formatter(Indenter); + if (!DryRun) + Formatter.format(Indent, &TheLine); + } else { + Penalty += format(TheLine, Indent, DryRun); + } + + IndentForLevel[TheLine.Level] = LevelIndent; + PreviousLineWasTouched = true; + } else { + // Format the first token if necessary, and notify the WhitespaceManager + // about the unchanged whitespace. + for (FormatToken *Tok = TheLine.First; Tok != NULL; Tok = Tok->Next) { + if (Tok == TheLine.First && + (Tok->NewlinesBefore > 0 || Tok->IsFirst)) { + unsigned LevelIndent = Tok->OriginalColumn; + if (!DryRun) { + // Remove trailing whitespace of the previous line if it was + // touched. + if (PreviousLineWasTouched || touchesEmptyLineBefore(TheLine)) { + formatFirstToken(*Tok, PreviousLine, TheLine.Level, LevelIndent, + TheLine.InPPDirective); + } else { + Whitespaces->addUntouchableToken(*Tok, TheLine.InPPDirective); + } + } + + if (static_cast(LevelIndent) - Offset >= 0) + LevelIndent -= Offset; + if (Tok->isNot(tok::comment)) + IndentForLevel[TheLine.Level] = LevelIndent; + } else if (!DryRun) { + Whitespaces->addUntouchableToken(*Tok, TheLine.InPPDirective); + } + } + // If we did not reformat this unwrapped line, the column at the end of + // the last token is unchanged - thus, we can calculate the end of the + // last token. + PreviousLineWasTouched = false; + } + if (!DryRun) { + for (FormatToken *Tok = TheLine.First; Tok != NULL; Tok = Tok->Next) { + Tok->Finalized = true; + } + } + PreviousLine = *I; + } + return Penalty; + } + +private: + /// \brief Formats an \c AnnotatedLine and returns the penalty. /// /// If \p DryRun is \c false, directly applies the changes. - unsigned format(unsigned FirstIndent, bool DryRun = false) { + unsigned format(const AnnotatedLine &Line, unsigned FirstIndent, + bool DryRun) { LineState State = Indenter->getInitialState(FirstIndent, &Line, DryRun); // If the ObjC method declaration does not fit on a line, we should format // it with one arg per line. - if (Line.Type == LT_ObjCMethodDecl) + if (State.Line->Type == LT_ObjCMethodDecl) State.Stack.back().BreakBeforeParameter = true; // Find best solution in solution space. return analyzeSolutionSpace(State, DryRun); } -private: /// \brief An edge in the solution space from \c Previous->State to \c State, /// inserting a newline dependent on the \c NewLine. struct StateNode { @@ -419,6 +539,262 @@ private: typedef std::priority_queue, std::greater > QueueType; + /// \brief Get the offset of the line relatively to the level. + /// + /// For example, 'public:' labels in classes are offset by 1 or 2 + /// characters to the left from their level. + int getIndentOffset(const FormatToken &RootToken) { + if (RootToken.isAccessSpecifier(false) || RootToken.isObjCAccessSpecifier()) + return Style.AccessModifierOffset; + return 0; + } + + /// \brief Add a new line and the required indent before the first Token + /// of the \c UnwrappedLine if there was no structural parsing error. + void formatFirstToken(FormatToken &RootToken, + const AnnotatedLine *PreviousLine, unsigned IndentLevel, + unsigned Indent, bool InPPDirective) { + unsigned Newlines = + std::min(RootToken.NewlinesBefore, Style.MaxEmptyLinesToKeep + 1); + // Remove empty lines before "}" where applicable. + if (RootToken.is(tok::r_brace) && + (!RootToken.Next || + (RootToken.Next->is(tok::semi) && !RootToken.Next->Next))) + Newlines = std::min(Newlines, 1u); + if (Newlines == 0 && !RootToken.IsFirst) + Newlines = 1; + + // Insert extra new line before access specifiers. + if (PreviousLine && PreviousLine->Last->isOneOf(tok::semi, tok::r_brace) && + RootToken.isAccessSpecifier() && RootToken.NewlinesBefore == 1) + ++Newlines; + + // Remove empty lines after access specifiers. + if (PreviousLine && PreviousLine->First->isAccessSpecifier()) + Newlines = std::min(1u, Newlines); + + Whitespaces->replaceWhitespace( + RootToken, Newlines, IndentLevel, Indent, Indent, + InPPDirective && !RootToken.HasUnescapedNewline); + } + + /// \brief Get the indent of \p Level from \p IndentForLevel. + /// + /// \p IndentForLevel must contain the indent for the level \c l + /// at \p IndentForLevel[l], or a value < 0 if the indent for + /// that level is unknown. + unsigned getIndent(const std::vector IndentForLevel, unsigned Level) { + if (IndentForLevel[Level] != -1) + return IndentForLevel[Level]; + if (Level == 0) + return 0; + return getIndent(IndentForLevel, Level - 1) + Style.IndentWidth; + } + + /// \brief Tries to merge lines into one. + /// + /// This will change \c Line and \c AnnotatedLine to contain the merged line, + /// if possible; note that \c I will be incremented when lines are merged. + void tryFitMultipleLinesInOne(unsigned Indent, + SmallVectorImpl::iterator &I, + SmallVectorImpl::iterator E) { + // We can never merge stuff if there are trailing line comments. + AnnotatedLine *TheLine = *I; + if (TheLine->Last->Type == TT_LineComment) + return; + + if (Indent > Style.ColumnLimit) + return; + + unsigned Limit = Style.ColumnLimit - Indent; + // If we already exceed the column limit, we set 'Limit' to 0. The different + // tryMerge..() functions can then decide whether to still do merging. + Limit = TheLine->Last->TotalLength > Limit + ? 0 + : Limit - TheLine->Last->TotalLength; + + if (I + 1 == E || (*(I + 1))->Type == LT_Invalid) + return; + + if (TheLine->Last->is(tok::l_brace)) { + tryMergeSimpleBlock(I, E, Limit); + } else if (Style.AllowShortIfStatementsOnASingleLine && + TheLine->First->is(tok::kw_if)) { + tryMergeSimpleControlStatement(I, E, Limit); + } else if (Style.AllowShortLoopsOnASingleLine && + TheLine->First->isOneOf(tok::kw_for, tok::kw_while)) { + tryMergeSimpleControlStatement(I, E, Limit); + } else if (TheLine->InPPDirective && (TheLine->First->HasUnescapedNewline || + TheLine->First->IsFirst)) { + tryMergeSimplePPDirective(I, E, Limit); + } + } + + void tryMergeSimplePPDirective(SmallVectorImpl::iterator &I, + SmallVectorImpl::iterator E, + unsigned Limit) { + if (Limit == 0) + return; + AnnotatedLine &Line = **I; + if (!(*(I + 1))->InPPDirective || (*(I + 1))->First->HasUnescapedNewline) + return; + if (I + 2 != E && (*(I + 2))->InPPDirective && + !(*(I + 2))->First->HasUnescapedNewline) + return; + if (1 + (*(I + 1))->Last->TotalLength > Limit) + return; + join(Line, **(++I)); + } + + void + tryMergeSimpleControlStatement(SmallVectorImpl::iterator &I, + SmallVectorImpl::iterator E, + unsigned Limit) { + if (Limit == 0) + return; + if (Style.BreakBeforeBraces == FormatStyle::BS_Allman && + (*(I + 1))->First->is(tok::l_brace)) + return; + if ((*(I + 1))->InPPDirective != (*I)->InPPDirective || + ((*(I + 1))->InPPDirective && (*(I + 1))->First->HasUnescapedNewline)) + return; + AnnotatedLine &Line = **I; + if (Line.Last->isNot(tok::r_paren)) + return; + if (1 + (*(I + 1))->Last->TotalLength > Limit) + return; + if ((*(I + 1))->First->isOneOf(tok::semi, tok::kw_if, tok::kw_for, + tok::kw_while) || + (*(I + 1))->First->Type == TT_LineComment) + return; + // Only inline simple if's (no nested if or else). + if (I + 2 != E && Line.First->is(tok::kw_if) && + (*(I + 2))->First->is(tok::kw_else)) + return; + join(Line, **(++I)); + } + + void tryMergeSimpleBlock(SmallVectorImpl::iterator &I, + SmallVectorImpl::iterator E, + unsigned Limit) { + // No merging if the brace already is on the next line. + if (Style.BreakBeforeBraces != FormatStyle::BS_Attach) + return; + + // First, check that the current line allows merging. This is the case if + // we're not in a control flow statement and the last token is an opening + // brace. + AnnotatedLine &Line = **I; + if (Line.First->isOneOf(tok::kw_if, tok::kw_while, tok::kw_do, tok::r_brace, + tok::kw_else, tok::kw_try, tok::kw_catch, + tok::kw_for, + // This gets rid of all ObjC @ keywords and methods. + tok::at, tok::minus, tok::plus)) + return; + + FormatToken *Tok = (*(I + 1))->First; + if (Tok->is(tok::r_brace) && !Tok->MustBreakBefore && + (Tok->getNextNonComment() == NULL || + Tok->getNextNonComment()->is(tok::semi))) { + // We merge empty blocks even if the line exceeds the column limit. + Tok->SpacesRequiredBefore = 0; + Tok->CanBreakBefore = true; + join(Line, **(I + 1)); + I += 1; + } else if (Limit != 0 && Line.First->isNot(tok::kw_namespace)) { + // Check that we still have three lines and they fit into the limit. + if (I + 2 == E || (*(I + 2))->Type == LT_Invalid || + !nextTwoLinesFitInto(I, Limit)) + return; + + // Second, check that the next line does not contain any braces - if it + // does, readability declines when putting it into a single line. + if ((*(I + 1))->Last->Type == TT_LineComment || Tok->MustBreakBefore) + return; + do { + if (Tok->isOneOf(tok::l_brace, tok::r_brace)) + return; + Tok = Tok->Next; + } while (Tok != NULL); + + // Last, check that the third line contains a single closing brace. + Tok = (*(I + 2))->First; + if (Tok->getNextNonComment() != NULL || Tok->isNot(tok::r_brace) || + Tok->MustBreakBefore) + return; + + join(Line, **(I + 1)); + join(Line, **(I + 2)); + I += 2; + } + } + + bool nextTwoLinesFitInto(SmallVectorImpl::iterator I, + unsigned Limit) { + return 1 + (*(I + 1))->Last->TotalLength + 1 + + (*(I + 2))->Last->TotalLength <= + Limit; + } + + void join(AnnotatedLine &A, const AnnotatedLine &B) { + assert(!A.Last->Next); + assert(!B.First->Previous); + A.Last->Next = B.First; + B.First->Previous = A.Last; + unsigned LengthA = A.Last->TotalLength + B.First->SpacesRequiredBefore; + for (FormatToken *Tok = B.First; Tok; Tok = Tok->Next) { + Tok->TotalLength += LengthA; + A.Last = Tok; + } + } + + unsigned getColumnLimit(bool InPPDirective) const { + // In preprocessor directives reserve two chars for trailing " \" + return Style.ColumnLimit - (InPPDirective ? 2 : 0); + } + + bool touchesRanges(const CharSourceRange &Range) { + for (SmallVectorImpl::const_iterator I = Ranges.begin(), + E = Ranges.end(); + I != E; ++I) { + if (!SourceMgr.isBeforeInTranslationUnit(Range.getEnd(), I->getBegin()) && + !SourceMgr.isBeforeInTranslationUnit(I->getEnd(), Range.getBegin())) + return true; + } + return false; + } + + bool touchesLine(const AnnotatedLine &TheLine) { + const FormatToken *First = TheLine.First; + const FormatToken *Last = TheLine.Last; + CharSourceRange LineRange = CharSourceRange::getCharRange( + First->WhitespaceRange.getBegin().getLocWithOffset( + First->LastNewlineOffset), + Last->getStartOfNonWhitespace().getLocWithOffset( + Last->TokenText.size() - 1)); + return touchesRanges(LineRange); + } + + bool touchesPPDirective(SmallVectorImpl::const_iterator I, + SmallVectorImpl::const_iterator E) { + for (; I != E; ++I) { + if ((*I)->First->HasUnescapedNewline) + return false; + if (touchesLine(**I)) + return true; + } + return false; + } + + bool touchesEmptyLineBefore(const AnnotatedLine &TheLine) { + const FormatToken *First = TheLine.First; + CharSourceRange LineRange = CharSourceRange::getCharRange( + First->WhitespaceRange.getBegin(), + First->WhitespaceRange.getBegin().getLocWithOffset( + First->LastNewlineOffset)); + return touchesRanges(LineRange); + } + /// \brief Analyze the entire solution space starting from \p InitialState. /// /// This implements a variant of Dijkstra's algorithm on the graph that spans @@ -430,6 +806,11 @@ private: unsigned analyzeSolutionSpace(LineState &InitialState, bool DryRun = false) { std::set Seen; + // Increasing count of \c StateNode items we have created. This is used to + // create a deterministic order independent of the container. + unsigned Count = 0; + QueueType Queue; + // Insert start element into queue. StateNode *Node = new (Allocator.Allocate()) StateNode(InitialState, false, NULL); @@ -459,9 +840,9 @@ private: FormatDecision LastFormat = Node->State.NextToken->Decision; if (LastFormat == FD_Unformatted || LastFormat == FD_Continue) - addNextStateToQueue(Penalty, Node, /*NewLine=*/false); + addNextStateToQueue(Penalty, Node, /*NewLine=*/false, &Count, &Queue); if (LastFormat == FD_Unformatted || LastFormat == FD_Break) - addNextStateToQueue(Penalty, Node, /*NewLine=*/true); + addNextStateToQueue(Penalty, Node, /*NewLine=*/true, &Count, &Queue); } if (Queue.empty()) { @@ -509,7 +890,7 @@ private: /// Assume the current state is \p PreviousNode and has been reached with a /// penalty of \p Penalty. Insert a line break if \p NewLine is \c true. void addNextStateToQueue(unsigned Penalty, StateNode *PreviousNode, - bool NewLine) { + bool NewLine, unsigned *Count, QueueType *Queue) { if (NewLine && !Indenter->canBreak(PreviousNode->State)) return; if (!NewLine && Indenter->mustBreak(PreviousNode->State)) @@ -522,8 +903,8 @@ private: Penalty += Indenter->addTokenToState(Node->State, NewLine, true); - Queue.push(QueueItem(OrderedPenalty(Penalty, Count), Node)); - ++Count; + Queue->push(QueueItem(OrderedPenalty(Penalty, *Count), Node)); + ++(*Count); } /// \brief If the \p State's next token is an r_brace closing a nested block, @@ -548,7 +929,7 @@ private: /// break or don't break. bool formatChildren(LineState &State, bool NewLine, bool DryRun, unsigned &Penalty) { - const FormatToken &Previous = *State.NextToken->Previous; + FormatToken &Previous = *State.NextToken->Previous; const FormatToken *LBrace = State.NextToken->getPreviousNonComment(); if (!LBrace || LBrace->isNot(tok::l_brace) || LBrace->BlockKind != BK_Block || Previous.Children.size() == 0) @@ -557,29 +938,15 @@ private: return true; if (NewLine) { - unsigned ParentIndent = State.Stack.back().Indent; - for (SmallVector::const_iterator - I = Previous.Children.begin(), - E = Previous.Children.end(); - I != E; ++I) { - unsigned Indent = - ParentIndent + ((*I)->Level - Line.Level - 1) * Style.IndentWidth; - if (!DryRun) { - unsigned Newlines = std::min((*I)->First->NewlinesBefore, - Style.MaxEmptyLinesToKeep + 1); - Newlines = std::max(1u, Newlines); - Whitespaces->replaceWhitespace( - *(*I)->First, Newlines, (*I)->Level, /*Spaces=*/Indent, - /*StartOfTokenColumn=*/Indent, Line.InPPDirective); - } - UnwrappedLineFormatter Formatter(Indenter, Whitespaces, Style, **I); - Penalty += Formatter.format(Indent, DryRun); - } + int AdditionalIndent = State.Stack.back().Indent - + Previous.Children[0]->Level * Style.IndentWidth; + Penalty += format(Previous.Children, DryRun, AdditionalIndent); return true; } + // Cannot merge multiple statements into a single line. if (Previous.Children.size() > 1) - return false; // Cannot merge multiple statements into a single line. + return false; // We can't put the closing "}" on a line with a trailing comment. if (Previous.Children[0]->Last->isTrailingComment()) @@ -590,25 +957,20 @@ private: *Previous.Children[0]->First, /*Newlines=*/0, /*IndentLevel=*/0, /*Spaces=*/1, /*StartOfTokenColumn=*/State.Column, State.Line->InPPDirective); - UnwrappedLineFormatter Formatter(Indenter, Whitespaces, Style, - *Previous.Children[0]); - Penalty += Formatter.format(State.Column + 1, DryRun); } + Penalty += format(*Previous.Children[0], State.Column + 1, DryRun); State.Column += 1 + Previous.Children[0]->Last->TotalLength; return true; } + SourceManager &SourceMgr; + SmallVectorImpl &Ranges; ContinuationIndenter *Indenter; WhitespaceManager *Whitespaces; FormatStyle Style; - const AnnotatedLine &Line; llvm::SpecificBumpPtrAllocator Allocator; - QueueType Queue; - // Increasing count of \c StateNode items we have created. This is used - // to create a deterministic order independent of the container. - unsigned Count; }; class FormatTokenLexer { @@ -831,7 +1193,7 @@ public: const std::vector &Ranges) : Style(Style), Lex(Lex), SourceMgr(SourceMgr), Whitespaces(SourceMgr, Style, inputUsesCRLF(Lex.getBuffer())), - Ranges(Ranges), UnwrappedLines(1), + Ranges(Ranges.begin(), Ranges.end()), UnwrappedLines(1), Encoding(encoding::detectEncoding(Lex.getBuffer())) { DEBUG(llvm::dbgs() << "File encoding: " << (Encoding == encoding::Encoding_UTF8 ? "UTF8" @@ -884,111 +1246,11 @@ public: } Annotator.setCommentLineLevels(AnnotatedLines); - - std::vector IndentForLevel; - bool PreviousLineWasTouched = false; - const AnnotatedLine *PreviousLine = NULL; - bool FormatPPDirective = false; - for (SmallVectorImpl::iterator I = AnnotatedLines.begin(), - E = AnnotatedLines.end(); - I != E; ++I) { - const AnnotatedLine &TheLine = **I; - const FormatToken *FirstTok = TheLine.First; - int Offset = getIndentOffset(*TheLine.First); - - // Check whether this line is part of a formatted preprocessor directive. - if (FirstTok->HasUnescapedNewline) - FormatPPDirective = false; - if (!FormatPPDirective && TheLine.InPPDirective && - (touchesLine(TheLine) || touchesPPDirective(I + 1, E))) - FormatPPDirective = true; - - // Determine indent and try to merge multiple unwrapped lines. - while (IndentForLevel.size() <= TheLine.Level) - IndentForLevel.push_back(-1); - IndentForLevel.resize(TheLine.Level + 1); - unsigned Indent = getIndent(IndentForLevel, TheLine.Level); - if (static_cast(Indent) + Offset >= 0) - Indent += Offset; - tryFitMultipleLinesInOne(Indent, I, E); - - bool WasMoved = PreviousLineWasTouched && FirstTok->NewlinesBefore == 0; - if (TheLine.First->is(tok::eof)) { - if (PreviousLineWasTouched) { - unsigned Newlines = std::min(FirstTok->NewlinesBefore, 1u); - Whitespaces.replaceWhitespace(*TheLine.First, Newlines, - /*IndentLevel=*/0, /*Spaces=*/0, - /*TargetColumn=*/0); - } - } else if (TheLine.Type != LT_Invalid && - (WasMoved || FormatPPDirective || touchesLine(TheLine))) { - unsigned LevelIndent = getIndent(IndentForLevel, TheLine.Level); - if (FirstTok->WhitespaceRange.isValid()) { - formatFirstToken(*TheLine.First, PreviousLine, TheLine.Level, Indent, - TheLine.InPPDirective); - } else { - Indent = LevelIndent = FirstTok->OriginalColumn; - } - ContinuationIndenter Indenter(Style, SourceMgr, Whitespaces, Encoding, - BinPackInconclusiveFunctions); - - // If everything fits on a single line, just put it there. - unsigned ColumnLimit = Style.ColumnLimit; - AnnotatedLine *NextLine = *(I + 1); - if ((I + 1) != E && NextLine->InPPDirective && - !NextLine->First->HasUnescapedNewline) - ColumnLimit = getColumnLimit(TheLine.InPPDirective); - - if (TheLine.Last->TotalLength + Indent <= ColumnLimit) { - LineState State = - Indenter.getInitialState(Indent, &TheLine, /*DryRun=*/false); - while (State.NextToken != NULL) - Indenter.addTokenToState(State, false, false); - } else if (Style.ColumnLimit == 0) { - NoColumnLimitFormatter Formatter(&Indenter); - Formatter.format(Indent, &TheLine); - } else { - UnwrappedLineFormatter Formatter(&Indenter, &Whitespaces, Style, - TheLine); - Formatter.format(Indent); - } - - IndentForLevel[TheLine.Level] = LevelIndent; - PreviousLineWasTouched = true; - } else { - // Format the first token if necessary, and notify the WhitespaceManager - // about the unchanged whitespace. - for (FormatToken *Tok = TheLine.First; Tok != NULL; Tok = Tok->Next) { - if (Tok == TheLine.First && - (Tok->NewlinesBefore > 0 || Tok->IsFirst)) { - unsigned LevelIndent = Tok->OriginalColumn; - // Remove trailing whitespace of the previous line if it was - // touched. - if (PreviousLineWasTouched || touchesEmptyLineBefore(TheLine)) { - formatFirstToken(*Tok, PreviousLine, TheLine.Level, LevelIndent, - TheLine.InPPDirective); - } else { - Whitespaces.addUntouchableToken(*Tok, TheLine.InPPDirective); - } - - if (static_cast(LevelIndent) - Offset >= 0) - LevelIndent -= Offset; - if (Tok->isNot(tok::comment)) - IndentForLevel[TheLine.Level] = LevelIndent; - } else { - Whitespaces.addUntouchableToken(*Tok, TheLine.InPPDirective); - } - } - // If we did not reformat this unwrapped line, the column at the end of - // the last token is unchanged - thus, we can calculate the end of the - // last token. - PreviousLineWasTouched = false; - } - for (FormatToken *Tok = TheLine.First; Tok != NULL; Tok = Tok->Next) { - Tok->Finalized = true; - } - PreviousLine = *I; - } + ContinuationIndenter Indenter(Style, SourceMgr, Whitespaces, Encoding, + BinPackInconclusiveFunctions); + UnwrappedLineFormatter Formatter(SourceMgr, Ranges, &Indenter, &Whitespaces, + Style); + Formatter.format(AnnotatedLines, /*DryRun=*/false); return Whitespaces.generateReplacements(); } @@ -1051,228 +1313,6 @@ private: HasBinPackedFunction || !HasOnePerLineFunction; } - /// \brief Get the indent of \p Level from \p IndentForLevel. - /// - /// \p IndentForLevel must contain the indent for the level \c l - /// at \p IndentForLevel[l], or a value < 0 if the indent for - /// that level is unknown. - unsigned getIndent(const std::vector IndentForLevel, unsigned Level) { - if (IndentForLevel[Level] != -1) - return IndentForLevel[Level]; - if (Level == 0) - return 0; - return getIndent(IndentForLevel, Level - 1) + Style.IndentWidth; - } - - /// \brief Get the offset of the line relatively to the level. - /// - /// For example, 'public:' labels in classes are offset by 1 or 2 - /// characters to the left from their level. - int getIndentOffset(const FormatToken &RootToken) { - if (RootToken.isAccessSpecifier(false) || RootToken.isObjCAccessSpecifier()) - return Style.AccessModifierOffset; - return 0; - } - - /// \brief Tries to merge lines into one. - /// - /// This will change \c Line and \c AnnotatedLine to contain the merged line, - /// if possible; note that \c I will be incremented when lines are merged. - void tryFitMultipleLinesInOne(unsigned Indent, - SmallVectorImpl::iterator &I, - SmallVectorImpl::iterator E) { - // We can never merge stuff if there are trailing line comments. - AnnotatedLine *TheLine = *I; - if (TheLine->Last->Type == TT_LineComment) - return; - - if (Indent > Style.ColumnLimit) - return; - - unsigned Limit = Style.ColumnLimit - Indent; - // If we already exceed the column limit, we set 'Limit' to 0. The different - // tryMerge..() functions can then decide whether to still do merging. - Limit = TheLine->Last->TotalLength > Limit - ? 0 - : Limit - TheLine->Last->TotalLength; - - if (I + 1 == E || (*(I + 1))->Type == LT_Invalid) - return; - - if (TheLine->Last->is(tok::l_brace)) { - tryMergeSimpleBlock(I, E, Limit); - } else if (Style.AllowShortIfStatementsOnASingleLine && - TheLine->First->is(tok::kw_if)) { - tryMergeSimpleControlStatement(I, E, Limit); - } else if (Style.AllowShortLoopsOnASingleLine && - TheLine->First->isOneOf(tok::kw_for, tok::kw_while)) { - tryMergeSimpleControlStatement(I, E, Limit); - } else if (TheLine->InPPDirective && (TheLine->First->HasUnescapedNewline || - TheLine->First->IsFirst)) { - tryMergeSimplePPDirective(I, E, Limit); - } - } - - void tryMergeSimplePPDirective(SmallVectorImpl::iterator &I, - SmallVectorImpl::iterator E, - unsigned Limit) { - if (Limit == 0) - return; - AnnotatedLine &Line = **I; - if (!(*(I + 1))->InPPDirective || (*(I + 1))->First->HasUnescapedNewline) - return; - if (I + 2 != E && (*(I + 2))->InPPDirective && - !(*(I + 2))->First->HasUnescapedNewline) - return; - if (1 + (*(I + 1))->Last->TotalLength > Limit) - return; - join(Line, **(++I)); - } - - void - tryMergeSimpleControlStatement(SmallVectorImpl::iterator &I, - SmallVectorImpl::iterator E, - unsigned Limit) { - if (Limit == 0) - return; - if (Style.BreakBeforeBraces == FormatStyle::BS_Allman && - (*(I + 1))->First->is(tok::l_brace)) - return; - if ((*(I + 1))->InPPDirective != (*I)->InPPDirective || - ((*(I + 1))->InPPDirective && (*(I + 1))->First->HasUnescapedNewline)) - return; - AnnotatedLine &Line = **I; - if (Line.Last->isNot(tok::r_paren)) - return; - if (1 + (*(I + 1))->Last->TotalLength > Limit) - return; - if ((*(I + 1))->First->isOneOf(tok::semi, tok::kw_if, tok::kw_for, - tok::kw_while) || - (*(I + 1))->First->Type == TT_LineComment) - return; - // Only inline simple if's (no nested if or else). - if (I + 2 != E && Line.First->is(tok::kw_if) && - (*(I + 2))->First->is(tok::kw_else)) - return; - join(Line, **(++I)); - } - - void tryMergeSimpleBlock(SmallVectorImpl::iterator &I, - SmallVectorImpl::iterator E, - unsigned Limit) { - // No merging if the brace already is on the next line. - if (Style.BreakBeforeBraces != FormatStyle::BS_Attach) - return; - - // First, check that the current line allows merging. This is the case if - // we're not in a control flow statement and the last token is an opening - // brace. - AnnotatedLine &Line = **I; - if (Line.First->isOneOf(tok::kw_if, tok::kw_while, tok::kw_do, tok::r_brace, - tok::kw_else, tok::kw_try, tok::kw_catch, - tok::kw_for, - // This gets rid of all ObjC @ keywords and methods. - tok::at, tok::minus, tok::plus)) - return; - - FormatToken *Tok = (*(I + 1))->First; - if (Tok->is(tok::r_brace) && !Tok->MustBreakBefore && - (Tok->getNextNonComment() == NULL || - Tok->getNextNonComment()->is(tok::semi))) { - // We merge empty blocks even if the line exceeds the column limit. - Tok->SpacesRequiredBefore = 0; - Tok->CanBreakBefore = true; - join(Line, **(I + 1)); - I += 1; - } else if (Limit != 0 && Line.First->isNot(tok::kw_namespace)) { - // Check that we still have three lines and they fit into the limit. - if (I + 2 == E || (*(I + 2))->Type == LT_Invalid || - !nextTwoLinesFitInto(I, Limit)) - return; - - // Second, check that the next line does not contain any braces - if it - // does, readability declines when putting it into a single line. - if ((*(I + 1))->Last->Type == TT_LineComment || Tok->MustBreakBefore) - return; - do { - if (Tok->isOneOf(tok::l_brace, tok::r_brace)) - return; - Tok = Tok->Next; - } while (Tok != NULL); - - // Last, check that the third line contains a single closing brace. - Tok = (*(I + 2))->First; - if (Tok->getNextNonComment() != NULL || Tok->isNot(tok::r_brace) || - Tok->MustBreakBefore) - return; - - join(Line, **(I + 1)); - join(Line, **(I + 2)); - I += 2; - } - } - - bool nextTwoLinesFitInto(SmallVectorImpl::iterator I, - unsigned Limit) { - return 1 + (*(I + 1))->Last->TotalLength + 1 + - (*(I + 2))->Last->TotalLength <= - Limit; - } - - void join(AnnotatedLine &A, const AnnotatedLine &B) { - assert(!A.Last->Next); - assert(!B.First->Previous); - A.Last->Next = B.First; - B.First->Previous = A.Last; - unsigned LengthA = A.Last->TotalLength + B.First->SpacesRequiredBefore; - for (FormatToken *Tok = B.First; Tok; Tok = Tok->Next) { - Tok->TotalLength += LengthA; - A.Last = Tok; - } - } - - bool touchesRanges(const CharSourceRange &Range) { - for (unsigned i = 0, e = Ranges.size(); i != e; ++i) { - if (!SourceMgr.isBeforeInTranslationUnit(Range.getEnd(), - Ranges[i].getBegin()) && - !SourceMgr.isBeforeInTranslationUnit(Ranges[i].getEnd(), - Range.getBegin())) - return true; - } - return false; - } - - bool touchesLine(const AnnotatedLine &TheLine) { - const FormatToken *First = TheLine.First; - const FormatToken *Last = TheLine.Last; - CharSourceRange LineRange = CharSourceRange::getCharRange( - First->WhitespaceRange.getBegin().getLocWithOffset( - First->LastNewlineOffset), - Last->getStartOfNonWhitespace().getLocWithOffset( - Last->TokenText.size() - 1)); - return touchesRanges(LineRange); - } - - bool touchesPPDirective(SmallVectorImpl::iterator I, - SmallVectorImpl::iterator E) { - for (; I != E; ++I) { - if ((*I)->First->HasUnescapedNewline) - return false; - if (touchesLine(**I)) - return true; - } - return false; - } - - bool touchesEmptyLineBefore(const AnnotatedLine &TheLine) { - const FormatToken *First = TheLine.First; - CharSourceRange LineRange = CharSourceRange::getCharRange( - First->WhitespaceRange.getBegin(), - First->WhitespaceRange.getBegin().getLocWithOffset( - First->LastNewlineOffset)); - return touchesRanges(LineRange); - } - virtual void consumeUnwrappedLine(const UnwrappedLine &TheLine) { assert(!UnwrappedLines.empty()); UnwrappedLines.back().push_back(TheLine); @@ -1282,45 +1322,11 @@ private: UnwrappedLines.push_back(SmallVector()); } - /// \brief Add a new line and the required indent before the first Token - /// of the \c UnwrappedLine if there was no structural parsing error. - void formatFirstToken(FormatToken &RootToken, - const AnnotatedLine *PreviousLine, unsigned IndentLevel, - unsigned Indent, bool InPPDirective) { - unsigned Newlines = - std::min(RootToken.NewlinesBefore, Style.MaxEmptyLinesToKeep + 1); - // Remove empty lines before "}" where applicable. - if (RootToken.is(tok::r_brace) && - (!RootToken.Next || - (RootToken.Next->is(tok::semi) && !RootToken.Next->Next))) - Newlines = std::min(Newlines, 1u); - if (Newlines == 0 && !RootToken.IsFirst) - Newlines = 1; - - // Insert extra new line before access specifiers. - if (PreviousLine && PreviousLine->Last->isOneOf(tok::semi, tok::r_brace) && - RootToken.isAccessSpecifier() && RootToken.NewlinesBefore == 1) - ++Newlines; - - // Remove empty lines after access specifiers. - if (PreviousLine && PreviousLine->First->isAccessSpecifier()) - Newlines = std::min(1u, Newlines); - - Whitespaces.replaceWhitespace( - RootToken, Newlines, IndentLevel, Indent, Indent, - InPPDirective && !RootToken.HasUnescapedNewline); - } - - unsigned getColumnLimit(bool InPPDirective) const { - // In preprocessor directives reserve two chars for trailing " \" - return Style.ColumnLimit - (InPPDirective ? 2 : 0); - } - FormatStyle Style; Lexer &Lex; SourceManager &SourceMgr; WhitespaceManager Whitespaces; - std::vector Ranges; + SmallVector Ranges; SmallVector, 2> UnwrappedLines; encoding::Encoding Encoding; diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index ac45859a5ae8..13d3facb079c 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -1013,22 +1013,21 @@ private: void TokenAnnotator::setCommentLineLevels(SmallVectorImpl &Lines) { - if (Lines.empty()) - return; - const AnnotatedLine *NextNonCommentLine = NULL; - for (unsigned i = Lines.size() - 1; i > 0; --i) { - if (NextNonCommentLine && Lines[i]->First->is(tok::comment) && - !Lines[i]->First->Next) - Lines[i]->Level = NextNonCommentLine->Level; + for (SmallVectorImpl::reverse_iterator I = Lines.rbegin(), + E = Lines.rend(); + I != E; ++I) { + if (NextNonCommentLine && (*I)->First->is(tok::comment) && + (*I)->First->Next == NULL) + (*I)->Level = NextNonCommentLine->Level; else - NextNonCommentLine = - Lines[i]->First->isNot(tok::r_brace) ? Lines[i] : NULL; + NextNonCommentLine = (*I)->First->isNot(tok::r_brace) ? (*I) : NULL; + + setCommentLineLevels((*I)->Children); } } void TokenAnnotator::annotate(AnnotatedLine &Line) { - setCommentLineLevels(Line.Children); for (SmallVectorImpl::iterator I = Line.Children.begin(), E = Line.Children.end(); I != E; ++I) { diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 5852d1c0ed41..fc6ef2bb5ecc 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -2447,6 +2447,45 @@ TEST_F(FormatTest, LayoutNestedBlocks) { " // comment\n" " int j;\n" "});")); + + verifyFormat("DEBUG({\n" + " if (a)\n" + " return;\n" + "});"); + verifyGoogleFormat("DEBUG({\n" + " if (a) return;\n" + "});"); + FormatStyle Style = getGoogleStyle(); + Style.ColumnLimit = 45; + verifyFormat("Debug(aaaaa, {\n" + " if (aaaaaaaaaaaaaaaaaaaaaaaa)\n" + " return;\n" + " },\n" + " a);", Style); + + EXPECT_EQ("Debug({\n" + " if (aaaaaaaaaaaaaaaaaaaaaaaa)\n" + " return;\n" + " },\n" + " a);", + format("Debug({\n" + " if (aaaaaaaaaaaaaaaaaaaaaaaa)\n" + " return;\n" + " },\n" + " a);", + 50, 1, getLLVMStyle())); +} + +TEST_F(FormatTest, IndividualStatementsOfNestedBlocks) { + EXPECT_EQ("DEBUG({\n" + " int i;\n" + " int j;\n" + "});", + format("DEBUG( {\n" + " int i;\n" + " int j;\n" + "} ) ;", + 40, 1, getLLVMStyle())); } TEST_F(FormatTest, PutEmptyBlocksIntoOneLine) {