diff options
Diffstat (limited to 'lib/Format')
-rw-r--r-- | lib/Format/BreakableToken.cpp | 14 | ||||
-rw-r--r-- | lib/Format/ContinuationIndenter.cpp | 40 | ||||
-rw-r--r-- | lib/Format/ContinuationIndenter.h | 81 | ||||
-rw-r--r-- | lib/Format/Format.cpp | 17 | ||||
-rw-r--r-- | lib/Format/FormatToken.cpp | 7 | ||||
-rw-r--r-- | lib/Format/FormatToken.h | 18 | ||||
-rw-r--r-- | lib/Format/TokenAnnotator.cpp | 100 | ||||
-rw-r--r-- | lib/Format/TokenAnnotator.h | 20 | ||||
-rw-r--r-- | lib/Format/UnwrappedLineFormatter.cpp | 25 | ||||
-rw-r--r-- | lib/Format/UnwrappedLineFormatter.h | 3 | ||||
-rw-r--r-- | lib/Format/UnwrappedLineParser.cpp | 54 | ||||
-rw-r--r-- | lib/Format/WhitespaceManager.h | 6 |
12 files changed, 220 insertions, 165 deletions
diff --git a/lib/Format/BreakableToken.cpp b/lib/Format/BreakableToken.cpp index e3e162d..36a8c4d 100644 --- a/lib/Format/BreakableToken.cpp +++ b/lib/Format/BreakableToken.cpp @@ -183,7 +183,7 @@ void BreakableStringLiteral::insertBreak(unsigned LineIndex, } static StringRef getLineCommentIndentPrefix(StringRef Comment) { - static const char *const KnownPrefixes[] = { "///", "//", "//!" }; + static const char *const KnownPrefixes[] = {"///", "//", "//!"}; StringRef LongestPrefix; for (StringRef KnownPrefix : KnownPrefixes) { if (Comment.startswith(KnownPrefix)) { @@ -239,9 +239,8 @@ void BreakableLineComment::replaceWhitespace(unsigned LineIndex, /*Spaces=*/1); } -void -BreakableLineComment::replaceWhitespaceBefore(unsigned LineIndex, - WhitespaceManager &Whitespaces) { +void BreakableLineComment::replaceWhitespaceBefore( + unsigned LineIndex, WhitespaceManager &Whitespaces) { if (OriginalPrefix != Prefix) { Whitespaces.replaceWhitespaceInToken(Tok, OriginalPrefix.size(), 0, "", "", /*InPPDirective=*/false, @@ -345,7 +344,7 @@ void BreakableBlockComment::adjustWhitespace(unsigned LineIndex, // Calculate the start of the non-whitespace text in the current line. size_t StartOfLine = Lines[LineIndex].find_first_not_of(Blanks); if (StartOfLine == StringRef::npos) - StartOfLine = Lines[LineIndex].size(); + StartOfLine = Lines[LineIndex].rtrim("\r\n").size(); StringRef Whitespace = Lines[LineIndex].substr(0, StartOfLine); // Adjust Lines to only contain relevant text. @@ -415,9 +414,8 @@ void BreakableBlockComment::replaceWhitespace(unsigned LineIndex, /*Newlines=*/0, /*IndentLevel=*/0, /*Spaces=*/1); } -void -BreakableBlockComment::replaceWhitespaceBefore(unsigned LineIndex, - WhitespaceManager &Whitespaces) { +void BreakableBlockComment::replaceWhitespaceBefore( + unsigned LineIndex, WhitespaceManager &Whitespaces) { if (LineIndex == 0) return; StringRef Prefix = Decoration; diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index 91bc64b..2357abd 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -150,12 +150,6 @@ bool ContinuationIndenter::mustBreak(const LineState &State) { State.Stack.back().BreakBeforeParameter && !Current.isTrailingComment() && !Current.isOneOf(tok::r_paren, tok::r_brace)) return true; - if (Style.AlwaysBreakBeforeMultilineStrings && - State.Column > State.Stack.back().Indent && // Breaking saves columns. - !Previous.isOneOf(tok::kw_return, tok::lessless, tok::at) && - !Previous.isOneOf(TT_InlineASMColon, TT_ConditionalExpr) && - nextIsMultilineString(State)) - return true; if (((Previous.is(TT_DictLiteral) && Previous.is(tok::l_brace)) || Previous.is(TT_ArrayInitializerLSquare)) && Style.ColumnLimit > 0 && @@ -170,9 +164,18 @@ bool ContinuationIndenter::mustBreak(const LineState &State) { State.Stack.back().BreakBeforeParameter) return true; - if (State.Column < getNewLineColumn(State)) + unsigned NewLineColumn = getNewLineColumn(State); + if (State.Column < NewLineColumn) return false; + if (Style.AlwaysBreakBeforeMultilineStrings && + (NewLineColumn == State.FirstIndent + Style.ContinuationIndentWidth || + Previous.is(tok::comma) || Current.NestingLevel < 2) && + !Previous.isOneOf(tok::kw_return, tok::lessless, tok::at) && + !Previous.isOneOf(TT_InlineASMColon, TT_ConditionalExpr) && + nextIsMultilineString(State)) + return true; + // Using CanBreakBefore here and below takes care of the decision whether the // current style uses wrapping before or after operators for the given // operator. @@ -416,7 +419,21 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State, Penalty += Style.PenaltyBreakFirstLessLess; State.Column = getNewLineColumn(State); - State.Stack.back().NestedBlockIndent = State.Column; + + // Indent nested blocks relative to this column, unless in a very specific + // JavaScript special case where: + // + // var loooooong_name = + // function() { + // // code + // } + // + // is common and should be formatted like a free-standing function. + if (Style.Language != FormatStyle::LK_JavaScript || + Current.NestingLevel != 0 || !PreviousNonComment->is(tok::equal) || + !Current.is(Keywords.kw_function)) + State.Stack.back().NestedBlockIndent = State.Column; + if (NextNonComment->isMemberAccess()) { if (State.Stack.back().CallContinuation == 0) State.Stack.back().CallContinuation = State.Column; @@ -688,7 +705,8 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State, // foo(); // bar(); // }, a, b, c); - if (Current.isNot(tok::comment) && Previous && Previous->is(tok::l_brace) && + if (Current.isNot(tok::comment) && Previous && + Previous->isOneOf(tok::l_brace, TT_ArrayInitializerLSquare) && State.Stack.size() > 1) { if (State.Stack[State.Stack.size() - 2].NestedBlockInlined && Newline) for (unsigned i = 0, e = State.Stack.size() - 1; i != e; ++i) @@ -713,7 +731,7 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State, if (Current.is(TT_ObjCStringLiteral) && State.StartOfStringLiteral == 0) State.StartOfStringLiteral = State.Column + 1; else if (!Current.isOneOf(tok::comment, tok::identifier, tok::hash) && - !Current.isStringLiteral()) + !Current.isStringLiteral()) State.StartOfStringLiteral = 0; State.Column += Current.ColumnWidth; @@ -891,7 +909,7 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State, // be a line break within this call. for (const FormatToken *Tok = &Current; Tok && Tok != Current.MatchingParen; Tok = Tok->Next) { - if (Tok->MustBreakBefore || + if (Tok->MustBreakBefore || (Tok->CanBreakBefore && Tok->NewlinesBefore > 0)) { BreakBeforeParameter = true; break; diff --git a/lib/Format/ContinuationIndenter.h b/lib/Format/ContinuationIndenter.h index 1da6bd9..9b9154e 100644 --- a/lib/Format/ContinuationIndenter.h +++ b/lib/Format/ContinuationIndenter.h @@ -148,13 +148,10 @@ struct ParenState { ParenState(unsigned Indent, unsigned IndentLevel, unsigned LastSpace, bool AvoidBinPacking, bool NoLineBreak) : Indent(Indent), IndentLevel(IndentLevel), LastSpace(LastSpace), - NestedBlockIndent(Indent), FirstLessLess(0), - BreakBeforeClosingBrace(false), QuestionColumn(0), + NestedBlockIndent(Indent), BreakBeforeClosingBrace(false), AvoidBinPacking(AvoidBinPacking), BreakBeforeParameter(false), - NoLineBreak(NoLineBreak), LastOperatorWrapped(true), ColonPos(0), - StartOfFunctionCall(0), StartOfArraySubscripts(0), - NestedNameSpecifierContinuation(0), CallContinuation(0), VariablePos(0), - ContainsLineBreak(false), ContainsUnwrappedBuilder(0), + NoLineBreak(NoLineBreak), LastOperatorWrapped(true), + ContainsLineBreak(false), ContainsUnwrappedBuilder(false), AlignColons(true), ObjCSelectorNameFound(false), HasMultipleNestedBlocks(false), NestedBlockInlined(false) {} @@ -180,90 +177,90 @@ struct ParenState { /// /// Used to align "<<" operators. 0 if no such operator has been encountered /// on a level. - unsigned FirstLessLess; - - /// \brief Whether a newline needs to be inserted before the block's closing - /// brace. - /// - /// We only want to insert a newline before the closing brace if there also - /// was a newline after the beginning left brace. - bool BreakBeforeClosingBrace; + unsigned FirstLessLess = 0; /// \brief The column of a \c ? in a conditional expression; - unsigned QuestionColumn; - - /// \brief Avoid bin packing, i.e. multiple parameters/elements on multiple - /// lines, in this context. - bool AvoidBinPacking; - - /// \brief Break after the next comma (or all the commas in this context if - /// \c AvoidBinPacking is \c true). - bool BreakBeforeParameter; - - /// \brief Line breaking in this context would break a formatting rule. - bool NoLineBreak; - - /// \brief True if the last binary operator on this level was wrapped to the - /// next line. - bool LastOperatorWrapped; + unsigned QuestionColumn = 0; /// \brief The position of the colon in an ObjC method declaration/call. - unsigned ColonPos; + unsigned ColonPos = 0; /// \brief The start of the most recent function in a builder-type call. - unsigned StartOfFunctionCall; + unsigned StartOfFunctionCall = 0; /// \brief Contains the start of array subscript expressions, so that they /// can be aligned. - unsigned StartOfArraySubscripts; + unsigned StartOfArraySubscripts = 0; /// \brief If a nested name specifier was broken over multiple lines, this /// contains the start column of the second line. Otherwise 0. - unsigned NestedNameSpecifierContinuation; + unsigned NestedNameSpecifierContinuation = 0; /// \brief If a call expression was broken over multiple lines, this /// contains the start column of the second line. Otherwise 0. - unsigned CallContinuation; + unsigned CallContinuation = 0; /// \brief The column of the first variable name in a variable declaration. /// /// Used to align further variables if necessary. - unsigned VariablePos; + unsigned VariablePos = 0; + + /// \brief Whether a newline needs to be inserted before the block's closing + /// brace. + /// + /// We only want to insert a newline before the closing brace if there also + /// was a newline after the beginning left brace. + bool BreakBeforeClosingBrace : 1; + + /// \brief Avoid bin packing, i.e. multiple parameters/elements on multiple + /// lines, in this context. + bool AvoidBinPacking : 1; + + /// \brief Break after the next comma (or all the commas in this context if + /// \c AvoidBinPacking is \c true). + bool BreakBeforeParameter : 1; + + /// \brief Line breaking in this context would break a formatting rule. + bool NoLineBreak : 1; + + /// \brief True if the last binary operator on this level was wrapped to the + /// next line. + bool LastOperatorWrapped : 1; /// \brief \c true if this \c ParenState already contains a line-break. /// /// The first line break in a certain \c ParenState causes extra penalty so /// that clang-format prefers similar breaks, i.e. breaks in the same /// parenthesis. - bool ContainsLineBreak; + bool ContainsLineBreak : 1; /// \brief \c true if this \c ParenState contains multiple segments of a /// builder-type call on one line. - bool ContainsUnwrappedBuilder; + bool ContainsUnwrappedBuilder : 1; /// \brief \c true if the colons of the curren ObjC method expression should /// be aligned. /// /// Not considered for memoization as it will always have the same value at /// the same token. - bool AlignColons; + bool AlignColons : 1; /// \brief \c true if at least one selector name was found in the current /// ObjC method expression. /// /// Not considered for memoization as it will always have the same value at /// the same token. - bool ObjCSelectorNameFound; + bool ObjCSelectorNameFound : 1; /// \brief \c true if there are multiple nested blocks inside these parens. /// /// Not considered for memoization as it will always have the same value at /// the same token. - bool HasMultipleNestedBlocks; + bool HasMultipleNestedBlocks : 1; // \brief The start of a nested block (e.g. lambda introducer in C++ or // "function" in JavaScript) is not wrapped to a new line. - bool NestedBlockInlined; + bool NestedBlockInlined : 1; bool operator<(const ParenState &Other) const { if (Indent != Other.Indent) diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index c725b4b..0feeaa0 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -174,7 +174,8 @@ template <> struct MappingTraits<FormatStyle> { IO.mapOptional("AlignEscapedNewlinesLeft", Style.AlignEscapedNewlinesLeft); IO.mapOptional("AlignOperands", Style.AlignOperands); IO.mapOptional("AlignTrailingComments", Style.AlignTrailingComments); - IO.mapOptional("AlignConsecutiveAssignments", Style.AlignConsecutiveAssignments); + IO.mapOptional("AlignConsecutiveAssignments", + Style.AlignConsecutiveAssignments); IO.mapOptional("AllowAllParametersOfDeclarationOnNextLine", Style.AllowAllParametersOfDeclarationOnNextLine); IO.mapOptional("AllowShortBlocksOnASingleLine", @@ -785,7 +786,8 @@ private: // Backticks get lexed as tok::unknown tokens. If a template string contains // a comment start, it gets lexed as a tok::comment, or tok::unknown if // unterminated. - if (!EndBacktick->isOneOf(tok::comment, tok::unknown)) + if (!EndBacktick->isOneOf(tok::comment, tok::string_literal, + tok::char_constant, tok::unknown)) return false; size_t CommentBacktickPos = EndBacktick->TokenText.find('`'); // Unknown token that's not actually a backtick, or a comment that doesn't @@ -1122,7 +1124,10 @@ private: Column = FormatTok->LastLineColumnWidth; } - if (std::find(ForEachMacros.begin(), ForEachMacros.end(), + if (!(Tokens.size() > 0 && Tokens.back()->Tok.getIdentifierInfo() && + Tokens.back()->Tok.getIdentifierInfo()->getPPKeywordID() == + tok::pp_define) && + std::find(ForEachMacros.begin(), ForEachMacros.end(), FormatTok->Tok.getIdentifierInfo()) != ForEachMacros.end()) FormatTok->Type = TT_ForEachMacro; @@ -1254,7 +1259,8 @@ public: } tooling::Replacements format(SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, - FormatTokenLexer &Tokens, bool *IncompleteFormat) { + FormatTokenLexer &Tokens, + bool *IncompleteFormat) { TokenAnnotator Annotator(Style, Tokens.getKeywords()); for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) { Annotator.annotate(*AnnotatedLines[i]); @@ -1500,8 +1506,7 @@ tooling::Replacements reformat(const FormatStyle &Style, tooling::Replacements reformat(const FormatStyle &Style, StringRef Code, ArrayRef<tooling::Range> Ranges, - StringRef FileName, - bool *IncompleteFormat) { + StringRef FileName, bool *IncompleteFormat) { if (Style.DisableFormat) return tooling::Replacements(); diff --git a/lib/Format/FormatToken.cpp b/lib/Format/FormatToken.cpp index 88678ca..316171d 100644 --- a/lib/Format/FormatToken.cpp +++ b/lib/Format/FormatToken.cpp @@ -203,11 +203,14 @@ void CommaSeparatedList::precomputeFormattingInfos(const FormatToken *Token) { // We can never place more than ColumnLimit / 3 items in a row (because of the // spaces and the comma). - for (unsigned Columns = 1; Columns <= Style.ColumnLimit / 3; ++Columns) { + unsigned MaxItems = Style.ColumnLimit / 3; + std::vector<unsigned> MinSizeInColumn; + MinSizeInColumn.reserve(MaxItems); + for (unsigned Columns = 1; Columns <= MaxItems; ++Columns) { ColumnFormat Format; Format.Columns = Columns; Format.ColumnSizes.resize(Columns); - std::vector<unsigned> MinSizeInColumn(Columns, UINT_MAX); + MinSizeInColumn.assign(Columns, UINT_MAX); Format.LineCount = 1; bool HasRowWithSufficientColumns = false; unsigned Column = 0; diff --git a/lib/Format/FormatToken.h b/lib/Format/FormatToken.h index dd12969..5b7dadb 100644 --- a/lib/Format/FormatToken.h +++ b/lib/Format/FormatToken.h @@ -86,24 +86,12 @@ enum TokenType { }; // Represents what type of block a set of braces open. -enum BraceBlockKind { - BK_Unknown, - BK_Block, - BK_BracedInit -}; +enum BraceBlockKind { BK_Unknown, BK_Block, BK_BracedInit }; // The packing kind of a function's parameters. -enum ParameterPackingKind { - PPK_BinPacked, - PPK_OnePerLine, - PPK_Inconclusive -}; +enum ParameterPackingKind { PPK_BinPacked, PPK_OnePerLine, PPK_Inconclusive }; -enum FormatDecision { - FD_Unformatted, - FD_Continue, - FD_Break -}; +enum FormatDecision { FD_Unformatted, FD_Continue, FD_Break }; class TokenRole; class AnnotatedLine; diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index 78e6103..8d08c3d 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -87,7 +87,7 @@ private: if (CurrentToken->Previous->isOneOf(tok::pipepipe, tok::ampamp) && CurrentToken->Previous->is(TT_BinaryOperator) && Contexts[Contexts.size() - 2].IsExpression && - Line.First->isNot(tok::kw_template)) + !Line.startsWith(tok::kw_template)) return false; updateParameterCount(Left, CurrentToken); if (!consumeToken()) @@ -255,7 +255,7 @@ private: // A '[' could be an index subscript (after an identifier or after // ')' or ']'), it could be the start of an Objective-C method - // expression, or it could the the start of an Objective-C array literal. + // expression, or it could the start of an Objective-C array literal. FormatToken *Left = CurrentToken->Previous; Left->ParentBracket = Contexts.back().ContextKind; FormatToken *Parent = Left->getPreviousNonComment(); @@ -457,7 +457,7 @@ private: if (Contexts.back().ColonIsDictLiteral) { Tok->Type = TT_DictLiteral; } else if (Contexts.back().ColonIsObjCMethodExpr || - Line.First->is(TT_ObjCMethodSpecifier)) { + Line.startsWith(TT_ObjCMethodSpecifier)) { Tok->Type = TT_ObjCMethodExpr; Tok->Previous->Type = TT_SelectorName; if (Tok->Previous->ColumnWidth > @@ -503,7 +503,7 @@ private: if (!parseParens()) return false; if (Line.MustBeDeclaration && Contexts.size() == 1 && - !Contexts.back().IsExpression && Line.First->isNot(TT_ObjCProperty) && + !Contexts.back().IsExpression && !Line.startsWith(TT_ObjCProperty) && (!Tok->Previous || !Tok->Previous->isOneOf(tok::kw_decltype, TT_LeadingJavaAnnotation))) Line.MightBeFunctionDecl = true; @@ -558,7 +558,7 @@ private: break; case tok::question: if (Style.Language == FormatStyle::LK_JavaScript && Tok->Next && - Tok->Next->isOneOf(tok::semi, tok::colon, tok::r_paren, + Tok->Next->isOneOf(tok::semi, tok::comma, tok::colon, tok::r_paren, tok::r_brace)) { // Question marks before semicolons, colons, etc. indicate optional // types (fields, parameters), e.g. @@ -581,7 +581,7 @@ private: if (Contexts.back().InCtorInitializer) Tok->Type = TT_CtorInitializerComma; else if (Contexts.back().FirstStartOfName && - (Contexts.size() == 1 || Line.First->is(tok::kw_for))) { + (Contexts.size() == 1 || Line.startsWith(tok::kw_for))) { Contexts.back().FirstStartOfName->PartOfMultiVariableDeclStmt = true; Line.IsMultiVariableDeclStmt = true; } @@ -724,7 +724,7 @@ public: if (ImportStatement) return LT_ImportStatement; - if (Line.First->is(TT_ObjCMethodSpecifier)) { + if (Line.startsWith(TT_ObjCMethodSpecifier)) { if (Contexts.back().FirstObjCSelectorName) Contexts.back().FirstObjCSelectorName->LongestObjCSelectorName = Contexts.back().LongestObjCSelectorName; @@ -820,7 +820,7 @@ private: !Line.First->isOneOf(tok::kw_template, tok::kw_using) && (!Current.Previous || Current.Previous->isNot(tok::kw_operator))) { Contexts.back().IsExpression = true; - if (!Line.First->is(TT_UnaryOperator)) { + if (!Line.startsWith(TT_UnaryOperator)) { for (FormatToken *Previous = Current.Previous; Previous && !Previous->isOneOf(tok::comma, tok::semi); Previous = Previous->Previous) { @@ -870,7 +870,7 @@ private: Contexts.back().InCtorInitializer = true; } else if (Current.is(tok::kw_new)) { Contexts.back().CanBeExpression = false; - } else if (Current.is(tok::semi) || Current.is(tok::exclaim)) { + } else if (Current.isOneOf(tok::semi, tok::exclaim)) { // This should be the condition or increment in a for-loop. Contexts.back().IsExpression = true; } @@ -1081,7 +1081,7 @@ private: // there is also an identifier before the (). else if (LeftOfParens && Tok.Next && (LeftOfParens->Tok.getIdentifierInfo() == nullptr || - LeftOfParens->is(tok::kw_return)) && + LeftOfParens->isOneOf(tok::kw_return, tok::kw_case)) && !LeftOfParens->isOneOf(TT_OverloadedOperator, tok::at, TT_TemplateCloser)) { if (Tok.Next->isOneOf(tok::identifier, tok::numeric_constant)) { @@ -1441,11 +1441,11 @@ void TokenAnnotator::annotate(AnnotatedLine &Line) { ExpressionParser ExprParser(Style, Keywords, Line); ExprParser.parse(); - if (Line.First->is(TT_ObjCMethodSpecifier)) + if (Line.startsWith(TT_ObjCMethodSpecifier)) Line.Type = LT_ObjCMethodDecl; - else if (Line.First->is(TT_ObjCDecl)) + else if (Line.startsWith(TT_ObjCDecl)) Line.Type = LT_ObjCDecl; - else if (Line.First->is(TT_ObjCProperty)) + else if (Line.startsWith(TT_ObjCProperty)) Line.Type = LT_ObjCProperty; Line.First->SpacesRequiredBefore = 1; @@ -1638,7 +1638,7 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line, if (Right.isOneOf(TT_StartOfName, TT_FunctionDeclarationName) || Right.is(tok::kw_operator)) { - if (Line.First->is(tok::kw_for) && Right.PartOfMultiVariableDeclStmt) + if (Line.startsWith(tok::kw_for) && Right.PartOfMultiVariableDeclStmt) return 3; if (Left.is(TT_StartOfName)) return 110; @@ -1674,8 +1674,7 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line, (!Right.Next || Right.Next->isNot(tok::l_paren))) { // Moving trailing annotations to the next line is fine for ObjC method // declarations. - if (Line.First->is(TT_ObjCMethodSpecifier)) - + if (Line.startsWith(TT_ObjCMethodSpecifier)) return 10; // Generally, breaking before a trailing annotation is bad unless it is // function-like. It seems to be especially preferable to keep standard @@ -1687,7 +1686,7 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line, } // In for-loops, prefer breaking at ',' and ';'. - if (Line.First->is(tok::kw_for) && Left.is(tok::equal)) + if (Line.startsWith(tok::kw_for) && Left.is(tok::equal)) return 4; // In Objective-C method expressions, prefer breaking before "param:" over @@ -1800,6 +1799,7 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, return true; if (Left.is(TT_PointerOrReference)) return Right.Tok.isLiteral() || Right.is(TT_BlockComment) || + (Right.is(tok::l_brace) && Right.BlockKind == BK_Block) || (!Right.isOneOf(TT_PointerOrReference, TT_ArraySubscriptLSquare, tok::l_paren) && (Style.PointerAlignment != FormatStyle::PAS_Right && @@ -1844,7 +1844,8 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, tok::kw_new, tok::kw_delete) && (!Left.Previous || Left.Previous->isNot(tok::period))))) || (Style.SpaceBeforeParens == FormatStyle::SBPO_Always && - (Left.is(tok::identifier) || Left.isFunctionLikeKeyword() || Left.is(tok::r_paren)) && + (Left.is(tok::identifier) || Left.isFunctionLikeKeyword() || + Left.is(tok::r_paren)) && Line.Type != LT_PreprocessorDirective); } if (Left.is(tok::at) && Right.Tok.getObjCKeywordID() != tok::objc_not_keyword) @@ -1989,7 +1990,7 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, Left.MatchingParen && Left.MatchingParen->is(TT_OverloadedOperatorLParen)) return false; if (Right.is(tok::less) && Left.isNot(tok::l_paren) && - Line.First->is(tok::hash)) + Line.startsWith(tok::hash)) return true; if (Right.is(TT_TrailingUnaryOperator)) return false; @@ -2010,6 +2011,39 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, if (Right.NewlinesBefore > 1) return true; + if (Style.Language == FormatStyle::LK_JavaScript) { + // FIXME: This might apply to other languages and token kinds. + if (Right.is(tok::char_constant) && Left.is(tok::plus) && Left.Previous && + Left.Previous->is(tok::char_constant)) + return true; + if (Left.is(TT_DictLiteral) && Left.is(tok::l_brace) && Line.Level == 0 && + Left.Previous && Left.Previous->is(tok::equal) && + Line.First->isOneOf(tok::identifier, Keywords.kw_import, tok::kw_export, + tok::kw_const) && + // kw_var is a pseudo-token that's a tok::identifier, so matches above. + !Line.startsWith(Keywords.kw_var)) + // Object literals on the top level of a file are treated as "enum-style". + // Each key/value pair is put on a separate line, instead of bin-packing. + return true; + if (Left.is(tok::l_brace) && Line.Level == 0 && + (Line.startsWith(tok::kw_enum) || + Line.startsWith(tok::kw_export, tok::kw_enum))) + // JavaScript top-level enum key/value pairs are put on separate lines + // instead of bin-packing. + return true; + if (Right.is(tok::r_brace) && Left.is(tok::l_brace) && + !Left.Children.empty()) + // Support AllowShortFunctionsOnASingleLine for JavaScript. + return Style.AllowShortFunctionsOnASingleLine == FormatStyle::SFS_None || + (Left.NestingLevel == 0 && Line.Level == 0 && + Style.AllowShortFunctionsOnASingleLine == + FormatStyle::SFS_Inline); + } else if (Style.Language == FormatStyle::LK_Java) { + if (Right.is(tok::plus) && Left.is(tok::string_literal) && Right.Next && + Right.Next->is(tok::string_literal)) + return true; + } + // If the last token before a '}' is a comma or a trailing comment, the // intention is to insert a line break after it in order to make shuffling // around entries easier. @@ -2030,7 +2064,7 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, Left.isNot(TT_CtorInitializerColon) && (Right.NewlinesBefore > 0 && Right.HasUnescapedNewline); if (Left.isTrailingComment()) - return true; + return true; if (Left.isStringLiteral() && (Right.isStringLiteral() || Right.is(TT_ObjCStringLiteral))) return true; @@ -2060,12 +2094,6 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, return true; if (Right.is(TT_InlineASMBrace)) return Right.HasUnescapedNewline; - if (Style.Language == FormatStyle::LK_JavaScript && Right.is(tok::r_brace) && - Left.is(tok::l_brace) && !Left.Children.empty()) - // Support AllowShortFunctionsOnASingleLine for JavaScript. - return Style.AllowShortFunctionsOnASingleLine == FormatStyle::SFS_None || - (Left.NestingLevel == 0 && Line.Level == 0 && - Style.AllowShortFunctionsOnASingleLine == FormatStyle::SFS_Inline); if (isAllmanBrace(Left) || isAllmanBrace(Right)) return Style.BreakBeforeBraces == FormatStyle::BS_Allman || Style.BreakBeforeBraces == FormatStyle::BS_GNU; @@ -2082,26 +2110,6 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, Line.Last->is(tok::l_brace)) return true; - if (Style.Language == FormatStyle::LK_JavaScript) { - // FIXME: This might apply to other languages and token kinds. - if (Right.is(tok::char_constant) && Left.is(tok::plus) && Left.Previous && - Left.Previous->is(tok::char_constant)) - return true; - if (Left.is(TT_DictLiteral) && Left.is(tok::l_brace) && - Left.NestingLevel == 0 && Left.Previous && - Left.Previous->is(tok::equal) && - Line.First->isOneOf(tok::identifier, Keywords.kw_import, - tok::kw_export) && - // kw_var is a pseudo-token that's a tok::identifier, so matches above. - !Line.First->is(Keywords.kw_var)) - // Enum style object literal. - return true; - } else if (Style.Language == FormatStyle::LK_Java) { - if (Right.is(tok::plus) && Left.is(tok::string_literal) && Right.Next && - Right.Next->is(tok::string_literal)) - return true; - } - return false; } diff --git a/lib/Format/TokenAnnotator.h b/lib/Format/TokenAnnotator.h index a948cdb..b8a6be0 100644 --- a/lib/Format/TokenAnnotator.h +++ b/lib/Format/TokenAnnotator.h @@ -59,7 +59,7 @@ public: I->Tok->Previous = Current; Current = Current->Next; Current->Children.clear(); - for (const auto& Child : Node.Children) { + for (const auto &Child : Node.Children) { Children.push_back(new AnnotatedLine(Child)); Current->Children.push_back(Children.back()); } @@ -80,6 +80,12 @@ public: } } + /// \c true if this line starts with the given tokens in order, ignoring + /// comments. + template <typename... Ts> bool startsWith(Ts... Tokens) const { + return startsWith(First, Tokens...); + } + FormatToken *First; FormatToken *Last; @@ -107,6 +113,18 @@ private: // Disallow copying. AnnotatedLine(const AnnotatedLine &) = delete; void operator=(const AnnotatedLine &) = delete; + + template <typename A, typename... Ts> + bool startsWith(FormatToken *Tok, A K1) const { + while (Tok && Tok->is(tok::comment)) + Tok = Tok->Next; + return Tok && Tok->is(K1); + } + + template <typename A, typename... Ts> + bool startsWith(FormatToken *Tok, A K1, Ts... Tokens) const { + return startsWith(Tok, K1) && startsWith(Tok->Next, Tokens...); + } }; /// \brief Determines extra information about the tokens comprising an diff --git a/lib/Format/UnwrappedLineFormatter.cpp b/lib/Format/UnwrappedLineFormatter.cpp index cbf8c6c..b6784b3 100644 --- a/lib/Format/UnwrappedLineFormatter.cpp +++ b/lib/Format/UnwrappedLineFormatter.cpp @@ -21,7 +21,7 @@ namespace { bool startsExternCBlock(const AnnotatedLine &Line) { const FormatToken *Next = Line.First->getNextNonComment(); const FormatToken *NextNext = Next ? Next->getNextNonComment() : nullptr; - return Line.First->is(tok::kw_extern) && Next && Next->isStringLiteral() && + return Line.startsWith(tok::kw_extern) && Next && Next->isStringLiteral() && NextNext && NextNext->is(tok::l_brace); } @@ -51,11 +51,13 @@ public: /// next. void nextLine(const AnnotatedLine &Line) { Offset = getIndentOffset(*Line.First); + // Update the indent level cache size so that we can rely on it + // having the right size in adjustToUnmodifiedline. + while (IndentForLevel.size() <= Line.Level) + IndentForLevel.push_back(-1); if (Line.InPPDirective) { Indent = Line.Level * Style.IndentWidth + AdditionalIndent; } else { - while (IndentForLevel.size() <= Line.Level) - IndentForLevel.push_back(-1); IndentForLevel.resize(Line.Level + 1); Indent = getIndent(IndentForLevel, Line.Level); } @@ -72,7 +74,7 @@ public: unsigned LevelIndent = Line.First->OriginalColumn; if (static_cast<int>(LevelIndent) - Offset >= 0) LevelIndent -= Offset; - if ((Line.First->isNot(tok::comment) || IndentForLevel[Line.Level] == -1) && + if ((!Line.First->is(tok::comment) || IndentForLevel[Line.Level] == -1) && !Line.InPPDirective) IndentForLevel[Line.Level] = LevelIndent; } @@ -187,7 +189,7 @@ private: // If necessary, change to something smarter. bool MergeShortFunctions = Style.AllowShortFunctionsOnASingleLine == FormatStyle::SFS_All || - (Style.AllowShortFunctionsOnASingleLine == FormatStyle::SFS_Empty && + (Style.AllowShortFunctionsOnASingleLine >= FormatStyle::SFS_Empty && I[1]->First->is(tok::r_brace)) || (Style.AllowShortFunctionsOnASingleLine == FormatStyle::SFS_Inline && TheLine->Level != 0); @@ -278,7 +280,7 @@ private: TT_LineComment)) return 0; // Only inline simple if's (no nested if or else). - if (I + 2 != E && Line.First->is(tok::kw_if) && + if (I + 2 != E && Line.startsWith(tok::kw_if) && I[2]->First->is(tok::kw_else)) return 0; return 1; @@ -332,12 +334,11 @@ private: return 0; if (Line.First->isOneOf(tok::kw_if, tok::kw_while, tok::kw_do, tok::kw_try, tok::kw___try, tok::kw_catch, tok::kw___finally, - tok::kw_for, tok::r_brace) || - Line.First->is(Keywords.kw___except)) { + tok::kw_for, tok::r_brace, Keywords.kw___except)) { if (!Style.AllowShortBlocksOnASingleLine) return 0; if (!Style.AllowShortIfStatementsOnASingleLine && - Line.First->is(tok::kw_if)) + Line.startsWith(tok::kw_if)) return 0; if (!Style.AllowShortLoopsOnASingleLine && Line.First->isOneOf(tok::kw_while, tok::kw_do, tok::kw_for)) @@ -360,7 +361,7 @@ private: Tok->SpacesRequiredBefore = 0; Tok->CanBreakBefore = true; return 1; - } else if (Limit != 0 && Line.First->isNot(tok::kw_namespace) && + } else if (Limit != 0 && !Line.startsWith(tok::kw_namespace) && !startsExternCBlock(Line)) { // We don't merge short records. if (Line.First->isOneOf(tok::kw_class, tok::kw_union, tok::kw_struct, @@ -444,9 +445,9 @@ private: const FormatStyle &Style; const AdditionalKeywords &Keywords; - const SmallVectorImpl<AnnotatedLine*>::const_iterator End; + const SmallVectorImpl<AnnotatedLine *>::const_iterator End; - SmallVectorImpl<AnnotatedLine*>::const_iterator Next; + SmallVectorImpl<AnnotatedLine *>::const_iterator Next; }; static void markFinalized(FormatToken *Tok) { diff --git a/lib/Format/UnwrappedLineFormatter.h b/lib/Format/UnwrappedLineFormatter.h index da9aa1c..478617d 100644 --- a/lib/Format/UnwrappedLineFormatter.h +++ b/lib/Format/UnwrappedLineFormatter.h @@ -52,7 +52,8 @@ private: /// \brief Returns the column limit for a line, taking into account whether we /// need an escaped newline due to a continued preprocessor directive. - unsigned getColumnLimit(bool InPPDirective, const AnnotatedLine *NextLine) const; + unsigned getColumnLimit(bool InPPDirective, + const AnnotatedLine *NextLine) const; // Cache to store the penalty of formatting a vector of AnnotatedLines // starting from a specific additional offset. Improves performance if there diff --git a/lib/Format/UnwrappedLineParser.cpp b/lib/Format/UnwrappedLineParser.cpp index 6ad4329..ea1ca39 100644 --- a/lib/Format/UnwrappedLineParser.cpp +++ b/lib/Format/UnwrappedLineParser.cpp @@ -251,7 +251,6 @@ void UnwrappedLineParser::parse() { assert(PPLevelBranchIndex.back() <= PPLevelBranchCount.back()); } } while (!PPLevelBranchIndex.empty()); - } void UnwrappedLineParser::parseFile() { @@ -774,7 +773,15 @@ void UnwrappedLineParser::parseStructuralElement() { parseBracedList(); break; case tok::kw_enum: + // parseEnum falls through and does not yet add an unwrapped line as an + // enum definition can start a structural element. parseEnum(); + // This does not apply for Java and JavaScript. + if (Style.Language == FormatStyle::LK_Java || + Style.Language == FormatStyle::LK_JavaScript) { + addUnwrappedLine(); + return; + } break; case tok::kw_typedef: nextToken(); @@ -785,9 +792,15 @@ void UnwrappedLineParser::parseStructuralElement() { case tok::kw_struct: case tok::kw_union: case tok::kw_class: + // parseRecord falls through and does not yet add an unwrapped line as a + // record declaration or definition can start a structural element. parseRecord(); - // A record declaration or definition is always the start of a structural - // element. + // This does not apply for Java and JavaScript. + if (Style.Language == FormatStyle::LK_Java || + Style.Language == FormatStyle::LK_JavaScript) { + addUnwrappedLine(); + return; + } break; case tok::period: nextToken(); @@ -848,6 +861,7 @@ void UnwrappedLineParser::parseStructuralElement() { Style.Language == FormatStyle::LK_Java) && FormatTok->is(Keywords.kw_interface)) { parseRecord(); + addUnwrappedLine(); break; } @@ -872,8 +886,7 @@ void UnwrappedLineParser::parseStructuralElement() { ? FormatTok->NewlinesBefore > 0 : CommentsBeforeNextToken.front()->NewlinesBefore > 0; - if (FollowedByNewline && - (Text.size() >= 5 || FunctionLike) && + if (FollowedByNewline && (Text.size() >= 5 || FunctionLike) && tokenCanStartNewLine(FormatTok->Tok) && Text == Text.upper()) { addUnwrappedLine(); return; @@ -1033,7 +1046,7 @@ void UnwrappedLineParser::tryToParseJSFunction() { if (FormatTok->is(tok::l_brace)) tryToParseBracedList(); else - while(FormatTok->isNot(tok::l_brace) && !eof()) + while (FormatTok->isNot(tok::l_brace) && !eof()) nextToken(); } @@ -1066,7 +1079,7 @@ bool UnwrappedLineParser::parseBracedList(bool ContinueOnSemicolons) { nextToken(); // Fat arrows can be followed by simple expressions or by child blocks // in curly braces. - if (FormatTok->is(tok::l_brace)){ + if (FormatTok->is(tok::l_brace)) { parseChildBlock(); continue; } @@ -1475,6 +1488,7 @@ void UnwrappedLineParser::parseEnum() { // Eat up enum class ... if (FormatTok->Tok.is(tok::kw_class) || FormatTok->Tok.is(tok::kw_struct)) nextToken(); + while (FormatTok->Tok.getIdentifierInfo() || FormatTok->isOneOf(tok::colon, tok::coloncolon, tok::less, tok::greater, tok::comma, tok::question)) { @@ -1482,8 +1496,14 @@ void UnwrappedLineParser::parseEnum() { // We can have macros or attributes in between 'enum' and the enum name. if (FormatTok->is(tok::l_paren)) parseParens(); - if (FormatTok->is(tok::identifier)) + if (FormatTok->is(tok::identifier)) { nextToken(); + // If there are two identifiers in a row, this is likely an elaborate + // return type. In Java, this can be "implements", etc. + if (Style.Language == FormatStyle::LK_Cpp && + FormatTok->is(tok::identifier)) + return; + } } // Just a declaration or something is wrong. @@ -1505,8 +1525,8 @@ void UnwrappedLineParser::parseEnum() { addUnwrappedLine(); } - // We fall through to parsing a structural element afterwards, so that in - // enum A {} n, m; + // There is no addUnwrappedLine() here so that we fall through to parsing a + // structural element afterwards. Thus, in "enum A {} n, m;", // "} n, m;" will end up in one unwrapped line. } @@ -1576,7 +1596,6 @@ void UnwrappedLineParser::parseRecord() { const FormatToken &InitialToken = *FormatTok; nextToken(); - // The actual identifier can be a nested name specifier, and in macros // it is often token-pasted. while (FormatTok->isOneOf(tok::identifier, tok::coloncolon, tok::hashhash, @@ -1623,13 +1642,9 @@ void UnwrappedLineParser::parseRecord() { parseBlock(/*MustBeDeclaration=*/true, /*AddLevel=*/true, /*MunchSemi=*/false); } - // We fall through to parsing a structural element afterwards, so - // class A {} n, m; - // will end up in one unwrapped line. - // This does not apply for Java and JavaScript. - if (Style.Language == FormatStyle::LK_Java || - Style.Language == FormatStyle::LK_JavaScript) - addUnwrappedLine(); + // There is no addUnwrappedLine() here so that we fall through to parsing a + // structural element afterwards. Thus, in "class A {} n, m;", + // "} n, m;" will end up in one unwrapped line. } void UnwrappedLineParser::parseObjCProtocolList() { @@ -1722,7 +1737,8 @@ void UnwrappedLineParser::parseJavaScriptEs6ImportExport() { return; } - if (FormatTok->isOneOf(tok::kw_const, tok::kw_class, Keywords.kw_var)) + if (FormatTok->isOneOf(tok::kw_const, tok::kw_class, tok::kw_enum, + Keywords.kw_var)) return; // Fall through to parsing the corresponding structure. if (FormatTok->is(tok::l_brace)) { diff --git a/lib/Format/WhitespaceManager.h b/lib/Format/WhitespaceManager.h index 4bfc813..d973838 100644 --- a/lib/Format/WhitespaceManager.h +++ b/lib/Format/WhitespaceManager.h @@ -167,9 +167,11 @@ private: /// \brief Align consecutive assignments over all \c Changes. void alignConsecutiveAssignments(); - /// \brief Align consecutive assignments from change \p Start to change \p End at + /// \brief Align consecutive assignments from change \p Start to change \p End + /// at /// the specified \p Column. - void alignConsecutiveAssignments(unsigned Start, unsigned End, unsigned Column); + void alignConsecutiveAssignments(unsigned Start, unsigned End, + unsigned Column); /// \brief Align trailing comments over all \c Changes. void alignTrailingComments(); |