diff options
Diffstat (limited to 'lib/Format/Format.cpp')
-rw-r--r-- | lib/Format/Format.cpp | 747 |
1 files changed, 194 insertions, 553 deletions
diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index 101b16f..a0557f7 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -15,8 +15,10 @@ #define DEBUG_TYPE "format-formatter" +#include "BreakableToken.h" #include "TokenAnnotator.h" #include "UnwrappedLineParser.h" +#include "WhitespaceManager.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/OperatorPrecedence.h" #include "clang/Basic/SourceManager.h" @@ -34,62 +36,66 @@ namespace format { FormatStyle getLLVMStyle() { FormatStyle LLVMStyle; - LLVMStyle.ColumnLimit = 80; - LLVMStyle.MaxEmptyLinesToKeep = 1; - LLVMStyle.PointerBindsToType = false; - LLVMStyle.DerivePointerBinding = false; LLVMStyle.AccessModifierOffset = -2; - LLVMStyle.Standard = FormatStyle::LS_Cpp03; - LLVMStyle.IndentCaseLabels = false; - LLVMStyle.SpacesBeforeTrailingComments = 1; - LLVMStyle.BinPackParameters = true; + LLVMStyle.AlignEscapedNewlinesLeft = false; LLVMStyle.AllowAllParametersOfDeclarationOnNextLine = true; - LLVMStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = false; LLVMStyle.AllowShortIfStatementsOnASingleLine = false; + LLVMStyle.BinPackParameters = true; + LLVMStyle.ColumnLimit = 80; + LLVMStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = false; + LLVMStyle.DerivePointerBinding = false; + LLVMStyle.IndentCaseLabels = false; + LLVMStyle.MaxEmptyLinesToKeep = 1; LLVMStyle.ObjCSpaceBeforeProtocolList = true; LLVMStyle.PenaltyExcessCharacter = 1000000; - LLVMStyle.PenaltyReturnTypeOnItsOwnLine = 5; + LLVMStyle.PenaltyReturnTypeOnItsOwnLine = 75; + LLVMStyle.PointerBindsToType = false; + LLVMStyle.SpacesBeforeTrailingComments = 1; + LLVMStyle.Standard = FormatStyle::LS_Cpp03; return LLVMStyle; } FormatStyle getGoogleStyle() { FormatStyle GoogleStyle; - GoogleStyle.ColumnLimit = 80; - GoogleStyle.MaxEmptyLinesToKeep = 1; - GoogleStyle.PointerBindsToType = true; - GoogleStyle.DerivePointerBinding = true; GoogleStyle.AccessModifierOffset = -1; - GoogleStyle.Standard = FormatStyle::LS_Auto; - GoogleStyle.IndentCaseLabels = true; - GoogleStyle.SpacesBeforeTrailingComments = 2; - GoogleStyle.BinPackParameters = true; + GoogleStyle.AlignEscapedNewlinesLeft = true; GoogleStyle.AllowAllParametersOfDeclarationOnNextLine = true; + GoogleStyle.AllowShortIfStatementsOnASingleLine = true; + GoogleStyle.BinPackParameters = true; + GoogleStyle.ColumnLimit = 80; GoogleStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = true; - GoogleStyle.AllowShortIfStatementsOnASingleLine = false; + GoogleStyle.DerivePointerBinding = true; + GoogleStyle.IndentCaseLabels = true; + GoogleStyle.MaxEmptyLinesToKeep = 1; GoogleStyle.ObjCSpaceBeforeProtocolList = false; GoogleStyle.PenaltyExcessCharacter = 1000000; - GoogleStyle.PenaltyReturnTypeOnItsOwnLine = 100; + GoogleStyle.PenaltyReturnTypeOnItsOwnLine = 200; + GoogleStyle.PointerBindsToType = true; + GoogleStyle.SpacesBeforeTrailingComments = 2; + GoogleStyle.Standard = FormatStyle::LS_Auto; return GoogleStyle; } FormatStyle getChromiumStyle() { FormatStyle ChromiumStyle = getGoogleStyle(); ChromiumStyle.AllowAllParametersOfDeclarationOnNextLine = false; + ChromiumStyle.AllowShortIfStatementsOnASingleLine = false; ChromiumStyle.BinPackParameters = false; ChromiumStyle.Standard = FormatStyle::LS_Cpp03; ChromiumStyle.DerivePointerBinding = false; return ChromiumStyle; } -static bool isTrailingComment(const AnnotatedToken &Tok) { - return Tok.is(tok::comment) && - (Tok.Children.empty() || Tok.Children[0].MustBreakBefore); -} - -static bool isComparison(const AnnotatedToken &Tok) { - prec::Level Precedence = getPrecedence(Tok); - return Tok.Type == TT_BinaryOperator && - (Precedence == prec::Equality || Precedence == prec::Relational); +FormatStyle getMozillaStyle() { + FormatStyle MozillaStyle = getLLVMStyle(); + MozillaStyle.AllowAllParametersOfDeclarationOnNextLine = false; + MozillaStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = true; + MozillaStyle.DerivePointerBinding = true; + MozillaStyle.IndentCaseLabels = true; + MozillaStyle.ObjCSpaceBeforeProtocolList = false; + MozillaStyle.PenaltyReturnTypeOnItsOwnLine = 200; + MozillaStyle.PointerBindsToType = true; + return MozillaStyle; } // Returns the length of everything up to the first possible line break after @@ -104,373 +110,12 @@ static unsigned getLengthToMatchingParen(const AnnotatedToken &Tok) { return End->TotalLength - Tok.TotalLength + 1; } -static size_t -calculateColumnLimit(const FormatStyle &Style, bool InPPDirective) { - // In preprocessor directives reserve two chars for trailing " \" - return Style.ColumnLimit - (InPPDirective ? 2 : 0); -} - -/// \brief Manages the whitespaces around tokens and their replacements. -/// -/// This includes special handling for certain constructs, e.g. the alignment of -/// trailing line comments. -class WhitespaceManager { -public: - WhitespaceManager(SourceManager &SourceMgr, const FormatStyle &Style) - : SourceMgr(SourceMgr), Style(Style) {} - - /// \brief Replaces the whitespace in front of \p Tok. Only call once for - /// each \c AnnotatedToken. - void replaceWhitespace(const AnnotatedToken &Tok, unsigned NewLines, - unsigned Spaces, unsigned WhitespaceStartColumn) { - // 2+ newlines mean an empty line separating logic scopes. - if (NewLines >= 2) - alignComments(); - - SourceLocation TokenLoc = Tok.FormatTok.Tok.getLocation(); - bool LineExceedsColumnLimit = Spaces + WhitespaceStartColumn + - Tok.FormatTok.TokenLength > Style.ColumnLimit; - - // Align line comments if they are trailing or if they continue other - // trailing comments. - if (isTrailingComment(Tok)) { - // Remove the comment's trailing whitespace. - if (Tok.FormatTok.Tok.getLength() != Tok.FormatTok.TokenLength) - Replaces.insert(tooling::Replacement( - SourceMgr, TokenLoc.getLocWithOffset(Tok.FormatTok.TokenLength), - Tok.FormatTok.Tok.getLength() - Tok.FormatTok.TokenLength, "")); - - // Align comment with other comments. - if ((Tok.Parent != NULL || !Comments.empty()) && - !LineExceedsColumnLimit) { - StoredComment Comment; - Comment.Tok = Tok.FormatTok; - Comment.Spaces = Spaces; - Comment.NewLines = NewLines; - Comment.MinColumn = - NewLines > 0 ? Spaces : WhitespaceStartColumn + Spaces; - Comment.MaxColumn = Style.ColumnLimit - Tok.FormatTok.TokenLength; - Comment.Untouchable = false; - Comments.push_back(Comment); - return; - } - } - - // If this line does not have a trailing comment, align the stored comments. - if (Tok.Children.empty() && !isTrailingComment(Tok)) - alignComments(); - - if (Tok.Type == TT_BlockComment) { - indentBlockComment(Tok, Spaces, WhitespaceStartColumn, NewLines, false); - } else if (Tok.Type == TT_LineComment && LineExceedsColumnLimit) { - StringRef Line(SourceMgr.getCharacterData(TokenLoc), - Tok.FormatTok.TokenLength); - int StartColumn = Spaces + (NewLines == 0 ? WhitespaceStartColumn : 0); - StringRef Prefix = getLineCommentPrefix(Line); - std::string NewPrefix = std::string(StartColumn, ' ') + Prefix.str(); - splitLineInComment(Tok.FormatTok, Line.substr(Prefix.size()), - StartColumn + Prefix.size(), NewPrefix, - /*InPPDirective=*/ false, - /*CommentHasMoreLines=*/ false); - } - - storeReplacement(Tok.FormatTok, getNewLineText(NewLines, Spaces)); - } - - /// \brief Like \c replaceWhitespace, but additionally adds right-aligned - /// backslashes to escape newlines inside a preprocessor directive. - /// - /// This function and \c replaceWhitespace have the same behavior if - /// \c Newlines == 0. - void replacePPWhitespace(const AnnotatedToken &Tok, unsigned NewLines, - unsigned Spaces, unsigned WhitespaceStartColumn) { - if (Tok.Type == TT_BlockComment) - indentBlockComment(Tok, Spaces, WhitespaceStartColumn, NewLines, true); - - storeReplacement(Tok.FormatTok, - getNewLineText(NewLines, Spaces, WhitespaceStartColumn)); - } - - /// \brief Inserts a line break into the middle of a token. - /// - /// Will break at \p Offset inside \p Tok, putting \p Prefix before the line - /// break and \p Postfix before the rest of the token starts in the next line. - /// - /// \p InPPDirective, \p Spaces, \p WhitespaceStartColumn and \p Style are - /// used to generate the correct line break. - void breakToken(const FormatToken &Tok, unsigned Offset, - unsigned ReplaceChars, StringRef Prefix, StringRef Postfix, - bool InPPDirective, unsigned Spaces, - unsigned WhitespaceStartColumn) { - std::string NewLineText; - if (!InPPDirective) - NewLineText = getNewLineText(1, Spaces); - else - NewLineText = getNewLineText(1, Spaces, WhitespaceStartColumn); - std::string ReplacementText = (Prefix + NewLineText + Postfix).str(); - SourceLocation Location = Tok.Tok.getLocation().getLocWithOffset(Offset); - Replaces.insert(tooling::Replacement(SourceMgr, Location, ReplaceChars, - ReplacementText)); - } - - /// \brief Returns all the \c Replacements created during formatting. - const tooling::Replacements &generateReplacements() { - alignComments(); - return Replaces; - } - - void addUntouchableComment(unsigned Column) { - StoredComment Comment; - Comment.MinColumn = Column; - Comment.MaxColumn = Column; - Comment.Untouchable = true; - Comments.push_back(Comment); - } - -private: - static StringRef getLineCommentPrefix(StringRef Comment) { - const char *KnownPrefixes[] = { "/// ", "///", "// ", "//" }; - for (size_t i = 0; i < llvm::array_lengthof(KnownPrefixes); ++i) - if (Comment.startswith(KnownPrefixes[i])) - return KnownPrefixes[i]; - return ""; - } - - /// \brief Finds a common prefix of lines of a block comment to properly - /// indent (and possibly decorate with '*'s) added lines. - /// - /// The first line is ignored (it's special and starts with /*). The number of - /// lines should be more than one. - static StringRef findCommentLinesPrefix(ArrayRef<StringRef> Lines, - const char *PrefixChars = " *") { - assert(Lines.size() > 1); - StringRef Prefix(Lines[1].data(), Lines[1].find_first_not_of(PrefixChars)); - for (size_t i = 2; i < Lines.size(); ++i) { - for (size_t j = 0; j < Prefix.size() && j < Lines[i].size(); ++j) { - if (Prefix[j] != Lines[i][j]) { - Prefix = Prefix.substr(0, j); - break; - } - } - } - return Prefix; - } - - /// \brief Splits one line in a line or block comment, if it doesn't fit to - /// provided column limit. Removes trailing whitespace in each line. - /// - /// \param Line points to the line contents without leading // or /*. - /// - /// \param StartColumn is the column where the first character of Line will be - /// located after formatting. - /// - /// \param LinePrefix is inserted after each line break. - /// - /// When \param InPPDirective is true, each line break will be preceded by a - /// backslash in the last column to make line breaks inside the comment - /// visually consistent with line breaks outside the comment. This only makes - /// sense for block comments. - /// - /// When \param CommentHasMoreLines is false, no line breaks/trailing - /// backslashes will be inserted after it. - void splitLineInComment(const FormatToken &Tok, StringRef Line, - size_t StartColumn, StringRef LinePrefix, - bool InPPDirective, bool CommentHasMoreLines, - const char *WhiteSpaceChars = " ") { - size_t ColumnLimit = calculateColumnLimit(Style, InPPDirective); - const char *TokenStart = SourceMgr.getCharacterData(Tok.Tok.getLocation()); - - StringRef TrimmedLine = Line.rtrim(); - int TrailingSpaceLength = Line.size() - TrimmedLine.size(); - - // Don't touch leading whitespace. - Line = TrimmedLine.ltrim(); - StartColumn += TrimmedLine.size() - Line.size(); - - while (Line.size() + StartColumn > ColumnLimit) { - // Try to break at the last whitespace before the column limit. - size_t SpacePos = - Line.find_last_of(WhiteSpaceChars, ColumnLimit - StartColumn + 1); - if (SpacePos == StringRef::npos) { - // Try to find any whitespace in the line. - SpacePos = Line.find_first_of(WhiteSpaceChars); - if (SpacePos == StringRef::npos) // No whitespace found, give up. - break; - } - - StringRef NextCut = Line.substr(0, SpacePos).rtrim(); - StringRef RemainingLine = Line.substr(SpacePos).ltrim(); - if (RemainingLine.empty()) - break; - - if (RemainingLine == "*/" && LinePrefix.endswith("* ")) - LinePrefix = LinePrefix.substr(0, LinePrefix.size() - 2); - - Line = RemainingLine; - - size_t ReplaceChars = Line.begin() - NextCut.end(); - breakToken(Tok, NextCut.end() - TokenStart, ReplaceChars, "", LinePrefix, - InPPDirective, 0, NextCut.size() + StartColumn); - StartColumn = LinePrefix.size(); - } - - if (TrailingSpaceLength > 0 || (InPPDirective && CommentHasMoreLines)) { - // Remove trailing whitespace/insert backslash. + 1 is for \n - breakToken(Tok, Line.end() - TokenStart, TrailingSpaceLength + 1, "", "", - InPPDirective, 0, Line.size() + StartColumn); - } - } - - /// \brief Changes indentation of all lines in a block comment by Indent, - /// removes trailing whitespace from each line, splits lines that end up - /// exceeding the column limit. - void indentBlockComment(const AnnotatedToken &Tok, int Indent, - int WhitespaceStartColumn, int NewLines, - bool InPPDirective) { - assert(Tok.Type == TT_BlockComment); - int StartColumn = Indent + (NewLines == 0 ? WhitespaceStartColumn : 0); - const SourceLocation TokenLoc = Tok.FormatTok.Tok.getLocation(); - const int CurrentIndent = SourceMgr.getSpellingColumnNumber(TokenLoc) - 1; - const int IndentDelta = Indent - CurrentIndent; - const StringRef Text(SourceMgr.getCharacterData(TokenLoc), - Tok.FormatTok.TokenLength); - assert(Text.startswith("/*") && Text.endswith("*/")); - - SmallVector<StringRef, 16> Lines; - Text.split(Lines, "\n"); - - if (IndentDelta > 0) { - std::string WhiteSpace(IndentDelta, ' '); - for (size_t i = 1; i < Lines.size(); ++i) { - Replaces.insert(tooling::Replacement( - SourceMgr, TokenLoc.getLocWithOffset(Lines[i].data() - Text.data()), - 0, WhiteSpace)); - } - } else if (IndentDelta < 0) { - std::string WhiteSpace(-IndentDelta, ' '); - // Check that the line is indented enough. - for (size_t i = 1; i < Lines.size(); ++i) { - if (!Lines[i].startswith(WhiteSpace)) - return; - } - for (size_t i = 1; i < Lines.size(); ++i) { - Replaces.insert(tooling::Replacement( - SourceMgr, TokenLoc.getLocWithOffset(Lines[i].data() - Text.data()), - -IndentDelta, "")); - } - } - - // Split long lines in comments. - size_t OldPrefixSize = 0; - std::string NewPrefix; - if (Lines.size() > 1) { - StringRef CurrentPrefix = findCommentLinesPrefix(Lines); - OldPrefixSize = CurrentPrefix.size(); - NewPrefix = (IndentDelta < 0) - ? CurrentPrefix.substr(-IndentDelta).str() - : std::string(IndentDelta, ' ') + CurrentPrefix.str(); - if (CurrentPrefix.endswith("*")) { - NewPrefix += " "; - ++OldPrefixSize; - } - } else if (Tok.Parent == 0) { - NewPrefix = std::string(StartColumn, ' ') + " * "; - } - - StartColumn += 2; - for (size_t i = 0; i < Lines.size(); ++i) { - StringRef Line = Lines[i].substr(i == 0 ? 2 : OldPrefixSize); - splitLineInComment(Tok.FormatTok, Line, StartColumn, NewPrefix, - InPPDirective, i != Lines.size() - 1); - StartColumn = NewPrefix.size(); - } - } - - std::string getNewLineText(unsigned NewLines, unsigned Spaces) { - return std::string(NewLines, '\n') + std::string(Spaces, ' '); - } - - std::string getNewLineText(unsigned NewLines, unsigned Spaces, - unsigned WhitespaceStartColumn) { - std::string NewLineText; - if (NewLines > 0) { - unsigned Offset = - std::min<int>(Style.ColumnLimit - 1, WhitespaceStartColumn); - for (unsigned i = 0; i < NewLines; ++i) { - NewLineText += std::string(Style.ColumnLimit - Offset - 1, ' '); - NewLineText += "\\\n"; - Offset = 0; - } - } - return NewLineText + std::string(Spaces, ' '); - } - - /// \brief Structure to store a comment for later layout and alignment. - struct StoredComment { - FormatToken Tok; - unsigned MinColumn; - unsigned MaxColumn; - unsigned NewLines; - unsigned Spaces; - bool Untouchable; - }; - SmallVector<StoredComment, 16> Comments; - typedef SmallVector<StoredComment, 16>::iterator comment_iterator; - - /// \brief Try to align all stashed comments. - void alignComments() { - unsigned MinColumn = 0; - unsigned MaxColumn = UINT_MAX; - comment_iterator Start = Comments.begin(); - for (comment_iterator I = Start, E = Comments.end(); I != E; ++I) { - if (I->MinColumn > MaxColumn || I->MaxColumn < MinColumn) { - alignComments(Start, I, MinColumn); - MinColumn = I->MinColumn; - MaxColumn = I->MaxColumn; - Start = I; - } else { - MinColumn = std::max(MinColumn, I->MinColumn); - MaxColumn = std::min(MaxColumn, I->MaxColumn); - } - } - alignComments(Start, Comments.end(), MinColumn); - Comments.clear(); - } - - /// \brief Put all the comments between \p I and \p E into \p Column. - void alignComments(comment_iterator I, comment_iterator E, unsigned Column) { - while (I != E) { - if (!I->Untouchable) { - unsigned Spaces = I->Spaces + Column - I->MinColumn; - storeReplacement(I->Tok, getNewLineText(I->NewLines, Spaces)); - } - ++I; - } - } - - /// \brief Stores \p Text as the replacement for the whitespace in front of - /// \p Tok. - void storeReplacement(const FormatToken &Tok, const std::string Text) { - // Don't create a replacement, if it does not change anything. - if (StringRef(SourceMgr.getCharacterData(Tok.WhiteSpaceStart), - Tok.WhiteSpaceLength) == Text) - return; - - Replaces.insert(tooling::Replacement(SourceMgr, Tok.WhiteSpaceStart, - Tok.WhiteSpaceLength, Text)); - } - - SourceManager &SourceMgr; - tooling::Replacements Replaces; - const FormatStyle &Style; -}; - class UnwrappedLineFormatter { public: UnwrappedLineFormatter(const FormatStyle &Style, SourceManager &SourceMgr, const AnnotatedLine &Line, unsigned FirstIndent, const AnnotatedToken &RootToken, - WhitespaceManager &Whitespaces, bool StructuralError) + WhitespaceManager &Whitespaces) : Style(Style), SourceMgr(SourceMgr), Line(Line), FirstIndent(FirstIndent), RootToken(RootToken), Whitespaces(Whitespaces), Count(0) {} @@ -486,16 +131,12 @@ public: State.NextToken = &RootToken; State.Stack.push_back( ParenState(FirstIndent, FirstIndent, !Style.BinPackParameters, - /*HasMultiParameterLine=*/ false)); + /*NoLineBreak=*/ false)); State.LineContainsContinuedForLoopSection = false; State.ParenLevel = 0; State.StartOfStringLiteral = 0; State.StartOfLineLevel = State.ParenLevel; - DEBUG({ - DebugTokenState(*State.NextToken); - }); - // The first token has already been indented and thus consumed. moveStateToNextToken(State, /*DryRun=*/ false); @@ -530,13 +171,13 @@ private: struct ParenState { ParenState(unsigned Indent, unsigned LastSpace, bool AvoidBinPacking, - bool HasMultiParameterLine) + bool NoLineBreak) : Indent(Indent), LastSpace(LastSpace), FirstLessLess(0), BreakBeforeClosingBrace(false), QuestionColumn(0), AvoidBinPacking(AvoidBinPacking), BreakBeforeParameter(false), - HasMultiParameterLine(HasMultiParameterLine), ColonPos(0), - StartOfFunctionCall(0), NestedNameSpecifierContinuation(0), - CallContinuation(0), VariablePos(0) {} + NoLineBreak(NoLineBreak), ColonPos(0), StartOfFunctionCall(0), + NestedNameSpecifierContinuation(0), CallContinuation(0), + VariablePos(0) {} /// \brief The position to which a specific parenthesis level needs to be /// indented. @@ -573,8 +214,8 @@ private: /// \c AvoidBinPacking is \c true). bool BreakBeforeParameter; - /// \brief This context already has a line with more than one parameter. - bool HasMultiParameterLine; + /// \brief Line breaking in this context would break a formatting rule. + bool NoLineBreak; /// \brief The position of the colon in an ObjC method declaration/call. unsigned ColonPos; @@ -610,14 +251,14 @@ private: return AvoidBinPacking; if (BreakBeforeParameter != Other.BreakBeforeParameter) return BreakBeforeParameter; - if (HasMultiParameterLine != Other.HasMultiParameterLine) - return HasMultiParameterLine; + if (NoLineBreak != Other.NoLineBreak) + return NoLineBreak; if (ColonPos != Other.ColonPos) return ColonPos < Other.ColonPos; if (StartOfFunctionCall != Other.StartOfFunctionCall) return StartOfFunctionCall < Other.StartOfFunctionCall; if (NestedNameSpecifierContinuation != - Other.NestedNameSpecifierContinuation) + Other.NestedNameSpecifierContinuation) return NestedNameSpecifierContinuation < Other.NestedNameSpecifierContinuation; if (CallContinuation != Other.CallContinuation) @@ -662,7 +303,7 @@ private: if (Column != Other.Column) return Column < Other.Column; if (LineContainsContinuedForLoopSection != - Other.LineContainsContinuedForLoopSection) + Other.LineContainsContinuedForLoopSection) return LineContainsContinuedForLoopSection; if (ParenLevel != Other.ParenLevel) return ParenLevel < Other.ParenLevel; @@ -730,7 +371,8 @@ private: State.Stack.back().VariablePos != 0) { State.Column = State.Stack.back().VariablePos; } else if (Previous.ClosesTemplateDeclaration || - (Current.Type == TT_StartOfName && State.ParenLevel == 0)) { + (Current.Type == TT_StartOfName && State.ParenLevel == 0 && + Line.StartsDefinition)) { State.Column = State.Stack.back().Indent; } else if (Current.Type == TT_ObjCSelectorName) { if (State.Stack.back().ColonPos > Current.FormatTok.TokenLength) { @@ -741,8 +383,7 @@ private: State.Stack.back().ColonPos = State.Column + Current.FormatTok.TokenLength; } - } else if (Current.Type == TT_StartOfName || Current.is(tok::question) || - Previous.is(tok::equal) || isComparison(Previous) || + } else if (Current.Type == TT_StartOfName || Previous.is(tok::equal) || Previous.Type == TT_ObjCMethodExpr) { State.Column = ContinuationIndent; } else { @@ -781,7 +422,9 @@ private: for (unsigned i = 0, e = State.Stack.size() - 1; i != e; ++i) { State.Stack[i].BreakBeforeParameter = true; } - if (Current.isOneOf(tok::period, tok::arrow)) + const AnnotatedToken *TokenBefore = Current.getPreviousNoneComment(); + if (TokenBefore && !TokenBefore->isOneOf(tok::comma, tok::semi) && + !TokenBefore->opensScope()) State.Stack.back().BreakBeforeParameter = true; // If we break after {, we should also break before the corresponding }. @@ -822,7 +465,7 @@ private: if (Current.Type == TT_ObjCSelectorName && State.Stack.back().ColonPos == 0) { if (State.Stack.back().Indent + Current.LongestObjCSelectorName > - State.Column + Spaces + Current.FormatTok.TokenLength) + State.Column + Spaces + Current.FormatTok.TokenLength) State.Stack.back().ColonPos = State.Stack.back().Indent + Current.LongestObjCSelectorName; else @@ -830,12 +473,12 @@ private: State.Column + Spaces + Current.FormatTok.TokenLength; } - if (Current.Type != TT_LineComment && - (Previous.isOneOf(tok::l_paren, tok::l_brace) || - State.NextToken->Parent->Type == TT_TemplateOpener)) + if (Previous.opensScope() && Previous.Type != TT_ObjCMethodExpr && + Current.Type != TT_LineComment) State.Stack.back().Indent = State.Column + Spaces; - if (Previous.is(tok::comma) && !isTrailingComment(Current)) - State.Stack.back().HasMultiParameterLine = true; + if (Previous.is(tok::comma) && !Current.isTrailingComment() && + State.Stack.back().AvoidBinPacking) + State.Stack.back().NoLineBreak = true; State.Column += Spaces; if (Current.is(tok::l_paren) && Previous.isOneOf(tok::kw_if, tok::kw_for)) @@ -851,9 +494,7 @@ private: State.Stack.back().LastSpace = State.Column; else if (Previous.Type == TT_InheritanceColon) State.Stack.back().Indent = State.Column; - else if (Previous.ParameterCount > 1 && - (Previous.isOneOf(tok::l_paren, tok::l_square, tok::l_brace) || - Previous.Type == TT_TemplateOpener)) + else if (Previous.opensScope() && Previous.ParameterCount > 1) // If this function has multiple parameters, indent nested calls from // the start of the first parameter. State.Stack.back().LastSpace = State.Column; @@ -879,28 +520,55 @@ private: State.Stack.back().StartOfFunctionCall = Current.LastInChainOfCalls ? 0 : State.Column; if (Current.Type == TT_CtorInitializerColon) { + State.Stack.back().Indent = State.Column + 2; if (Style.ConstructorInitializerAllOnOneLineOrOnePerLine) State.Stack.back().AvoidBinPacking = true; State.Stack.back().BreakBeforeParameter = false; } + // If return returns a binary expression, align after it. + if (Current.is(tok::kw_return) && !Current.FakeLParens.empty()) + State.Stack.back().LastSpace = State.Column + 7; + // In ObjC method declaration we align on the ":" of parameters, but we need // to ensure that we indent parameters on subsequent lines by at least 4. if (Current.Type == TT_ObjCMethodSpecifier) State.Stack.back().Indent += 4; // Insert scopes created by fake parenthesis. - for (unsigned i = 0, e = Current.FakeLParens; i != e; ++i) { + const AnnotatedToken *Previous = Current.getPreviousNoneComment(); + // Don't add extra indentation for the first fake parenthesis after + // 'return', assignements or opening <({[. The indentation for these cases + // is special cased. + bool SkipFirstExtraIndent = + Current.is(tok::kw_return) || + (Previous && (Previous->opensScope() || + getPrecedence(*Previous) == prec::Assignment)); + for (SmallVector<prec::Level, 4>::const_reverse_iterator + I = Current.FakeLParens.rbegin(), + E = Current.FakeLParens.rend(); + I != E; ++I) { ParenState NewParenState = State.Stack.back(); - NewParenState.Indent = std::max(State.Column, State.Stack.back().Indent); - NewParenState.BreakBeforeParameter = false; + NewParenState.Indent = + std::max(std::max(State.Column, NewParenState.Indent), + State.Stack.back().LastSpace); + + // Always indent conditional expressions. Never indent expression where + // the 'operator' is ',', ';' or an assignment (i.e. *I <= + // prec::Assignment) as those have different indentation rules. Indent + // other expression, unless the indentation needs to be skipped. + if (*I == prec::Conditional || + (!SkipFirstExtraIndent && *I > prec::Assignment)) + NewParenState.Indent += 4; + if (Previous && !Previous->opensScope()) + NewParenState.BreakBeforeParameter = false; State.Stack.push_back(NewParenState); + SkipFirstExtraIndent = false; } // If we encounter an opening (, [, { or <, we add a level to our stacks to // prepare for the following tokens. - if (Current.isOneOf(tok::l_paren, tok::l_square, tok::l_brace) || - State.NextToken->Type == TT_TemplateOpener) { + if (Current.opensScope()) { unsigned NewIndent; bool AvoidBinPacking; if (Current.is(tok::l_brace)) { @@ -909,12 +577,20 @@ private: } else { NewIndent = 4 + std::max(State.Stack.back().LastSpace, State.Stack.back().StartOfFunctionCall); - AvoidBinPacking = - !Style.BinPackParameters || State.Stack.back().AvoidBinPacking; + AvoidBinPacking = !Style.BinPackParameters; } State.Stack.push_back( ParenState(NewIndent, State.Stack.back().LastSpace, AvoidBinPacking, - State.Stack.back().HasMultiParameterLine)); + State.Stack.back().NoLineBreak)); + + if (Current.NoMoreTokensOnLevel && Current.FakeLParens.empty()) { + // This parenthesis was the last token possibly making use of Indent and + // LastSpace of the next higher ParenLevel. Thus, erase them to acieve + // better memoization results. + State.Stack[State.Stack.size() - 2].Indent = 0; + State.Stack[State.Stack.size() - 2].LastSpace = 0; + } + ++State.ParenLevel; } @@ -962,115 +638,74 @@ private: /// it if possible. unsigned breakProtrudingToken(const AnnotatedToken &Current, LineState &State, bool DryRun) { - if (Current.isNot(tok::string_literal)) - return 0; - // Only break up default narrow strings. - const char *LiteralData = Current.FormatTok.Tok.getLiteralData(); - if (!LiteralData || *LiteralData != '"') + llvm::OwningPtr<BreakableToken> Token; + unsigned StartColumn = State.Column - Current.FormatTok.TokenLength; + if (Current.is(tok::string_literal)) { + // Only break up default narrow strings. + const char *LiteralData = SourceMgr.getCharacterData( + Current.FormatTok.getStartOfNonWhitespace()); + if (!LiteralData || *LiteralData != '"') + return 0; + + Token.reset(new BreakableStringLiteral(SourceMgr, Current.FormatTok, + StartColumn)); + } else if (Current.Type == TT_BlockComment) { + BreakableBlockComment *BBC = + new BreakableBlockComment(SourceMgr, Current, StartColumn); + if (!DryRun) + BBC->alignLines(Whitespaces); + Token.reset(BBC); + } else if (Current.Type == TT_LineComment && + (Current.Parent == NULL || + Current.Parent->Type != TT_ImplicitStringLiteral)) { + Token.reset(new BreakableLineComment(SourceMgr, Current, StartColumn)); + } else { return 0; + } + bool BreakInserted = false; unsigned Penalty = 0; - unsigned TailOffset = 0; - unsigned TailLength = Current.FormatTok.TokenLength; - unsigned StartColumn = State.Column - Current.FormatTok.TokenLength; - unsigned OffsetFromStart = 0; - while (StartColumn + TailLength > getColumnLimit()) { - StringRef Text = StringRef(LiteralData + TailOffset, TailLength); - if (StartColumn + OffsetFromStart + 1 > getColumnLimit()) - break; - StringRef::size_type SplitPoint = getSplitPoint( - Text, getColumnLimit() - StartColumn - OffsetFromStart - 1); - if (SplitPoint == StringRef::npos) - break; - assert(SplitPoint != 0); - // +2, because 'Text' starts after the opening quotes, and does not - // include the closing quote we need to insert. - unsigned WhitespaceStartColumn = - StartColumn + OffsetFromStart + SplitPoint + 2; - State.Stack.back().LastSpace = StartColumn; + for (unsigned LineIndex = 0; LineIndex < Token->getLineCount(); + ++LineIndex) { + unsigned TailOffset = 0; + unsigned RemainingLength = + Token->getLineLengthAfterSplit(LineIndex, TailOffset); + while (RemainingLength > getColumnLimit()) { + BreakableToken::Split Split = + Token->getSplit(LineIndex, TailOffset, getColumnLimit()); + if (Split.first == StringRef::npos) + break; + assert(Split.first != 0); + unsigned NewRemainingLength = Token->getLineLengthAfterSplit( + LineIndex, TailOffset + Split.first + Split.second); + if (NewRemainingLength >= RemainingLength) + break; + if (!DryRun) { + Token->insertBreak(LineIndex, TailOffset, Split, Line.InPPDirective, + Whitespaces); + } + TailOffset += Split.first + Split.second; + RemainingLength = NewRemainingLength; + Penalty += Style.PenaltyExcessCharacter; + BreakInserted = true; + } + State.Column = RemainingLength; if (!DryRun) { - Whitespaces.breakToken(Current.FormatTok, TailOffset + SplitPoint + 1, - 0, "\"", "\"", Line.InPPDirective, StartColumn, - WhitespaceStartColumn); + Token->trimLine(LineIndex, TailOffset, Line.InPPDirective, Whitespaces); } - TailOffset += SplitPoint + 1; - TailLength -= SplitPoint + 1; - OffsetFromStart = 1; - Penalty += Style.PenaltyExcessCharacter; + } + + if (BreakInserted) { for (unsigned i = 0, e = State.Stack.size(); i != e; ++i) State.Stack[i].BreakBeforeParameter = true; + State.Stack.back().LastSpace = StartColumn; } - State.Column = StartColumn + TailLength; return Penalty; } - StringRef::size_type - getSplitPoint(StringRef Text, StringRef::size_type Offset) { - StringRef::size_type SpaceOffset = Text.rfind(' ', Offset); - if (SpaceOffset != StringRef::npos && SpaceOffset != 0) - return SpaceOffset; - StringRef::size_type SlashOffset = Text.rfind('/', Offset); - if (SlashOffset != StringRef::npos && SlashOffset != 0) - return SlashOffset; - StringRef::size_type Split = getStartOfCharacter(Text, Offset); - if (Split != StringRef::npos && Split > 1) - // Do not split at 0. - return Split - 1; - return StringRef::npos; - } - - StringRef::size_type - getStartOfCharacter(StringRef Text, StringRef::size_type Offset) { - StringRef::size_type NextEscape = Text.find('\\'); - while (NextEscape != StringRef::npos && NextEscape < Offset) { - StringRef::size_type SequenceLength = - getEscapeSequenceLength(Text.substr(NextEscape)); - if (Offset < NextEscape + SequenceLength) - return NextEscape; - NextEscape = Text.find('\\', NextEscape + SequenceLength); - } - return Offset; - } - - unsigned getEscapeSequenceLength(StringRef Text) { - assert(Text[0] == '\\'); - if (Text.size() < 2) - return 1; - - switch (Text[1]) { - case 'u': - return 6; - case 'U': - return 10; - case 'x': - return getHexLength(Text); - default: - if (Text[1] >= '0' && Text[1] <= '7') - return getOctalLength(Text); - return 2; - } - } - - unsigned getHexLength(StringRef Text) { - unsigned I = 2; // Point after '\x'. - while (I < Text.size() && ((Text[I] >= '0' && Text[I] <= '9') || - (Text[I] >= 'a' && Text[I] <= 'f') || - (Text[I] >= 'A' && Text[I] <= 'F'))) { - ++I; - } - return I; - } - - unsigned getOctalLength(StringRef Text) { - unsigned I = 1; - while (I < Text.size() && I < 4 && (Text[I] >= '0' && Text[I] <= '7')) { - ++I; - } - return I; - } - unsigned getColumnLimit() { - return calculateColumnLimit(Style, Line.InPPDirective); + // In preprocessor directives reserve two chars for trailing " \" + return Style.ColumnLimit - (Line.InPPDirective ? 2 : 0); } /// \brief An edge in the solution space from \c Previous->State to \c State, @@ -1194,12 +829,7 @@ private: !(State.NextToken->is(tok::r_brace) && State.Stack.back().BreakBeforeClosingBrace)) return false; - // Trying to insert a parameter on a new line if there are already more than - // one parameter on the current line is bin packing. - if (State.Stack.back().HasMultiParameterLine && - State.Stack.back().AvoidBinPacking) - return false; - return true; + return !State.Stack.back().NoLineBreak; } /// \brief Returns \c true, if a line break after \p State is mandatory. @@ -1216,7 +846,7 @@ private: State.NextToken->is(tok::question) || State.NextToken->Type == TT_ConditionalExpr) && State.Stack.back().BreakBeforeParameter && - !isTrailingComment(*State.NextToken) && + !State.NextToken->isTrailingComment() && State.NextToken->isNot(tok::r_paren) && State.NextToken->isNot(tok::r_brace)) return true; @@ -1310,6 +940,11 @@ public: // Now FormatTok is the next non-whitespace token. FormatTok.TokenLength = Text.size(); + if (FormatTok.Tok.is(tok::comment)) { + FormatTok.TrailingWhiteSpaceLength = Text.size() - Text.rtrim().size(); + FormatTok.TokenLength -= FormatTok.TrailingWhiteSpaceLength; + } + // In case the token starts with escaped newlines, we want to // take them into account as whitespace - this pattern is quite frequent // in macro definitions. @@ -1336,11 +971,6 @@ public: GreaterStashed = true; } - // If we reformat comments, we remove trailing whitespace. Update the length - // accordingly. - if (FormatTok.Tok.is(tok::comment)) - FormatTok.TokenLength = Text.rtrim().size(); - return FormatTok; } @@ -1373,7 +1003,7 @@ public: tooling::Replacements format() { LexerBasedFormatTokenSource Tokens(Lex, SourceMgr); UnwrappedLineParser Parser(Diag, Style, Tokens, *this); - StructuralError = Parser.parse(); + bool StructuralError = Parser.parse(); unsigned PreviousEndOfLineColumn = 0; TokenAnnotator Annotator(Style, SourceMgr, Lex, Tokens.getIdentTable().get("in")); @@ -1383,14 +1013,21 @@ public: deriveLocalStyle(); for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) { Annotator.calculateFormattingInformation(AnnotatedLines[i]); + } - // Adapt level to the next line if this is a comment. - // FIXME: Can/should this be done in the UnwrappedLineParser? - if (i + 1 != e && AnnotatedLines[i].First.is(tok::comment) && - AnnotatedLines[i].First.Children.empty() && - AnnotatedLines[i + 1].First.isNot(tok::r_brace)) - AnnotatedLines[i].Level = AnnotatedLines[i + 1].Level; + // Adapt level to the next line if this is a comment. + // FIXME: Can/should this be done in the UnwrappedLineParser? + const AnnotatedLine *NextNoneCommentLine = NULL; + for (unsigned i = AnnotatedLines.size() - 1; i > 0; --i) { + if (NextNoneCommentLine && AnnotatedLines[i].First.is(tok::comment) && + AnnotatedLines[i].First.Children.empty()) + AnnotatedLines[i].Level = NextNoneCommentLine->Level; + else + NextNoneCommentLine = + AnnotatedLines[i].First.isNot(tok::r_brace) ? &AnnotatedLines[i] + : NULL; } + std::vector<int> IndentForLevel; bool PreviousLineWasTouched = false; const AnnotatedToken *PreviousLineLastToken = 0; @@ -1416,17 +1053,19 @@ public: unsigned Indent = LevelIndent; if (static_cast<int>(Indent) + Offset >= 0) Indent += Offset; - if (!FirstTok.WhiteSpaceStart.isValid() || StructuralError) { - Indent = LevelIndent = - SourceMgr.getSpellingColumnNumber(FirstTok.Tok.getLocation()) - 1; - } else { + if (FirstTok.WhiteSpaceStart.isValid() && + // Insert a break even if there is a structural error in case where + // we break apart a line consisting of multiple unwrapped lines. + (FirstTok.NewlinesBefore == 0 || !StructuralError)) { formatFirstToken(TheLine.First, PreviousLineLastToken, Indent, TheLine.InPPDirective, PreviousEndOfLineColumn); + } else { + Indent = LevelIndent = + SourceMgr.getSpellingColumnNumber(FirstTok.Tok.getLocation()) - 1; } tryFitMultipleLinesInOne(Indent, I, E); UnwrappedLineFormatter Formatter(Style, SourceMgr, TheLine, Indent, - TheLine.First, Whitespaces, - StructuralError); + TheLine.First, Whitespaces); PreviousEndOfLineColumn = Formatter.format(I + 1 != E ? &*(I + 1) : NULL); IndentForLevel[TheLine.Level] = LevelIndent; @@ -1457,6 +1096,8 @@ public: if (TheLine.Last->is(tok::comment)) Whitespaces.addUntouchableComment(SourceMgr.getSpellingColumnNumber( TheLine.Last->FormatTok.Tok.getLocation()) - 1); + else + Whitespaces.alignComments(); } PreviousLineLastToken = I->Last; } @@ -1727,12 +1368,12 @@ private: WhitespaceManager Whitespaces; std::vector<CharSourceRange> Ranges; std::vector<AnnotatedLine> AnnotatedLines; - bool StructuralError; }; -tooling::Replacements -reformat(const FormatStyle &Style, Lexer &Lex, SourceManager &SourceMgr, - std::vector<CharSourceRange> Ranges, DiagnosticConsumer *DiagClient) { +tooling::Replacements reformat(const FormatStyle &Style, Lexer &Lex, + SourceManager &SourceMgr, + std::vector<CharSourceRange> Ranges, + DiagnosticConsumer *DiagClient) { IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); OwningPtr<DiagnosticConsumer> DiagPrinter; if (DiagClient == 0) { |