diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:50:49 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:50:49 +0000 |
commit | 2298981669bf3bd63335a4be179bc0f96823a8f4 (patch) | |
tree | 1cbe2eb27f030d2d70b80ee5ca3c86bee7326a9f /lib/Format | |
parent | 9a83721404652cea39e9f02ae3e3b5c964602a5c (diff) |
Diffstat (limited to 'lib/Format')
29 files changed, 1203 insertions, 417 deletions
diff --git a/lib/Format/AffectedRangeManager.cpp b/lib/Format/AffectedRangeManager.cpp index b14316a14cd9..7ad1f7070d0a 100644 --- a/lib/Format/AffectedRangeManager.cpp +++ b/lib/Format/AffectedRangeManager.cpp @@ -1,9 +1,8 @@ //===--- AffectedRangeManager.cpp - Format C++ code -----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// diff --git a/lib/Format/AffectedRangeManager.h b/lib/Format/AffectedRangeManager.h index b0c9dd259fb8..8cf39443fd41 100644 --- a/lib/Format/AffectedRangeManager.h +++ b/lib/Format/AffectedRangeManager.h @@ -1,9 +1,8 @@ //===--- AffectedRangeManager.h - Format C++ code ---------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// diff --git a/lib/Format/BreakableToken.cpp b/lib/Format/BreakableToken.cpp index e6ce01b520b5..72886ed00736 100644 --- a/lib/Format/BreakableToken.cpp +++ b/lib/Format/BreakableToken.cpp @@ -1,9 +1,8 @@ //===--- BreakableToken.cpp - Format C++ code -----------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -63,12 +62,11 @@ static StringRef getLineCommentIndentPrefix(StringRef Comment, return LongestPrefix; } -static BreakableToken::Split getCommentSplit(StringRef Text, - unsigned ContentStartColumn, - unsigned ColumnLimit, - unsigned TabWidth, - encoding::Encoding Encoding, - const FormatStyle &Style) { +static BreakableToken::Split +getCommentSplit(StringRef Text, unsigned ContentStartColumn, + unsigned ColumnLimit, unsigned TabWidth, + encoding::Encoding Encoding, const FormatStyle &Style, + bool DecorationEndsWithStar = false) { LLVM_DEBUG(llvm::dbgs() << "Comment split: \"" << Text << "\", Column limit: " << ColumnLimit << ", Content start: " << ContentStartColumn << "\n"); @@ -126,7 +124,10 @@ static BreakableToken::Split getCommentSplit(StringRef Text, if (SpaceOffset == 1 && Text[SpaceOffset - 1] == '*') return BreakableToken::Split(StringRef::npos, 0); StringRef BeforeCut = Text.substr(0, SpaceOffset).rtrim(Blanks); - StringRef AfterCut = Text.substr(SpaceOffset).ltrim(Blanks); + StringRef AfterCut = Text.substr(SpaceOffset); + // Don't trim the leading blanks if it would create a */ after the break. + if (!DecorationEndsWithStar || AfterCut.size() <= 1 || AfterCut[1] != '/') + AfterCut = AfterCut.ltrim(Blanks); return BreakableToken::Split(BeforeCut.size(), AfterCut.begin() - BeforeCut.end()); } @@ -192,7 +193,7 @@ bool switchesFormatting(const FormatToken &Token) { unsigned BreakableToken::getLengthAfterCompression(unsigned RemainingTokenColumns, - Split Split) const { + Split Split) const { // Example: consider the content // lala lala // - RemainingTokenColumns is the original number of columns, 10; @@ -332,7 +333,7 @@ static bool mayReflowContent(StringRef Content) { BreakableBlockComment::BreakableBlockComment( const FormatToken &Token, unsigned StartColumn, unsigned OriginalStartColumn, bool FirstInLine, bool InPPDirective, - encoding::Encoding Encoding, const FormatStyle &Style) + encoding::Encoding Encoding, const FormatStyle &Style, bool UseCRLF) : BreakableComment(Token, StartColumn, InPPDirective, Encoding, Style), DelimitersOnNewline(false), UnbreakableTailLength(Token.UnbreakableTailLength) { @@ -341,7 +342,8 @@ BreakableBlockComment::BreakableBlockComment( StringRef TokenText(Tok.TokenText); assert(TokenText.startswith("/*") && TokenText.endswith("*/")); - TokenText.substr(2, TokenText.size() - 4).split(Lines, "\n"); + TokenText.substr(2, TokenText.size() - 4).split(Lines, + UseCRLF ? "\r\n" : "\n"); int IndentDelta = StartColumn - OriginalStartColumn; Content.resize(Lines.size()); @@ -454,6 +456,18 @@ BreakableBlockComment::BreakableBlockComment( }); } +BreakableToken::Split +BreakableBlockComment::getSplit(unsigned LineIndex, unsigned TailOffset, + unsigned ColumnLimit, unsigned ContentStartColumn, + llvm::Regex &CommentPragmasRegex) const { + // Don't break lines matching the comment pragmas regex. + if (CommentPragmasRegex.match(Content[LineIndex])) + return Split(StringRef::npos, 0); + return getCommentSplit(Content[LineIndex].substr(TailOffset), + ContentStartColumn, ColumnLimit, Style.TabWidth, + Encoding, Style, Decoration.endswith("*")); +} + void BreakableBlockComment::adjustWhitespace(unsigned LineIndex, int IndentDelta) { // When in a preprocessor directive, the trailing backslash in a block comment @@ -475,7 +489,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].rtrim("\r\n").size(); + StartOfLine = Lines[LineIndex].size(); StringRef Whitespace = Lines[LineIndex].substr(0, StartOfLine); // Adjust Lines to only contain relevant text. @@ -871,23 +885,20 @@ void BreakableLineCommentSection::reflow(unsigned LineIndex, // the next line. unsigned WhitespaceLength = Lines[LineIndex].data() - tokenAt(LineIndex).TokenText.data() - Offset; - Whitespaces.replaceWhitespaceInToken(*Tokens[LineIndex], - Offset, + Whitespaces.replaceWhitespaceInToken(*Tokens[LineIndex], Offset, /*ReplaceChars=*/WhitespaceLength, /*PreviousPostfix=*/"", /*CurrentPrefix=*/"", /*InPPDirective=*/false, /*Newlines=*/0, /*Spaces=*/0); - } // Replace the indent and prefix of the token with the reflow prefix. unsigned Offset = Lines[LineIndex].data() - tokenAt(LineIndex).TokenText.data(); unsigned WhitespaceLength = Content[LineIndex].data() - Lines[LineIndex].data(); - Whitespaces.replaceWhitespaceInToken(*Tokens[LineIndex], - Offset, + Whitespaces.replaceWhitespaceInToken(*Tokens[LineIndex], Offset, /*ReplaceChars=*/WhitespaceLength, /*PreviousPostfix=*/"", /*CurrentPrefix=*/ReflowPrefix, diff --git a/lib/Format/BreakableToken.h b/lib/Format/BreakableToken.h index 10e180178021..72852d59f9c4 100644 --- a/lib/Format/BreakableToken.h +++ b/lib/Format/BreakableToken.h @@ -1,9 +1,8 @@ //===--- BreakableToken.h - Format C++ code ---------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -147,9 +146,7 @@ public: // * @param loooooooooooooong line // * continuation // */ - virtual unsigned getContentIndent(unsigned LineIndex) const { - return 0; - } + virtual unsigned getContentIndent(unsigned LineIndex) const { return 0; } /// Returns a range (offset, length) at which to break the line at /// \p LineIndex, if previously broken at \p TailOffset. If possible, do not @@ -203,9 +200,7 @@ public: /// Returns whether there will be a line break at the start of the /// token. - virtual bool introducesBreakBeforeToken() const { - return false; - } + virtual bool introducesBreakBeforeToken() const { return false; } /// Replaces the whitespace between \p LineIndex-1 and \p LineIndex. virtual void adaptStartOfLine(unsigned LineIndex, @@ -364,8 +359,11 @@ public: BreakableBlockComment(const FormatToken &Token, unsigned StartColumn, unsigned OriginalStartColumn, bool FirstInLine, bool InPPDirective, encoding::Encoding Encoding, - const FormatStyle &Style); + const FormatStyle &Style, bool UseCRLF); + Split getSplit(unsigned LineIndex, unsigned TailOffset, unsigned ColumnLimit, + unsigned ContentStartColumn, + llvm::Regex &CommentPragmasRegex) const override; unsigned getRangeLength(unsigned LineIndex, unsigned Offset, StringRef::size_type Length, unsigned StartColumn) const override; diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index c369b94b9987..b04ede6fa939 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -1,9 +1,8 @@ //===--- ContinuationIndenter.cpp - Format C++ code -----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -404,8 +403,7 @@ bool ContinuationIndenter::mustBreak(const LineState &State) { // FIXME: We should find a more generic solution to this problem. !(State.Column <= NewLineColumn && Style.Language == FormatStyle::LK_JavaScript) && - !(Previous.closesScopeAfterBlock() && - State.Column <= NewLineColumn)) + !(Previous.closesScopeAfterBlock() && State.Column <= NewLineColumn)) return true; // If the template declaration spans multiple lines, force wrap before the @@ -420,7 +418,8 @@ bool ContinuationIndenter::mustBreak(const LineState &State) { 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(tok::kw_return, tok::lessless, tok::at, + Keywords.kw_dollar) && !Previous.isOneOf(TT_InlineASMColon, TT_ConditionalExpr) && nextIsMultilineString(State)) return true; @@ -836,8 +835,8 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State, // about removing empty lines on closing blocks. Special case them here. MaxEmptyLinesToKeep = 1; } - unsigned Newlines = std::max( - 1u, std::min(Current.NewlinesBefore, MaxEmptyLinesToKeep)); + unsigned Newlines = + std::max(1u, std::min(Current.NewlinesBefore, MaxEmptyLinesToKeep)); bool ContinuePPDirective = State.Line->InPPDirective && State.Line->Type != LT_ImportStatement; Whitespaces.replaceWhitespace(Current, Newlines, State.Column, State.Column, @@ -882,14 +881,30 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State, State.Stack.back().BreakBeforeClosingBrace = true; if (State.Stack.back().AvoidBinPacking) { - // If we are breaking after '(', '{', '<', this is not bin packing - // unless AllowAllParametersOfDeclarationOnNextLine is false or this is a - // dict/object literal. - if (!Previous.isOneOf(tok::l_paren, tok::l_brace, TT_BinaryOperator) || + // If we are breaking after '(', '{', '<', or this is the break after a ':' + // to start a member initializater list in a constructor, this should not + // be considered bin packing unless the relevant AllowAll option is false or + // this is a dict/object literal. + bool PreviousIsBreakingCtorInitializerColon = + Previous.is(TT_CtorInitializerColon) && + Style.BreakConstructorInitializers == FormatStyle::BCIS_AfterColon; + if (!(Previous.isOneOf(tok::l_paren, tok::l_brace, TT_BinaryOperator) || + PreviousIsBreakingCtorInitializerColon) || (!Style.AllowAllParametersOfDeclarationOnNextLine && State.Line->MustBeDeclaration) || + (!Style.AllowAllArgumentsOnNextLine && + !State.Line->MustBeDeclaration) || + (!Style.AllowAllConstructorInitializersOnNextLine && + PreviousIsBreakingCtorInitializerColon) || Previous.is(TT_DictLiteral)) State.Stack.back().BreakBeforeParameter = true; + + // If we are breaking after a ':' to start a member initializer list, + // and we allow all arguments on the next line, we should not break + // before the next parameter. + if (PreviousIsBreakingCtorInitializerColon && + Style.AllowAllConstructorInitializersOnNextLine) + State.Stack.back().BreakBeforeParameter = false; } return Penalty; @@ -930,18 +945,24 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { return State.Stack[State.Stack.size() - 2].LastSpace; return State.FirstIndent; } - // Indent a closing parenthesis at the previous level if followed by a semi or - // opening brace. This allows indentations such as: + // Indent a closing parenthesis at the previous level if followed by a semi, + // const, or opening brace. This allows indentations such as: // foo( // a, // ); + // int Foo::getter( + // // + // ) const { + // return foo; + // } // function foo( // a, // ) { // code(); // // } if (Current.is(tok::r_paren) && State.Stack.size() > 1 && - (!Current.Next || Current.Next->isOneOf(tok::semi, tok::l_brace))) + (!Current.Next || + Current.Next->isOneOf(tok::semi, tok::kw_const, tok::l_brace))) return State.Stack[State.Stack.size() - 2].LastSpace; if (NextNonComment->is(TT_TemplateString) && NextNonComment->closesScope()) return State.Stack[State.Stack.size() - 2].LastSpace; @@ -1042,7 +1063,7 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { if (Current.is(TT_ProtoExtensionLSquare)) return State.Stack.back().Indent; if (State.Stack.back().Indent == State.FirstIndent && PreviousNonComment && - PreviousNonComment->isNot(tok::r_brace)) + !PreviousNonComment->isOneOf(tok::r_brace, TT_CtorInitializerComma)) // Ensure that we fall back to the continuation indent width instead of // just flushing continuations left. return State.Stack.back().Indent + Style.ContinuationIndentWidth; @@ -1103,9 +1124,13 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State, ? 0 : 2); State.Stack.back().NestedBlockIndent = State.Stack.back().Indent; - if (Style.ConstructorInitializerAllOnOneLineOrOnePerLine) + if (Style.ConstructorInitializerAllOnOneLineOrOnePerLine) { State.Stack.back().AvoidBinPacking = true; - State.Stack.back().BreakBeforeParameter = false; + State.Stack.back().BreakBeforeParameter = + !Style.AllowAllConstructorInitializersOnNextLine; + } else { + State.Stack.back().BreakBeforeParameter = false; + } } if (Current.is(TT_CtorInitializerColon) && Style.BreakConstructorInitializers == FormatStyle::BCIS_AfterColon) { @@ -1160,6 +1185,8 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State, if (Current.is(TT_ObjCStringLiteral) && State.StartOfStringLiteral == 0) State.StartOfStringLiteral = State.Column + 1; + if (Current.is(TT_CSharpStringLiteral) && State.StartOfStringLiteral == 0) + State.StartOfStringLiteral = State.Column + 1; else if (Current.isStringLiteral() && State.StartOfStringLiteral == 0) State.StartOfStringLiteral = State.Column; else if (!Current.isOneOf(tok::comment, tok::identifier, tok::hash) && @@ -1170,7 +1197,7 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State, State.NextToken = State.NextToken->Next; unsigned Penalty = - handleEndOfLine(Current, State, DryRun, AllowBreak); + handleEndOfLine(Current, State, DryRun, AllowBreak, Newline); if (Current.Role) Current.Role->formatFromToken(State, this, DryRun); @@ -1464,7 +1491,7 @@ static unsigned getLastLineEndColumn(StringRef Text, unsigned StartColumn, unsigned ContinuationIndenter::reformatRawStringLiteral( const FormatToken &Current, LineState &State, - const FormatStyle &RawStringStyle, bool DryRun) { + const FormatStyle &RawStringStyle, bool DryRun, bool Newline) { unsigned StartColumn = State.Column - Current.ColumnWidth; StringRef OldDelimiter = *getRawStringDelimiter(Current.TokenText); StringRef NewDelimiter = @@ -1504,8 +1531,10 @@ unsigned ContinuationIndenter::reformatRawStringLiteral( // source. bool ContentStartsOnNewline = Current.TokenText[OldPrefixSize] == '\n'; // If this token is the last parameter (checked by looking if it's followed by - // `)`, the base the indent off the line's nested block indent. Otherwise, - // base the indent off the arguments indent, so we can achieve: + // `)` and is not on a newline, the base the indent off the line's nested + // block indent. Otherwise, base the indent off the arguments indent, so we + // can achieve: + // // fffffffffff(1, 2, 3, R"pb( // key1: 1 # // key2: 2)pb"); @@ -1514,11 +1543,18 @@ unsigned ContinuationIndenter::reformatRawStringLiteral( // R"pb( // key1: 1 # // key2: 2 + // )pb"); + // + // fffffffffff(1, 2, 3, + // R"pb( + // key1: 1 # + // key2: 2 // )pb", // 5); - unsigned CurrentIndent = (Current.Next && Current.Next->is(tok::r_paren)) - ? State.Stack.back().NestedBlockIndent - : State.Stack.back().Indent; + unsigned CurrentIndent = + (!Newline && Current.Next && Current.Next->is(tok::r_paren)) + ? State.Stack.back().NestedBlockIndent + : State.Stack.back().Indent; unsigned NextStartColumn = ContentStartsOnNewline ? CurrentIndent + Style.IndentWidth : FirstStartColumn; @@ -1531,9 +1567,8 @@ unsigned ContinuationIndenter::reformatRawStringLiteral( // that raw string prefix starts, and // - if the raw string prefix does not start on a newline, it is the current // indent. - unsigned LastStartColumn = Current.NewlinesBefore - ? FirstStartColumn - NewPrefixSize - : CurrentIndent; + unsigned LastStartColumn = + Current.NewlinesBefore ? FirstStartColumn - NewPrefixSize : CurrentIndent; std::pair<tooling::Replacements, unsigned> Fixes = internal::reformat( RawStringStyle, RawText, {tooling::Range(0, RawText.size())}, @@ -1590,8 +1625,9 @@ unsigned ContinuationIndenter::reformatRawStringLiteral( // have to manually add the penalty for the prefix R"delim( over the column // limit. unsigned PrefixExcessCharacters = - StartColumn + NewPrefixSize > Style.ColumnLimit ? - StartColumn + NewPrefixSize - Style.ColumnLimit : 0; + StartColumn + NewPrefixSize > Style.ColumnLimit + ? StartColumn + NewPrefixSize - Style.ColumnLimit + : 0; bool IsMultiline = ContentStartsOnNewline || (NewCode->find('\n') != std::string::npos); if (IsMultiline) { @@ -1620,13 +1656,14 @@ unsigned ContinuationIndenter::addMultilineToken(const FormatToken &Current, unsigned ContinuationIndenter::handleEndOfLine(const FormatToken &Current, LineState &State, bool DryRun, - bool AllowBreak) { + bool AllowBreak, bool Newline) { unsigned Penalty = 0; // Compute the raw string style to use in case this is a raw string literal // that can be reformatted. auto RawStringStyle = getRawStringStyle(Current, State); if (RawStringStyle && !Current.Finalized) { - Penalty = reformatRawStringLiteral(Current, State, *RawStringStyle, DryRun); + Penalty = reformatRawStringLiteral(Current, State, *RawStringStyle, DryRun, + Newline); } else if (Current.IsMultiline && Current.isNot(TT_BlockComment)) { // Don't break multi-line tokens other than block comments and raw string // literals. Instead, just update the state. @@ -1709,16 +1746,17 @@ ContinuationIndenter::getRawStringStyle(const FormatToken &Current, return RawStringStyle; } -std::unique_ptr<BreakableToken> ContinuationIndenter::createBreakableToken( - const FormatToken &Current, LineState &State, bool AllowBreak) { +std::unique_ptr<BreakableToken> +ContinuationIndenter::createBreakableToken(const FormatToken &Current, + LineState &State, bool AllowBreak) { unsigned StartColumn = State.Column - Current.ColumnWidth; if (Current.isStringLiteral()) { - // FIXME: String literal breaking is currently disabled for Java and JS, as - // it requires strings to be merged using "+" which we don't support. + // FIXME: String literal breaking is currently disabled for C#,Java and + // JavaScript, as it requires strings to be merged using "+" which we + // don't support. if (Style.Language == FormatStyle::LK_Java || - Style.Language == FormatStyle::LK_JavaScript || - !Style.BreakStringLiterals || - !AllowBreak) + Style.Language == FormatStyle::LK_JavaScript || Style.isCSharp() || + !Style.BreakStringLiterals || !AllowBreak) return nullptr; // Don't break string literals inside preprocessor directives (except for @@ -1772,7 +1810,7 @@ std::unique_ptr<BreakableToken> ContinuationIndenter::createBreakableToken( } return llvm::make_unique<BreakableBlockComment>( Current, StartColumn, Current.OriginalColumn, !Current.Previous, - State.Line->InPPDirective, Encoding, Style); + State.Line->InPPDirective, Encoding, Style, Whitespaces.useCRLF()); } else if (Current.is(TT_LineComment) && (Current.Previous == nullptr || Current.Previous->isNot(TT_ImplicitStringLiteral))) { diff --git a/lib/Format/ContinuationIndenter.h b/lib/Format/ContinuationIndenter.h index fde89db864b1..11df619e0f40 100644 --- a/lib/Format/ContinuationIndenter.h +++ b/lib/Format/ContinuationIndenter.h @@ -1,9 +1,8 @@ //===--- ContinuationIndenter.h - Format C++ code ---------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -112,12 +111,12 @@ private: unsigned reformatRawStringLiteral(const FormatToken &Current, LineState &State, const FormatStyle &RawStringStyle, - bool DryRun); + bool DryRun, bool Newline); /// If the current token is at the end of the current line, handle /// the transition to the next line. unsigned handleEndOfLine(const FormatToken &Current, LineState &State, - bool DryRun, bool AllowBreak); + bool DryRun, bool AllowBreak, bool Newline); /// If \p Current is a raw string that is configured to be reformatted, /// return the style to be used. diff --git a/lib/Format/Encoding.h b/lib/Format/Encoding.h index 4c877e7e49d5..fe3d5f019858 100644 --- a/lib/Format/Encoding.h +++ b/lib/Format/Encoding.h @@ -1,9 +1,8 @@ //===--- Encoding.h - Format C++ code ---------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index 2c4f8760540a..c48182976b04 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -1,9 +1,8 @@ //===--- Format.cpp - Format C++ code -------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -62,6 +61,7 @@ template <> struct ScalarEnumerationTraits<FormatStyle::LanguageKind> { IO.enumCase(Value, "Proto", FormatStyle::LK_Proto); IO.enumCase(Value, "TableGen", FormatStyle::LK_TableGen); IO.enumCase(Value, "TextProto", FormatStyle::LK_TextProto); + IO.enumCase(Value, "CSharp", FormatStyle::LK_CSharp); } }; @@ -107,6 +107,29 @@ template <> struct ScalarEnumerationTraits<FormatStyle::ShortFunctionStyle> { } }; +template <> struct ScalarEnumerationTraits<FormatStyle::ShortIfStyle> { + static void enumeration(IO &IO, FormatStyle::ShortIfStyle &Value) { + IO.enumCase(Value, "Never", FormatStyle::SIS_Never); + IO.enumCase(Value, "Always", FormatStyle::SIS_Always); + IO.enumCase(Value, "WithoutElse", FormatStyle::SIS_WithoutElse); + + // For backward compatibility. + IO.enumCase(Value, "false", FormatStyle::SIS_Never); + IO.enumCase(Value, "true", FormatStyle::SIS_WithoutElse); + } +}; + +template <> struct ScalarEnumerationTraits<FormatStyle::ShortLambdaStyle> { + static void enumeration(IO &IO, FormatStyle::ShortLambdaStyle &Value) { + IO.enumCase(Value, "None", FormatStyle::SLS_None); + IO.enumCase(Value, "false", FormatStyle::SLS_None); + IO.enumCase(Value, "Empty", FormatStyle::SLS_Empty); + IO.enumCase(Value, "Inline", FormatStyle::SLS_Inline); + IO.enumCase(Value, "All", FormatStyle::SLS_All); + IO.enumCase(Value, "true", FormatStyle::SLS_All); + } +}; + template <> struct ScalarEnumerationTraits<FormatStyle::BinPackStyle> { static void enumeration(IO &IO, FormatStyle::BinPackStyle &Value) { IO.enumCase(Value, "Auto", FormatStyle::BPS_Auto); @@ -150,8 +173,8 @@ struct ScalarEnumerationTraits<FormatStyle::BreakConstructorInitializersStyle> { template <> struct ScalarEnumerationTraits<FormatStyle::BreakInheritanceListStyle> { - static void - enumeration(IO &IO, FormatStyle::BreakInheritanceListStyle &Value) { + static void enumeration(IO &IO, + FormatStyle::BreakInheritanceListStyle &Value) { IO.enumCase(Value, "BeforeColon", FormatStyle::BILS_BeforeColon); IO.enumCase(Value, "BeforeComma", FormatStyle::BILS_BeforeComma); IO.enumCase(Value, "AfterColon", FormatStyle::BILS_AfterColon); @@ -163,6 +186,7 @@ struct ScalarEnumerationTraits<FormatStyle::PPDirectiveIndentStyle> { static void enumeration(IO &IO, FormatStyle::PPDirectiveIndentStyle &Value) { IO.enumCase(Value, "None", FormatStyle::PPDIS_None); IO.enumCase(Value, "AfterHash", FormatStyle::PPDIS_AfterHash); + IO.enumCase(Value, "BeforeHash", FormatStyle::PPDIS_BeforeHash); } }; @@ -180,7 +204,8 @@ struct ScalarEnumerationTraits<FormatStyle::ReturnTypeBreakingStyle> { template <> struct ScalarEnumerationTraits<FormatStyle::BreakTemplateDeclarationsStyle> { - static void enumeration(IO &IO, FormatStyle::BreakTemplateDeclarationsStyle &Value) { + static void enumeration(IO &IO, + FormatStyle::BreakTemplateDeclarationsStyle &Value) { IO.enumCase(Value, "No", FormatStyle::BTDS_No); IO.enumCase(Value, "MultiLine", FormatStyle::BTDS_MultiLine); IO.enumCase(Value, "Yes", FormatStyle::BTDS_Yes); @@ -260,6 +285,8 @@ struct ScalarEnumerationTraits<FormatStyle::SpaceBeforeParensOptions> { IO.enumCase(Value, "Never", FormatStyle::SBPO_Never); IO.enumCase(Value, "ControlStatements", FormatStyle::SBPO_ControlStatements); + IO.enumCase(Value, "NonEmptyParentheses", + FormatStyle::SBPO_NonEmptyParentheses); IO.enumCase(Value, "Always", FormatStyle::SBPO_Always); // For backward compatibility. @@ -274,8 +301,8 @@ template <> struct MappingTraits<FormatStyle> { IO.mapOptional("Language", Style.Language); if (IO.outputting()) { - StringRef StylesArray[] = {"LLVM", "Google", "Chromium", - "Mozilla", "WebKit", "GNU"}; + StringRef StylesArray[] = {"LLVM", "Google", "Chromium", "Mozilla", + "WebKit", "GNU", "Microsoft"}; ArrayRef<StringRef> Styles(StylesArray); for (size_t i = 0, e = Styles.size(); i < e; ++i) { StringRef StyleName(Styles[i]); @@ -314,6 +341,7 @@ template <> struct MappingTraits<FormatStyle> { IO.mapOptional("AccessModifierOffset", Style.AccessModifierOffset); IO.mapOptional("AlignAfterOpenBracket", Style.AlignAfterOpenBracket); + IO.mapOptional("AlignConsecutiveMacros", Style.AlignConsecutiveMacros); IO.mapOptional("AlignConsecutiveAssignments", Style.AlignConsecutiveAssignments); IO.mapOptional("AlignConsecutiveDeclarations", @@ -321,6 +349,10 @@ template <> struct MappingTraits<FormatStyle> { IO.mapOptional("AlignEscapedNewlines", Style.AlignEscapedNewlines); IO.mapOptional("AlignOperands", Style.AlignOperands); IO.mapOptional("AlignTrailingComments", Style.AlignTrailingComments); + IO.mapOptional("AllowAllArgumentsOnNextLine", + Style.AllowAllArgumentsOnNextLine); + IO.mapOptional("AllowAllConstructorInitializersOnNextLine", + Style.AllowAllConstructorInitializersOnNextLine); IO.mapOptional("AllowAllParametersOfDeclarationOnNextLine", Style.AllowAllParametersOfDeclarationOnNextLine); IO.mapOptional("AllowShortBlocksOnASingleLine", @@ -329,6 +361,8 @@ template <> struct MappingTraits<FormatStyle> { Style.AllowShortCaseLabelsOnASingleLine); IO.mapOptional("AllowShortFunctionsOnASingleLine", Style.AllowShortFunctionsOnASingleLine); + IO.mapOptional("AllowShortLambdasOnASingleLine", + Style.AllowShortLambdasOnASingleLine); IO.mapOptional("AllowShortIfStatementsOnASingleLine", Style.AllowShortIfStatementsOnASingleLine); IO.mapOptional("AllowShortLoopsOnASingleLine", @@ -337,6 +371,7 @@ template <> struct MappingTraits<FormatStyle> { Style.AlwaysBreakAfterDefinitionReturnType); IO.mapOptional("AlwaysBreakAfterReturnType", Style.AlwaysBreakAfterReturnType); + // If AlwaysBreakAfterDefinitionReturnType was specified but // AlwaysBreakAfterReturnType was not, initialize the latter from the // former for backwards compatibility. @@ -362,10 +397,8 @@ template <> struct MappingTraits<FormatStyle> { IO.mapOptional("BreakBeforeBraces", Style.BreakBeforeBraces); bool BreakBeforeInheritanceComma = false; - IO.mapOptional("BreakBeforeInheritanceComma", - BreakBeforeInheritanceComma); - IO.mapOptional("BreakInheritanceList", - Style.BreakInheritanceList); + IO.mapOptional("BreakBeforeInheritanceComma", BreakBeforeInheritanceComma); + IO.mapOptional("BreakInheritanceList", Style.BreakInheritanceList); // If BreakBeforeInheritanceComma was specified but // BreakInheritance was not, initialize the latter from the // former for backwards compatibility. @@ -423,6 +456,7 @@ template <> struct MappingTraits<FormatStyle> { IO.mapOptional("MacroBlockEnd", Style.MacroBlockEnd); IO.mapOptional("MaxEmptyLinesToKeep", Style.MaxEmptyLinesToKeep); IO.mapOptional("NamespaceIndentation", Style.NamespaceIndentation); + IO.mapOptional("NamespaceMacros", Style.NamespaceMacros); IO.mapOptional("ObjCBinPackProtocolList", Style.ObjCBinPackProtocolList); IO.mapOptional("ObjCBlockIndentWidth", Style.ObjCBlockIndentWidth); IO.mapOptional("ObjCSpaceAfterProperty", Style.ObjCSpaceAfterProperty); @@ -446,6 +480,7 @@ template <> struct MappingTraits<FormatStyle> { IO.mapOptional("SortIncludes", Style.SortIncludes); IO.mapOptional("SortUsingDeclarations", Style.SortUsingDeclarations); IO.mapOptional("SpaceAfterCStyleCast", Style.SpaceAfterCStyleCast); + IO.mapOptional("SpaceAfterLogicalNot", Style.SpaceAfterLogicalNot); IO.mapOptional("SpaceAfterTemplateKeyword", Style.SpaceAfterTemplateKeyword); IO.mapOptional("SpaceBeforeAssignmentOperators", @@ -472,12 +507,14 @@ template <> struct MappingTraits<FormatStyle> { IO.mapOptional("Standard", Style.Standard); IO.mapOptional("StatementMacros", Style.StatementMacros); IO.mapOptional("TabWidth", Style.TabWidth); + IO.mapOptional("TypenameMacros", Style.TypenameMacros); IO.mapOptional("UseTab", Style.UseTab); } }; template <> struct MappingTraits<FormatStyle::BraceWrappingFlags> { static void mapping(IO &IO, FormatStyle::BraceWrappingFlags &Wrapping) { + IO.mapOptional("AfterCaseLabel", Wrapping.AfterCaseLabel); IO.mapOptional("AfterClass", Wrapping.AfterClass); IO.mapOptional("AfterControlStatement", Wrapping.AfterControlStatement); IO.mapOptional("AfterEnum", Wrapping.AfterEnum); @@ -570,7 +607,7 @@ static FormatStyle expandPresets(const FormatStyle &Style) { if (Style.BreakBeforeBraces == FormatStyle::BS_Custom) return Style; FormatStyle Expanded = Style; - Expanded.BraceWrapping = {false, false, false, false, false, + Expanded.BraceWrapping = {false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, true}; switch (Style.BreakBeforeBraces) { @@ -595,6 +632,7 @@ static FormatStyle expandPresets(const FormatStyle &Style) { Expanded.BraceWrapping.BeforeElse = true; break; case FormatStyle::BS_Allman: + Expanded.BraceWrapping.AfterCaseLabel = true; Expanded.BraceWrapping.AfterClass = true; Expanded.BraceWrapping.AfterControlStatement = true; Expanded.BraceWrapping.AfterEnum = true; @@ -608,7 +646,7 @@ static FormatStyle expandPresets(const FormatStyle &Style) { break; case FormatStyle::BS_GNU: Expanded.BraceWrapping = {true, true, true, true, true, true, true, true, - true, true, true, true, true, true, true}; + true, true, true, true, true, true, true, true}; break; case FormatStyle::BS_WebKit: Expanded.BraceWrapping.AfterFunction = true; @@ -619,9 +657,9 @@ static FormatStyle expandPresets(const FormatStyle &Style) { return Expanded; } -FormatStyle getLLVMStyle() { +FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { FormatStyle LLVMStyle; - LLVMStyle.Language = FormatStyle::LK_Cpp; + LLVMStyle.Language = Language; LLVMStyle.AccessModifierOffset = -2; LLVMStyle.AlignEscapedNewlines = FormatStyle::ENAS_Right; LLVMStyle.AlignAfterOpenBracket = FormatStyle::BAS_Align; @@ -629,11 +667,15 @@ FormatStyle getLLVMStyle() { LLVMStyle.AlignTrailingComments = true; LLVMStyle.AlignConsecutiveAssignments = false; LLVMStyle.AlignConsecutiveDeclarations = false; + LLVMStyle.AlignConsecutiveMacros = false; + LLVMStyle.AllowAllArgumentsOnNextLine = true; + LLVMStyle.AllowAllConstructorInitializersOnNextLine = true; LLVMStyle.AllowAllParametersOfDeclarationOnNextLine = true; LLVMStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_All; LLVMStyle.AllowShortBlocksOnASingleLine = false; LLVMStyle.AllowShortCaseLabelsOnASingleLine = false; - LLVMStyle.AllowShortIfStatementsOnASingleLine = false; + LLVMStyle.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never; + LLVMStyle.AllowShortLambdasOnASingleLine = FormatStyle::SLS_All; LLVMStyle.AllowShortLoopsOnASingleLine = false; LLVMStyle.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None; LLVMStyle.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_None; @@ -644,7 +686,7 @@ FormatStyle getLLVMStyle() { LLVMStyle.BreakBeforeBinaryOperators = FormatStyle::BOS_None; LLVMStyle.BreakBeforeTernaryOperators = true; LLVMStyle.BreakBeforeBraces = FormatStyle::BS_Attach; - LLVMStyle.BraceWrapping = {false, false, false, false, false, + LLVMStyle.BraceWrapping = {false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, true}; LLVMStyle.BreakAfterJavaFieldAnnotations = false; @@ -695,6 +737,7 @@ FormatStyle getLLVMStyle() { LLVMStyle.SpacesInContainerLiterals = true; LLVMStyle.SpacesInCStyleCastParentheses = false; LLVMStyle.SpaceAfterCStyleCast = false; + LLVMStyle.SpaceAfterLogicalNot = false; LLVMStyle.SpaceAfterTemplateKeyword = true; LLVMStyle.SpaceBeforeCtorInitializerColon = true; LLVMStyle.SpaceBeforeInheritanceColon = true; @@ -719,6 +762,11 @@ FormatStyle getLLVMStyle() { LLVMStyle.StatementMacros.push_back("Q_UNUSED"); LLVMStyle.StatementMacros.push_back("QT_REQUIRE_VERSION"); + // Defaults that differ when not C++. + if (Language == FormatStyle::LK_TableGen) { + LLVMStyle.SpacesInContainerLiterals = false; + } + return LLVMStyle; } @@ -730,12 +778,12 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) { return GoogleStyle; } - FormatStyle GoogleStyle = getLLVMStyle(); - GoogleStyle.Language = Language; + FormatStyle GoogleStyle = getLLVMStyle(Language); GoogleStyle.AccessModifierOffset = -1; GoogleStyle.AlignEscapedNewlines = FormatStyle::ENAS_Left; - GoogleStyle.AllowShortIfStatementsOnASingleLine = true; + GoogleStyle.AllowShortIfStatementsOnASingleLine = + FormatStyle::SIS_WithoutElse; GoogleStyle.AllowShortLoopsOnASingleLine = true; GoogleStyle.AlwaysBreakBeforeMultilineStrings = true; GoogleStyle.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_Yes; @@ -744,6 +792,7 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) { GoogleStyle.IncludeStyle.IncludeCategories = { {"^<ext/.*\\.h>", 2}, {"^<.*\\.h>", 1}, {"^<.*", 2}, {".*", 3}}; GoogleStyle.IncludeStyle.IncludeIsMainRegex = "([-_](test|unittest))?$"; + GoogleStyle.IncludeStyle.IncludeBlocks = tooling::IncludeStyle::IBS_Regroup; GoogleStyle.IndentCaseLabels = true; GoogleStyle.KeepEmptyLinesAtTheStartOfBlocks = false; GoogleStyle.ObjCBinPackProtocolList = FormatStyle::BPS_Never; @@ -802,7 +851,7 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) { GoogleStyle.AlignOperands = false; GoogleStyle.AlignTrailingComments = false; GoogleStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Empty; - GoogleStyle.AllowShortIfStatementsOnASingleLine = false; + GoogleStyle.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never; GoogleStyle.AlwaysBreakBeforeMultilineStrings = false; GoogleStyle.BreakBeforeBinaryOperators = FormatStyle::BOS_NonAssignment; GoogleStyle.ColumnLimit = 100; @@ -836,6 +885,11 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) { } else if (Language == FormatStyle::LK_ObjC) { GoogleStyle.AlwaysBreakBeforeMultilineStrings = false; GoogleStyle.ColumnLimit = 100; + // "Regroup" doesn't work well for ObjC yet (main header heuristic, + // relationship between ObjC standard library headers and other heades, + // #imports, etc.) + GoogleStyle.IncludeStyle.IncludeBlocks = + tooling::IncludeStyle::IBS_Preserve; } return GoogleStyle; @@ -844,7 +898,8 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) { FormatStyle getChromiumStyle(FormatStyle::LanguageKind Language) { FormatStyle ChromiumStyle = getGoogleStyle(Language); if (Language == FormatStyle::LK_Java) { - ChromiumStyle.AllowShortIfStatementsOnASingleLine = true; + ChromiumStyle.AllowShortIfStatementsOnASingleLine = + FormatStyle::SIS_WithoutElse; ChromiumStyle.BreakAfterJavaFieldAnnotations = true; ChromiumStyle.ContinuationIndentWidth = 8; ChromiumStyle.IndentWidth = 4; @@ -852,6 +907,7 @@ FormatStyle getChromiumStyle(FormatStyle::LanguageKind Language) { // https://chromium.googlesource.com/chromium/src/+/master/styleguide/java/java.md#Import-Order ChromiumStyle.JavaImportGroups = { "android", + "androidx", "com", "dalvik", "junit", @@ -863,12 +919,12 @@ FormatStyle getChromiumStyle(FormatStyle::LanguageKind Language) { }; ChromiumStyle.SortIncludes = true; } else if (Language == FormatStyle::LK_JavaScript) { - ChromiumStyle.AllowShortIfStatementsOnASingleLine = false; + ChromiumStyle.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never; ChromiumStyle.AllowShortLoopsOnASingleLine = false; } else { ChromiumStyle.AllowAllParametersOfDeclarationOnNextLine = false; ChromiumStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Inline; - ChromiumStyle.AllowShortIfStatementsOnASingleLine = false; + ChromiumStyle.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never; ChromiumStyle.AllowShortLoopsOnASingleLine = false; ChromiumStyle.BinPackParameters = false; ChromiumStyle.DerivePointerAlignment = false; @@ -940,6 +996,32 @@ FormatStyle getGNUStyle() { return Style; } +FormatStyle getMicrosoftStyle(FormatStyle::LanguageKind Language) { + FormatStyle Style = getLLVMStyle(); + Style.ColumnLimit = 120; + Style.TabWidth = 4; + Style.IndentWidth = 4; + Style.UseTab = FormatStyle::UT_Never; + Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.AfterClass = true; + Style.BraceWrapping.AfterControlStatement = true; + Style.BraceWrapping.AfterEnum = true; + Style.BraceWrapping.AfterFunction = true; + Style.BraceWrapping.AfterNamespace = true; + Style.BraceWrapping.AfterObjCDeclaration = true; + Style.BraceWrapping.AfterStruct = true; + Style.BraceWrapping.AfterExternBlock = true; + Style.BraceWrapping.BeforeCatch = true; + Style.BraceWrapping.BeforeElse = true; + Style.PenaltyReturnTypeOnItsOwnLine = 1000; + Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; + Style.AllowShortBlocksOnASingleLine = false; + Style.AllowShortCaseLabelsOnASingleLine = false; + Style.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never; + Style.AllowShortLoopsOnASingleLine = false; + return Style; +} + FormatStyle getNoStyle() { FormatStyle NoStyle = getLLVMStyle(); NoStyle.DisableFormat = true; @@ -951,7 +1033,7 @@ FormatStyle getNoStyle() { bool getPredefinedStyle(StringRef Name, FormatStyle::LanguageKind Language, FormatStyle *Style) { if (Name.equals_lower("llvm")) { - *Style = getLLVMStyle(); + *Style = getLLVMStyle(Language); } else if (Name.equals_lower("chromium")) { *Style = getChromiumStyle(Language); } else if (Name.equals_lower("mozilla")) { @@ -962,6 +1044,8 @@ bool getPredefinedStyle(StringRef Name, FormatStyle::LanguageKind Language, *Style = getWebKitStyle(); } else if (Name.equals_lower("gnu")) { *Style = getGNUStyle(); + } else if (Name.equals_lower("microsoft")) { + *Style = getMicrosoftStyle(Language); } else if (Name.equals_lower("none")) { *Style = getNoStyle(); } else { @@ -1060,9 +1144,7 @@ void FormatStyle::FormatStyleSet::Add(FormatStyle Style) { (*Styles)[Style.Language] = std::move(Style); } -void FormatStyle::FormatStyleSet::Clear() { - Styles.reset(); -} +void FormatStyle::FormatStyleSet::Clear() { Styles.reset(); } llvm::Optional<FormatStyle> FormatStyle::GetLanguageStyle(FormatStyle::LanguageKind Language) const { @@ -1691,6 +1773,7 @@ FindCursorIndex(const SmallVectorImpl<IncludeDirective> &Includes, static void sortCppIncludes(const FormatStyle &Style, const SmallVectorImpl<IncludeDirective> &Includes, ArrayRef<tooling::Range> Ranges, StringRef FileName, + StringRef Code, tooling::Replacements &Replaces, unsigned *Cursor) { unsigned IncludesBeginOffset = Includes.front().Offset; unsigned IncludesEndOffset = @@ -1701,11 +1784,10 @@ static void sortCppIncludes(const FormatStyle &Style, SmallVector<unsigned, 16> Indices; for (unsigned i = 0, e = Includes.size(); i != e; ++i) Indices.push_back(i); - std::stable_sort( - Indices.begin(), Indices.end(), [&](unsigned LHSI, unsigned RHSI) { - return std::tie(Includes[LHSI].Category, Includes[LHSI].Filename) < - std::tie(Includes[RHSI].Category, Includes[RHSI].Filename); - }); + llvm::stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) { + return std::tie(Includes[LHSI].Category, Includes[LHSI].Filename) < + std::tie(Includes[RHSI].Category, Includes[RHSI].Filename); + }); // The index of the include on which the cursor will be put after // sorting/deduplicating. unsigned CursorIndex; @@ -1726,6 +1808,10 @@ static void sortCppIncludes(const FormatStyle &Style, // If the #includes are out of order, we generate a single replacement fixing // the entire block. Otherwise, no replacement is generated. + // In case Style.IncldueStyle.IncludeBlocks != IBS_Preserve, this check is not + // enough as additional newlines might be added or removed across #include + // blocks. This we handle below by generating the updated #imclude blocks and + // comparing it to the original. if (Indices.size() == Includes.size() && std::is_sorted(Indices.begin(), Indices.end()) && Style.IncludeStyle.IncludeBlocks == tooling::IncludeStyle::IBS_Preserve) @@ -1746,6 +1832,11 @@ static void sortCppIncludes(const FormatStyle &Style, CurrentCategory = Includes[Index].Category; } + // If the #includes are out of order, we generate a single replacement fixing + // the entire range of blocks. Otherwise, no replacement is generated. + if (result == Code.substr(IncludesBeginOffset, IncludesBlockSize)) + return; + auto Err = Replaces.add(tooling::Replacement( FileName, Includes.front().Offset, IncludesBlockSize, result)); // FIXME: better error handling. For now, just skip the replacement for the @@ -1792,9 +1883,10 @@ tooling::Replacements sortCppIncludes(const FormatStyle &Style, StringRef Code, Code.substr(Prev, (Pos != StringRef::npos ? Pos : Code.size()) - Prev); StringRef Trimmed = Line.trim(); - if (Trimmed == "// clang-format off") + if (Trimmed == "// clang-format off" || Trimmed == "/* clang-format off */") FormattingOff = true; - else if (Trimmed == "// clang-format on") + else if (Trimmed == "// clang-format on" || + Trimmed == "/* clang-format on */") FormattingOff = false; const bool EmptyLineSkipped = @@ -1813,8 +1905,8 @@ tooling::Replacements sortCppIncludes(const FormatStyle &Style, StringRef Code, MainIncludeFound = true; IncludesInBlock.push_back({IncludeName, Line, Prev, Category}); } else if (!IncludesInBlock.empty() && !EmptyLineSkipped) { - sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Replaces, - Cursor); + sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Code, + Replaces, Cursor); IncludesInBlock.clear(); FirstIncludeBlock = false; } @@ -1824,8 +1916,10 @@ tooling::Replacements sortCppIncludes(const FormatStyle &Style, StringRef Code, break; SearchFrom = Pos + 1; } - if (!IncludesInBlock.empty()) - sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Replaces, Cursor); + if (!IncludesInBlock.empty()) { + sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Code, Replaces, + Cursor); + } return Replaces; } @@ -1854,7 +1948,7 @@ static unsigned findJavaImportGroup(const FormatStyle &Style, static void sortJavaImports(const FormatStyle &Style, const SmallVectorImpl<JavaImportDirective> &Imports, ArrayRef<tooling::Range> Ranges, StringRef FileName, - tooling::Replacements &Replaces) { + StringRef Code, tooling::Replacements &Replaces) { unsigned ImportsBeginOffset = Imports.front().Offset; unsigned ImportsEndOffset = Imports.back().Offset + Imports.back().Text.size(); @@ -1868,13 +1962,13 @@ static void sortJavaImports(const FormatStyle &Style, JavaImportGroups.push_back( findJavaImportGroup(Style, Imports[i].Identifier)); } - llvm::sort(Indices.begin(), Indices.end(), [&](unsigned LHSI, unsigned RHSI) { - // Negating IsStatic to push static imports above non-static imports. - return std::make_tuple(!Imports[LHSI].IsStatic, JavaImportGroups[LHSI], - Imports[LHSI].Identifier) < - std::make_tuple(!Imports[RHSI].IsStatic, JavaImportGroups[RHSI], - Imports[RHSI].Identifier); - }); + llvm::sort(Indices, [&](unsigned LHSI, unsigned RHSI) { + // Negating IsStatic to push static imports above non-static imports. + return std::make_tuple(!Imports[LHSI].IsStatic, JavaImportGroups[LHSI], + Imports[LHSI].Identifier) < + std::make_tuple(!Imports[RHSI].IsStatic, JavaImportGroups[RHSI], + Imports[RHSI].Identifier); + }); // Deduplicate imports. Indices.erase(std::unique(Indices.begin(), Indices.end(), @@ -1903,6 +1997,11 @@ static void sortJavaImports(const FormatStyle &Style, CurrentImportGroup = JavaImportGroups[Index]; } + // If the imports are out of order, we generate a single replacement fixing + // the entire block. Otherwise, no replacement is generated. + if (result == Code.substr(Imports.front().Offset, ImportsBlockSize)) + return; + auto Err = Replaces.add(tooling::Replacement(FileName, Imports.front().Offset, ImportsBlockSize, result)); // FIXME: better error handling. For now, just skip the replacement for the @@ -1916,7 +2015,7 @@ static void sortJavaImports(const FormatStyle &Style, namespace { const char JavaImportRegexPattern[] = - "^[\t ]*import[\t ]*(static[\t ]*)?([^\t ]*)[\t ]*;"; + "^[\t ]*import[\t ]+(static[\t ]*)?([^\t ]*)[\t ]*;"; } // anonymous namespace @@ -1956,7 +2055,8 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code, if (Static.contains("static")) { IsStatic = true; } - ImportsInBlock.push_back({Identifier, Line, Prev, AssociatedCommentLines, IsStatic}); + ImportsInBlock.push_back( + {Identifier, Line, Prev, AssociatedCommentLines, IsStatic}); AssociatedCommentLines.clear(); } else if (Trimmed.size() > 0 && !ImportsInBlock.empty()) { // Associating comments within the imports with the nearest import below @@ -1968,7 +2068,7 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code, SearchFrom = Pos + 1; } if (!ImportsInBlock.empty()) - sortJavaImports(Style, ImportsInBlock, Ranges, FileName, Replaces); + sortJavaImports(Style, ImportsInBlock, Ranges, FileName, Code, Replaces); return Replaces; } @@ -2085,7 +2185,6 @@ fixCppIncludeInsertions(StringRef Code, const tooling::Replacements &Replaces, if (HeaderInsertions.empty() && HeadersToDelete.empty()) return Replaces; - StringRef FileName = Replaces.begin()->getFilePath(); tooling::HeaderIncludes Includes(FileName, Code, Style.IncludeStyle); @@ -2118,7 +2217,8 @@ fixCppIncludeInsertions(StringRef Code, const tooling::Replacements &Replaces, auto Err = Result.add(*Replace); if (Err) { llvm::consumeError(std::move(Err)); - unsigned NewOffset = Result.getShiftedCodePosition(Replace->getOffset()); + unsigned NewOffset = + Result.getShiftedCodePosition(Replace->getOffset()); auto Shifted = tooling::Replacement(FileName, NewOffset, 0, Replace->getReplacementText()); Result = Result.merge(tooling::Replacements(Shifted)); @@ -2307,6 +2407,8 @@ static FormatStyle::LanguageKind getLanguageByFileName(StringRef FileName) { return FormatStyle::LK_TextProto; if (FileName.endswith_lower(".td")) return FormatStyle::LK_TableGen; + if (FileName.endswith_lower(".cs")) + return FormatStyle::LK_CSharp; return FormatStyle::LK_Cpp; } @@ -2339,8 +2441,7 @@ llvm::Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName, if (!FS) { FS = llvm::vfs::getRealFileSystem().get(); } - FormatStyle Style = getLLVMStyle(); - Style.Language = guessLanguage(FileName, Code); + FormatStyle Style = getLLVMStyle(guessLanguage(FileName, Code)); FormatStyle FallbackStyle = getNoStyle(); if (!getPredefinedStyle(FallbackStyleName, Style.Language, &FallbackStyle)) diff --git a/lib/Format/FormatInternal.h b/lib/Format/FormatInternal.h index 5c59e7656eee..3aa616da23d8 100644 --- a/lib/Format/FormatInternal.h +++ b/lib/Format/FormatInternal.h @@ -1,9 +1,8 @@ //===--- FormatInternal.h - Format C++ code ---------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// diff --git a/lib/Format/FormatToken.cpp b/lib/Format/FormatToken.cpp index 62b08c576e05..90d09064bb43 100644 --- a/lib/Format/FormatToken.cpp +++ b/lib/Format/FormatToken.cpp @@ -1,9 +1,8 @@ //===--- FormatToken.cpp - Format C++ code --------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// diff --git a/lib/Format/FormatToken.h b/lib/Format/FormatToken.h index 10390c42911b..df7493742025 100644 --- a/lib/Format/FormatToken.h +++ b/lib/Format/FormatToken.h @@ -1,9 +1,8 @@ //===--- FormatToken.h - Format C++ code ------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -61,15 +60,18 @@ namespace format { TYPE(JsExponentiationEqual) \ TYPE(JsFatArrow) \ TYPE(JsNonNullAssertion) \ + TYPE(JsPrivateIdentifier) \ TYPE(JsTypeColon) \ TYPE(JsTypeOperator) \ TYPE(JsTypeOptionalQuestion) \ TYPE(LambdaArrow) \ + TYPE(LambdaLBrace) \ TYPE(LambdaLSquare) \ TYPE(LeadingJavaAnnotation) \ TYPE(LineComment) \ TYPE(MacroBlockBegin) \ TYPE(MacroBlockEnd) \ + TYPE(NamespaceMacro) \ TYPE(ObjCBlockLBrace) \ TYPE(ObjCBlockLParen) \ TYPE(ObjCDecl) \ @@ -95,7 +97,10 @@ namespace format { TYPE(TrailingAnnotation) \ TYPE(TrailingReturnArrow) \ TYPE(TrailingUnaryOperator) \ + TYPE(TypenameMacro) \ TYPE(UnaryOperator) \ + TYPE(CSharpStringLiteral) \ + TYPE(CSharpNullCoalescing) \ TYPE(Unknown) enum TokenType { @@ -490,8 +495,7 @@ struct FormatToken { bool opensBlockOrBlockTypeList(const FormatStyle &Style) const { if (is(TT_TemplateString) && opensScope()) return true; - return is(TT_ArrayInitializerLSquare) || - is(TT_ProtoExtensionLSquare) || + return is(TT_ArrayInitializerLSquare) || is(TT_ProtoExtensionLSquare) || (is(tok::l_brace) && (BlockKind == BK_Block || is(TT_DictLiteral) || (!Style.Cpp11BracedListStyle && NestingLevel == 0))) || @@ -528,8 +532,10 @@ struct FormatToken { // Detect "(inline|export)? namespace" in the beginning of a line. if (NamespaceTok && NamespaceTok->isOneOf(tok::kw_inline, tok::kw_export)) NamespaceTok = NamespaceTok->getNextNonComment(); - return NamespaceTok && NamespaceTok->is(tok::kw_namespace) ? NamespaceTok - : nullptr; + return NamespaceTok && + NamespaceTok->isOneOf(tok::kw_namespace, TT_NamespaceMacro) + ? NamespaceTok + : nullptr; } private: @@ -724,7 +730,36 @@ struct AdditionalKeywords { kw_slots = &IdentTable.get("slots"); kw_qslots = &IdentTable.get("Q_SLOTS"); - // Keep this at the end of the constructor to make sure everything here is + // C# keywords + kw_dollar = &IdentTable.get("dollar"); + kw_base = &IdentTable.get("base"); + kw_byte = &IdentTable.get("byte"); + kw_checked = &IdentTable.get("checked"); + kw_decimal = &IdentTable.get("decimal"); + kw_delegate = &IdentTable.get("delegate"); + kw_event = &IdentTable.get("event"); + kw_fixed = &IdentTable.get("fixed"); + kw_foreach = &IdentTable.get("foreach"); + kw_implicit = &IdentTable.get("implicit"); + kw_internal = &IdentTable.get("internal"); + kw_lock = &IdentTable.get("lock"); + kw_null = &IdentTable.get("null"); + kw_object = &IdentTable.get("object"); + kw_out = &IdentTable.get("out"); + kw_params = &IdentTable.get("params"); + kw_ref = &IdentTable.get("ref"); + kw_string = &IdentTable.get("string"); + kw_stackalloc = &IdentTable.get("stackalloc"); + kw_sbyte = &IdentTable.get("sbyte"); + kw_sealed = &IdentTable.get("sealed"); + kw_uint = &IdentTable.get("uint"); + kw_ulong = &IdentTable.get("ulong"); + kw_unchecked = &IdentTable.get("unchecked"); + kw_unsafe = &IdentTable.get("unsafe"); + kw_ushort = &IdentTable.get("ushort"); + + // Keep this at the end of the constructor to make sure everything here + // is // already initialized. JsExtraKeywords = std::unordered_set<IdentifierInfo *>( {kw_as, kw_async, kw_await, kw_declare, kw_finally, kw_from, @@ -732,6 +767,19 @@ struct AdditionalKeywords { kw_set, kw_type, kw_typeof, kw_var, kw_yield, // Keywords from the Java section. kw_abstract, kw_extends, kw_implements, kw_instanceof, kw_interface}); + + CSharpExtraKeywords = std::unordered_set<IdentifierInfo *>( + {kw_base, kw_byte, kw_checked, kw_decimal, kw_delegate, kw_event, + kw_fixed, kw_foreach, kw_implicit, kw_in, kw_interface, kw_internal, + kw_is, kw_lock, kw_null, kw_object, kw_out, kw_override, kw_params, + kw_readonly, kw_ref, kw_string, kw_stackalloc, kw_sbyte, kw_sealed, + kw_uint, kw_ulong, kw_unchecked, kw_unsafe, kw_ushort, + // Keywords from the JavaScript section. + kw_as, kw_async, kw_await, kw_declare, kw_finally, kw_from, + kw_function, kw_get, kw_import, kw_is, kw_let, kw_module, kw_readonly, + kw_set, kw_type, kw_typeof, kw_var, kw_yield, + // Keywords from the Java section. + kw_abstract, kw_extends, kw_implements, kw_instanceof, kw_interface}); } // Context sensitive keywords. @@ -797,6 +845,37 @@ struct AdditionalKeywords { IdentifierInfo *kw_slots; IdentifierInfo *kw_qslots; + // C# keywords + IdentifierInfo *kw_dollar; + IdentifierInfo *kw_base; + IdentifierInfo *kw_byte; + IdentifierInfo *kw_checked; + IdentifierInfo *kw_decimal; + IdentifierInfo *kw_delegate; + IdentifierInfo *kw_event; + IdentifierInfo *kw_fixed; + IdentifierInfo *kw_foreach; + IdentifierInfo *kw_implicit; + IdentifierInfo *kw_internal; + + IdentifierInfo *kw_lock; + IdentifierInfo *kw_null; + IdentifierInfo *kw_object; + IdentifierInfo *kw_out; + + IdentifierInfo *kw_params; + + IdentifierInfo *kw_ref; + IdentifierInfo *kw_string; + IdentifierInfo *kw_stackalloc; + IdentifierInfo *kw_sbyte; + IdentifierInfo *kw_sealed; + IdentifierInfo *kw_uint; + IdentifierInfo *kw_ulong; + IdentifierInfo *kw_unchecked; + IdentifierInfo *kw_unsafe; + IdentifierInfo *kw_ushort; + /// Returns \c true if \p Tok is a true JavaScript identifier, returns /// \c false if it is a keyword or a pseudo keyword. bool IsJavaScriptIdentifier(const FormatToken &Tok) const { @@ -805,9 +884,68 @@ struct AdditionalKeywords { JsExtraKeywords.end(); } + /// Returns \c true if \p Tok is a C# keyword, returns + /// \c false if it is a anything else. + bool isCSharpKeyword(const FormatToken &Tok) const { + switch (Tok.Tok.getKind()) { + case tok::kw_bool: + case tok::kw_break: + case tok::kw_case: + case tok::kw_catch: + case tok::kw_char: + case tok::kw_class: + case tok::kw_const: + case tok::kw_continue: + case tok::kw_default: + case tok::kw_do: + case tok::kw_double: + case tok::kw_else: + case tok::kw_enum: + case tok::kw_explicit: + case tok::kw_extern: + case tok::kw_false: + case tok::kw_float: + case tok::kw_for: + case tok::kw_goto: + case tok::kw_if: + case tok::kw_int: + case tok::kw_long: + case tok::kw_namespace: + case tok::kw_new: + case tok::kw_operator: + case tok::kw_private: + case tok::kw_protected: + case tok::kw_public: + case tok::kw_return: + case tok::kw_short: + case tok::kw_sizeof: + case tok::kw_static: + case tok::kw_struct: + case tok::kw_switch: + case tok::kw_this: + case tok::kw_throw: + case tok::kw_true: + case tok::kw_try: + case tok::kw_typeof: + case tok::kw_using: + case tok::kw_virtual: + case tok::kw_void: + case tok::kw_volatile: + case tok::kw_while: + return true; + default: + return Tok.is(tok::identifier) && + CSharpExtraKeywords.find(Tok.Tok.getIdentifierInfo()) == + CSharpExtraKeywords.end(); + } + } + private: /// The JavaScript keywords beyond the C++ keyword set. std::unordered_set<IdentifierInfo *> JsExtraKeywords; + + /// The C# keywords beyond the C++ keyword set + std::unordered_set<IdentifierInfo *> CSharpExtraKeywords; }; } // namespace format diff --git a/lib/Format/FormatTokenLexer.cpp b/lib/Format/FormatTokenLexer.cpp index 146f5d68b559..009b8849753c 100644 --- a/lib/Format/FormatTokenLexer.cpp +++ b/lib/Format/FormatTokenLexer.cpp @@ -1,9 +1,8 @@ //===--- FormatTokenLexer.cpp - Lex FormatTokens -------------*- C++ ----*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -40,6 +39,10 @@ FormatTokenLexer::FormatTokenLexer(const SourceManager &SourceMgr, FileID ID, Macros.insert({&IdentTable.get(ForEachMacro), TT_ForEachMacro}); for (const std::string &StatementMacro : Style.StatementMacros) Macros.insert({&IdentTable.get(StatementMacro), TT_StatementMacro}); + for (const std::string &TypenameMacro : Style.TypenameMacros) + Macros.insert({&IdentTable.get(TypenameMacro), TT_TypenameMacro}); + for (const std::string &NamespaceMacro : Style.NamespaceMacros) + Macros.insert({&IdentTable.get(NamespaceMacro), TT_NamespaceMacro}); } ArrayRef<FormatToken *> FormatTokenLexer::lex() { @@ -67,6 +70,21 @@ void FormatTokenLexer::tryMergePreviousTokens() { return; if (tryMergeLessLess()) return; + + if (Style.isCSharp()) { + if (tryMergeCSharpKeywordVariables()) + return; + if (tryMergeCSharpVerbatimStringLiteral()) + return; + if (tryMergeCSharpDoubleQuestion()) + return; + if (tryMergeCSharpNullConditionals()) + return; + static const tok::TokenKind JSRightArrow[] = {tok::equal, tok::greater}; + if (tryMergeTokens(JSRightArrow, TT_JsFatArrow)) + return; + } + if (tryMergeNSStringLiteral()) return; @@ -96,6 +114,8 @@ void FormatTokenLexer::tryMergePreviousTokens() { Tokens.back()->Tok.setKind(tok::starequal); return; } + if (tryMergeJSPrivateIdentifier()) + return; } if (Style.Language == FormatStyle::LK_Java) { @@ -122,6 +142,118 @@ bool FormatTokenLexer::tryMergeNSStringLiteral() { return true; } +bool FormatTokenLexer::tryMergeJSPrivateIdentifier() { + // Merges #idenfier into a single identifier with the text #identifier + // but the token tok::identifier. + if (Tokens.size() < 2) + return false; + auto &Hash = *(Tokens.end() - 2); + auto &Identifier = *(Tokens.end() - 1); + if (!Hash->is(tok::hash) || !Identifier->is(tok::identifier)) + return false; + Hash->Tok.setKind(tok::identifier); + Hash->TokenText = + StringRef(Hash->TokenText.begin(), + Identifier->TokenText.end() - Hash->TokenText.begin()); + Hash->ColumnWidth += Identifier->ColumnWidth; + Hash->Type = TT_JsPrivateIdentifier; + Tokens.erase(Tokens.end() - 1); + return true; +} + +// Search for verbatim or interpolated string literals @"ABC" or +// $"aaaaa{abc}aaaaa" i and mark the token as TT_CSharpStringLiteral, and to +// prevent splitting of @, $ and ". +bool FormatTokenLexer::tryMergeCSharpVerbatimStringLiteral() { + if (Tokens.size() < 2) + return false; + auto &At = *(Tokens.end() - 2); + auto &String = *(Tokens.end() - 1); + + // Look for $"aaaaaa" @"aaaaaa". + if (!(At->is(tok::at) || At->TokenText == "$") || + !String->is(tok::string_literal)) + return false; + + if (Tokens.size() >= 2 && At->is(tok::at)) { + auto &Dollar = *(Tokens.end() - 3); + if (Dollar->TokenText == "$") { + // This looks like $@"aaaaa" so we need to combine all 3 tokens. + Dollar->Tok.setKind(tok::string_literal); + Dollar->TokenText = + StringRef(Dollar->TokenText.begin(), + String->TokenText.end() - Dollar->TokenText.begin()); + Dollar->ColumnWidth += (At->ColumnWidth + String->ColumnWidth); + Dollar->Type = TT_CSharpStringLiteral; + Tokens.erase(Tokens.end() - 2); + Tokens.erase(Tokens.end() - 1); + return true; + } + } + + // Convert back into just a string_literal. + At->Tok.setKind(tok::string_literal); + At->TokenText = StringRef(At->TokenText.begin(), + String->TokenText.end() - At->TokenText.begin()); + At->ColumnWidth += String->ColumnWidth; + At->Type = TT_CSharpStringLiteral; + Tokens.erase(Tokens.end() - 1); + return true; +} + +bool FormatTokenLexer::tryMergeCSharpDoubleQuestion() { + if (Tokens.size() < 2) + return false; + auto &FirstQuestion = *(Tokens.end() - 2); + auto &SecondQuestion = *(Tokens.end() - 1); + if (!FirstQuestion->is(tok::question) || !SecondQuestion->is(tok::question)) + return false; + FirstQuestion->Tok.setKind(tok::question); + FirstQuestion->TokenText = StringRef(FirstQuestion->TokenText.begin(), + SecondQuestion->TokenText.end() - + FirstQuestion->TokenText.begin()); + FirstQuestion->ColumnWidth += SecondQuestion->ColumnWidth; + FirstQuestion->Type = TT_CSharpNullCoalescing; + Tokens.erase(Tokens.end() - 1); + return true; +} + +bool FormatTokenLexer::tryMergeCSharpKeywordVariables() { + if (Tokens.size() < 2) + return false; + auto &At = *(Tokens.end() - 2); + auto &Keyword = *(Tokens.end() - 1); + if (!At->is(tok::at)) + return false; + if (!Keywords.isCSharpKeyword(*Keyword)) + return false; + + At->Tok.setKind(tok::identifier); + At->TokenText = StringRef(At->TokenText.begin(), + Keyword->TokenText.end() - At->TokenText.begin()); + At->ColumnWidth += Keyword->ColumnWidth; + At->Type = Keyword->Type; + Tokens.erase(Tokens.end() - 1); + return true; +} + +// In C# merge the Identifier and the ? together e.g. arg?. +bool FormatTokenLexer::tryMergeCSharpNullConditionals() { + if (Tokens.size() < 2) + return false; + auto &Identifier = *(Tokens.end() - 2); + auto &Question = *(Tokens.end() - 1); + if (!Identifier->isOneOf(tok::r_square, tok::identifier) || + !Question->is(tok::question)) + return false; + Identifier->TokenText = + StringRef(Identifier->TokenText.begin(), + Question->TokenText.end() - Identifier->TokenText.begin()); + Identifier->ColumnWidth += Question->ColumnWidth; + Tokens.erase(Tokens.end() - 1); + return true; +} + bool FormatTokenLexer::tryMergeLessLess() { // Merge X,less,less,Y into X,lessless,Y unless X or Y is less. if (Tokens.size() < 3) diff --git a/lib/Format/FormatTokenLexer.h b/lib/Format/FormatTokenLexer.h index 0cf357c85f3b..1e096fc50205 100644 --- a/lib/Format/FormatTokenLexer.h +++ b/lib/Format/FormatTokenLexer.h @@ -1,9 +1,8 @@ //===--- FormatTokenLexer.h - Format C++ code ----------------*- C++ ----*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -21,8 +20,8 @@ #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Format/Format.h" -#include "llvm/Support/Regex.h" #include "llvm/ADT/MapVector.h" +#include "llvm/Support/Regex.h" #include <stack> @@ -49,6 +48,11 @@ private: bool tryMergeLessLess(); bool tryMergeNSStringLiteral(); + bool tryMergeJSPrivateIdentifier(); + bool tryMergeCSharpVerbatimStringLiteral(); + bool tryMergeCSharpKeywordVariables(); + bool tryMergeCSharpNullConditionals(); + bool tryMergeCSharpDoubleQuestion(); bool tryMergeTokens(ArrayRef<tok::TokenKind> Kinds, TokenType NewType); diff --git a/lib/Format/NamespaceEndCommentsFixer.cpp b/lib/Format/NamespaceEndCommentsFixer.cpp index dd364866d1ce..d04fc8f115fb 100644 --- a/lib/Format/NamespaceEndCommentsFixer.cpp +++ b/lib/Format/NamespaceEndCommentsFixer.cpp @@ -1,9 +1,8 @@ //===--- NamespaceEndCommentsFixer.cpp --------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -30,24 +29,41 @@ static const int kShortNamespaceMaxLines = 1; // Computes the name of a namespace given the namespace token. // Returns "" for anonymous namespace. std::string computeName(const FormatToken *NamespaceTok) { - assert(NamespaceTok && NamespaceTok->is(tok::kw_namespace) && + assert(NamespaceTok && + NamespaceTok->isOneOf(tok::kw_namespace, TT_NamespaceMacro) && "expecting a namespace token"); std::string name = ""; - // Collects all the non-comment tokens between 'namespace' and '{'. const FormatToken *Tok = NamespaceTok->getNextNonComment(); - while (Tok && !Tok->is(tok::l_brace)) { - name += Tok->TokenText; + if (NamespaceTok->is(TT_NamespaceMacro)) { + // Collects all the non-comment tokens between opening parenthesis + // and closing parenthesis or comma + assert(Tok && Tok->is(tok::l_paren) && "expected an opening parenthesis"); Tok = Tok->getNextNonComment(); + while (Tok && !Tok->isOneOf(tok::r_paren, tok::comma)) { + name += Tok->TokenText; + Tok = Tok->getNextNonComment(); + } + } else { + // Collects all the non-comment tokens between 'namespace' and '{'. + while (Tok && !Tok->is(tok::l_brace)) { + name += Tok->TokenText; + Tok = Tok->getNextNonComment(); + } } return name; } -std::string computeEndCommentText(StringRef NamespaceName, bool AddNewline) { - std::string text = "// namespace"; - if (!NamespaceName.empty()) { +std::string computeEndCommentText(StringRef NamespaceName, bool AddNewline, + const FormatToken *NamespaceTok) { + std::string text = "// "; + text += NamespaceTok->TokenText; + if (NamespaceTok->is(TT_NamespaceMacro)) + text += "("; + else if (!NamespaceName.empty()) text += ' '; - text += NamespaceName; - } + text += NamespaceName; + if (NamespaceTok->is(TT_NamespaceMacro)) + text += ")"; if (AddNewline) text += '\n'; return text; @@ -57,7 +73,8 @@ bool hasEndComment(const FormatToken *RBraceTok) { return RBraceTok->Next && RBraceTok->Next->is(tok::comment); } -bool validEndComment(const FormatToken *RBraceTok, StringRef NamespaceName) { +bool validEndComment(const FormatToken *RBraceTok, StringRef NamespaceName, + const FormatToken *NamespaceTok) { assert(hasEndComment(RBraceTok)); const FormatToken *Comment = RBraceTok->Next; @@ -67,19 +84,32 @@ bool validEndComment(const FormatToken *RBraceTok, StringRef NamespaceName) { new llvm::Regex("^/[/*] *(end (of )?)? *(anonymous|unnamed)? *" "namespace( +([a-zA-Z0-9:_]+))?\\.? *(\\*/)?$", llvm::Regex::IgnoreCase); - SmallVector<StringRef, 7> Groups; - if (NamespaceCommentPattern->match(Comment->TokenText, &Groups)) { - StringRef NamespaceNameInComment = Groups.size() > 5 ? Groups[5] : ""; - // Anonymous namespace comments must not mention a namespace name. - if (NamespaceName.empty() && !NamespaceNameInComment.empty()) - return false; - StringRef AnonymousInComment = Groups.size() > 3 ? Groups[3] : ""; - // Named namespace comments must not mention anonymous namespace. - if (!NamespaceName.empty() && !AnonymousInComment.empty()) + static llvm::Regex *const NamespaceMacroCommentPattern = + new llvm::Regex("^/[/*] *(end (of )?)? *(anonymous|unnamed)? *" + "([a-zA-Z0-9_]+)\\(([a-zA-Z0-9:_]*)\\)\\.? *(\\*/)?$", + llvm::Regex::IgnoreCase); + + SmallVector<StringRef, 8> Groups; + if (NamespaceTok->is(TT_NamespaceMacro) && + NamespaceMacroCommentPattern->match(Comment->TokenText, &Groups)) { + StringRef NamespaceTokenText = Groups.size() > 4 ? Groups[4] : ""; + // The name of the macro must be used. + if (NamespaceTokenText != NamespaceTok->TokenText) return false; - return NamespaceNameInComment == NamespaceName; + } else if (NamespaceTok->isNot(tok::kw_namespace) || + !NamespaceCommentPattern->match(Comment->TokenText, &Groups)) { + // Comment does not match regex. + return false; } - return false; + StringRef NamespaceNameInComment = Groups.size() > 5 ? Groups[5] : ""; + // Anonymous namespace comments must not mention a namespace name. + if (NamespaceName.empty() && !NamespaceNameInComment.empty()) + return false; + StringRef AnonymousInComment = Groups.size() > 3 ? Groups[3] : ""; + // Named namespace comments must not mention anonymous namespace. + if (!NamespaceName.empty() && !AnonymousInComment.empty()) + return false; + return NamespaceNameInComment == NamespaceName; } void addEndComment(const FormatToken *RBraceTok, StringRef EndCommentText, @@ -128,6 +158,13 @@ getNamespaceToken(const AnnotatedLine *Line, return NamespaceTok->getNamespaceToken(); } +StringRef +getNamespaceTokenText(const AnnotatedLine *Line, + const SmallVectorImpl<AnnotatedLine *> &AnnotatedLines) { + const FormatToken *NamespaceTok = getNamespaceToken(Line, AnnotatedLines); + return NamespaceTok ? NamespaceTok->TokenText : StringRef(); +} + NamespaceEndCommentsFixer::NamespaceEndCommentsFixer(const Environment &Env, const FormatStyle &Style) : TokenAnalyzer(Env, Style) {} @@ -140,6 +177,7 @@ std::pair<tooling::Replacements, unsigned> NamespaceEndCommentsFixer::analyze( tooling::Replacements Fixes; std::string AllNamespaceNames = ""; size_t StartLineIndex = SIZE_MAX; + StringRef NamespaceTokenText; unsigned int CompactedNamespacesCount = 0; for (size_t I = 0, E = AnnotatedLines.size(); I != E; ++I) { const AnnotatedLine *EndLine = AnnotatedLines[I]; @@ -161,8 +199,11 @@ std::pair<tooling::Replacements, unsigned> NamespaceEndCommentsFixer::analyze( StartLineIndex = EndLine->MatchingOpeningBlockLineIndex; std::string NamespaceName = computeName(NamespaceTok); if (Style.CompactNamespaces) { + if (CompactedNamespacesCount == 0) + NamespaceTokenText = NamespaceTok->TokenText; if ((I + 1 < E) && - getNamespaceToken(AnnotatedLines[I + 1], AnnotatedLines) && + NamespaceTokenText == + getNamespaceTokenText(AnnotatedLines[I + 1], AnnotatedLines) && StartLineIndex - CompactedNamespacesCount - 1 == AnnotatedLines[I + 1]->MatchingOpeningBlockLineIndex && !AnnotatedLines[I + 1]->First->Finalized) { @@ -190,12 +231,13 @@ std::pair<tooling::Replacements, unsigned> NamespaceEndCommentsFixer::analyze( EndCommentNextTok->NewlinesBefore == 0 && EndCommentNextTok->isNot(tok::eof); const std::string EndCommentText = - computeEndCommentText(NamespaceName, AddNewline); + computeEndCommentText(NamespaceName, AddNewline, NamespaceTok); if (!hasEndComment(EndCommentPrevTok)) { bool isShort = I - StartLineIndex <= kShortNamespaceMaxLines + 1; if (!isShort) addEndComment(EndCommentPrevTok, EndCommentText, SourceMgr, &Fixes); - } else if (!validEndComment(EndCommentPrevTok, NamespaceName)) { + } else if (!validEndComment(EndCommentPrevTok, NamespaceName, + NamespaceTok)) { updateEndComment(EndCommentPrevTok, EndCommentText, SourceMgr, &Fixes); } StartLineIndex = SIZE_MAX; diff --git a/lib/Format/NamespaceEndCommentsFixer.h b/lib/Format/NamespaceEndCommentsFixer.h index 07a1c7bb0c35..eba24236f268 100644 --- a/lib/Format/NamespaceEndCommentsFixer.h +++ b/lib/Format/NamespaceEndCommentsFixer.h @@ -1,9 +1,8 @@ //===--- NamespaceEndCommentsFixer.h ----------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// diff --git a/lib/Format/SortJavaScriptImports.cpp b/lib/Format/SortJavaScriptImports.cpp index 2ec577382ffb..5be243f4c07a 100644 --- a/lib/Format/SortJavaScriptImports.cpp +++ b/lib/Format/SortJavaScriptImports.cpp @@ -1,9 +1,8 @@ //===--- SortJavaScriptImports.cpp - Sort ES6 Imports -----------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -142,10 +141,9 @@ public: SmallVector<unsigned, 16> Indices; for (unsigned i = 0, e = References.size(); i != e; ++i) Indices.push_back(i); - std::stable_sort(Indices.begin(), Indices.end(), - [&](unsigned LHSI, unsigned RHSI) { - return References[LHSI] < References[RHSI]; - }); + llvm::stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) { + return References[LHSI] < References[RHSI]; + }); bool ReferencesInOrder = std::is_sorted(Indices.begin(), Indices.end()); std::string ReferencesText; @@ -247,9 +245,8 @@ private: // Sort the individual symbols within the import. // E.g. `import {b, a} from 'x';` -> `import {a, b} from 'x';` SmallVector<JsImportedSymbol, 1> Symbols = Reference.Symbols; - std::stable_sort( - Symbols.begin(), Symbols.end(), - [&](const JsImportedSymbol &LHS, const JsImportedSymbol &RHS) { + llvm::stable_sort( + Symbols, [&](const JsImportedSymbol &LHS, const JsImportedSymbol &RHS) { return LHS.Symbol.compare_lower(RHS.Symbol) < 0; }); if (Symbols == Reference.Symbols) { diff --git a/lib/Format/SortJavaScriptImports.h b/lib/Format/SortJavaScriptImports.h index ecab0ae54cb3..7336db9537b0 100644 --- a/lib/Format/SortJavaScriptImports.h +++ b/lib/Format/SortJavaScriptImports.h @@ -1,9 +1,8 @@ //===--- SortJavaScriptImports.h - Sort ES6 Imports -------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// diff --git a/lib/Format/TokenAnalyzer.cpp b/lib/Format/TokenAnalyzer.cpp index 99fc61ef1c32..eb98a205d526 100644 --- a/lib/Format/TokenAnalyzer.cpp +++ b/lib/Format/TokenAnalyzer.cpp @@ -1,9 +1,8 @@ //===--- TokenAnalyzer.cpp - Analyze Token Streams --------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// diff --git a/lib/Format/TokenAnalyzer.h b/lib/Format/TokenAnalyzer.h index e43a860e46cf..5ce44a0f3ea7 100644 --- a/lib/Format/TokenAnalyzer.h +++ b/lib/Format/TokenAnalyzer.h @@ -1,9 +1,8 @@ //===--- TokenAnalyzer.h - Analyze Token Streams ----------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -36,10 +35,6 @@ namespace format { class Environment { public: - Environment(SourceManager &SM, FileID ID, ArrayRef<CharSourceRange> Ranges) - : SM(SM), ID(ID), CharRanges(Ranges.begin(), Ranges.end()), - FirstStartColumn(0), NextStartColumn(0), LastStartColumn(0) {} - // This sets up an virtual file system with file \p FileName containing the // fragment \p Code. Assumes that \p Code starts at \p FirstStartColumn, // that the next lines of \p Code should start at \p NextStartColumn, and diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index 24c2f998c388..490c4f46135e 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -1,9 +1,8 @@ //===--- TokenAnnotator.cpp - Format C++ code -----------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -14,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "TokenAnnotator.h" +#include "FormatToken.h" #include "clang/Basic/SourceManager.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/Support/Debug.h" @@ -62,7 +62,7 @@ private: if (NonTemplateLess.count(CurrentToken->Previous)) return false; - const FormatToken &Previous = *CurrentToken->Previous; // The '<'. + const FormatToken &Previous = *CurrentToken->Previous; // The '<'. if (Previous.Previous) { if (Previous.Previous->Tok.isLiteral()) return false; @@ -299,6 +299,8 @@ private: CurrentToken->Type = TT_JavaAnnotation; if (Left->Previous && Left->Previous->is(TT_LeadingJavaAnnotation)) CurrentToken->Type = TT_LeadingJavaAnnotation; + if (Left->Previous && Left->Previous->is(TT_AttributeSquare)) + CurrentToken->Type = TT_AttributeSquare; if (!HasMultipleLines) Left->PackingKind = PPK_Inconclusive; @@ -349,9 +351,47 @@ private: return false; } + bool isCSharpAttributeSpecifier(const FormatToken &Tok) { + if (!Style.isCSharp()) + return false; + + const FormatToken *AttrTok = Tok.Next; + if (!AttrTok) + return false; + + // Just an empty declaration e.g. string []. + if (AttrTok->is(tok::r_square)) + return false; + + // Move along the tokens inbetween the '[' and ']' e.g. [STAThread]. + while (AttrTok && AttrTok->isNot(tok::r_square)) { + AttrTok = AttrTok->Next; + } + + if (!AttrTok) + return false; + + // Move past the end of ']'. + AttrTok = AttrTok->Next; + if (!AttrTok) + return false; + + // Limit this to being an access modifier that follows. + if (AttrTok->isOneOf(tok::kw_public, tok::kw_private, tok::kw_protected, + tok::kw_class, tok::kw_static, tok::l_square, + Keywords.kw_internal)) { + return true; + } + return false; + } + bool isCpp11AttributeSpecifier(const FormatToken &Tok) { if (!Style.isCpp() || !Tok.startsSequence(tok::l_square, tok::l_square)) return false; + // The first square bracket is part of an ObjC array literal + if (Tok.Previous && Tok.Previous->is(tok::at)) { + return false; + } const FormatToken *AttrTok = Tok.Next->Next; if (!AttrTok) return false; @@ -364,9 +404,9 @@ private: while (AttrTok && !AttrTok->startsSequence(tok::r_square, tok::r_square)) { // ObjC message send. We assume nobody will use : in a C++11 attribute // specifier parameter, although this is technically valid: - // [[foo(:)]] + // [[foo(:)]]. if (AttrTok->is(tok::colon) || - AttrTok->startsSequence(tok::identifier, tok::identifier) || + AttrTok->startsSequence(tok::identifier, tok::identifier) || AttrTok->startsSequence(tok::r_paren, tok::identifier)) return false; if (AttrTok->is(tok::ellipsis)) @@ -399,11 +439,17 @@ private: bool IsCpp11AttributeSpecifier = isCpp11AttributeSpecifier(*Left) || Contexts.back().InCpp11AttributeSpecifier; + // Treat C# Attributes [STAThread] much like C++ attributes [[...]]. + bool IsCSharp11AttributeSpecifier = + isCSharpAttributeSpecifier(*Left) || + Contexts.back().InCSharpAttributeSpecifier; + bool InsideInlineASM = Line.startsWith(tok::kw_asm); + bool IsCppStructuredBinding = Left->isCppStructuredBinding(Style); bool StartsObjCMethodExpr = - !InsideInlineASM && !CppArrayTemplates && Style.isCpp() && - !IsCpp11AttributeSpecifier && Contexts.back().CanBeExpression && - Left->isNot(TT_LambdaLSquare) && + !IsCppStructuredBinding && !InsideInlineASM && !CppArrayTemplates && + Style.isCpp() && !IsCpp11AttributeSpecifier && + Contexts.back().CanBeExpression && Left->isNot(TT_LambdaLSquare) && !CurrentToken->isOneOf(tok::l_brace, tok::r_square) && (!Parent || Parent->isOneOf(tok::colon, tok::l_square, tok::l_paren, @@ -411,11 +457,12 @@ private: Parent->isUnaryOperator() || // FIXME(bug 36976): ObjC return types shouldn't use TT_CastRParen. Parent->isOneOf(TT_ObjCForIn, TT_CastRParen) || - getBinOpPrecedence(Parent->Tok.getKind(), true, true) > prec::Unknown); + (getBinOpPrecedence(Parent->Tok.getKind(), true, true) > + prec::Unknown)); bool ColonFound = false; unsigned BindingIncrease = 1; - if (Left->isCppStructuredBinding(Style)) { + if (IsCppStructuredBinding) { Left->Type = TT_StructuredBindingLSquare; } else if (Left->is(TT_Unknown)) { if (StartsObjCMethodExpr) { @@ -476,6 +523,8 @@ private: // Should only be relevant to JavaScript: tok::kw_default)) { Left->Type = TT_ArrayInitializerLSquare; + } else if (IsCSharp11AttributeSpecifier) { + Left->Type = TT_AttributeSquare; } else { BindingIncrease = 10; Left->Type = TT_ArraySubscriptLSquare; @@ -490,14 +539,22 @@ private: Contexts.back().ColonIsObjCMethodExpr = StartsObjCMethodExpr; Contexts.back().InCpp11AttributeSpecifier = IsCpp11AttributeSpecifier; + Contexts.back().InCSharpAttributeSpecifier = IsCSharp11AttributeSpecifier; while (CurrentToken) { if (CurrentToken->is(tok::r_square)) { if (IsCpp11AttributeSpecifier) CurrentToken->Type = TT_AttributeSquare; - else if (CurrentToken->Next && CurrentToken->Next->is(tok::l_paren) && + if (IsCSharp11AttributeSpecifier) + CurrentToken->Type = TT_AttributeSquare; + else if (((CurrentToken->Next && + CurrentToken->Next->is(tok::l_paren)) || + (CurrentToken->Previous && + CurrentToken->Previous->Previous == Left)) && Left->is(TT_ObjCMethodExpr)) { - // An ObjC method call is rarely followed by an open parenthesis. + // An ObjC method call is rarely followed by an open parenthesis. It + // also can't be composed of just one token, unless it's a macro that + // will be expanded to more tokens. // FIXME: Do we incorrectly label ":" with this? StartsObjCMethodExpr = false; Left->Type = TT_Unknown; @@ -516,6 +573,10 @@ private: if (Parent && Parent->is(TT_PointerOrReference)) Parent->Type = TT_BinaryOperator; } + // An arrow after an ObjC method expression is not a lambda arrow. + if (CurrentToken->Type == TT_ObjCMethodExpr && CurrentToken->Next && + CurrentToken->Next->is(TT_LambdaArrow)) + CurrentToken->Next->Type = TT_Unknown; Left->MatchingParen = CurrentToken; CurrentToken->MatchingParen = Left; // FirstObjCSelectorName is set when a colon is found. This does @@ -523,11 +584,11 @@ private: // Here, we set FirstObjCSelectorName when the end of the method call is // reached, in case it was not set already. if (!Contexts.back().FirstObjCSelectorName) { - FormatToken* Previous = CurrentToken->getPreviousNonComment(); - if (Previous && Previous->is(TT_SelectorName)) { - Previous->ObjCSelectorNameParts = 1; - Contexts.back().FirstObjCSelectorName = Previous; - } + FormatToken *Previous = CurrentToken->getPreviousNonComment(); + if (Previous && Previous->is(TT_SelectorName)) { + Previous->ObjCSelectorNameParts = 1; + Contexts.back().FirstObjCSelectorName = Previous; + } } else { Left->ParameterCount = Contexts.back().FirstObjCSelectorName->ObjCSelectorNameParts; @@ -1062,13 +1123,16 @@ public: return LT_ImportStatement; } - // In .proto files, top-level options are very similar to import statements - // and should not be line-wrapped. + // In .proto files, top-level options and package statements are very + // similar to import statements and should not be line-wrapped. if (Style.Language == FormatStyle::LK_Proto && Line.Level == 0 && - CurrentToken->is(Keywords.kw_option)) { + CurrentToken->isOneOf(Keywords.kw_option, Keywords.kw_package)) { next(); - if (CurrentToken && CurrentToken->is(tok::identifier)) + if (CurrentToken && CurrentToken->is(tok::identifier)) { + while (CurrentToken) + next(); return LT_ImportStatement; + } } bool KeywordVirtualFound = false; @@ -1134,11 +1198,12 @@ private: // Reset token type in case we have already looked at it and then // recovered from an error (e.g. failure to find the matching >). - if (!CurrentToken->isOneOf(TT_LambdaLSquare, TT_ForEachMacro, - TT_FunctionLBrace, TT_ImplicitStringLiteral, - TT_InlineASMBrace, TT_JsFatArrow, TT_LambdaArrow, - TT_OverloadedOperator, TT_RegexLiteral, - TT_TemplateString, TT_ObjCStringLiteral)) + if (!CurrentToken->isOneOf( + TT_LambdaLSquare, TT_LambdaLBrace, TT_ForEachMacro, + TT_TypenameMacro, TT_FunctionLBrace, TT_ImplicitStringLiteral, + TT_InlineASMBrace, TT_JsFatArrow, TT_LambdaArrow, TT_NamespaceMacro, + TT_OverloadedOperator, TT_RegexLiteral, TT_TemplateString, + TT_ObjCStringLiteral)) CurrentToken->Type = TT_Unknown; CurrentToken->Role.reset(); CurrentToken->MatchingParen = nullptr; @@ -1182,6 +1247,7 @@ private: bool CaretFound = false; bool IsForEachMacro = false; bool InCpp11AttributeSpecifier = false; + bool InCSharpAttributeSpecifier = false; }; /// Puts a new \c Context onto the stack \c Contexts for the lifetime @@ -1355,6 +1421,7 @@ private: if (AfterParen->Tok.isNot(tok::caret)) { if (FormatToken *BeforeParen = Current.MatchingParen->Previous) if (BeforeParen->is(tok::identifier) && + !BeforeParen->is(TT_TypenameMacro) && BeforeParen->TokenText == BeforeParen->TokenText.upper() && (!BeforeParen->Previous || BeforeParen->Previous->ClosesTemplateDeclaration)) @@ -1389,7 +1456,8 @@ private: Current.Type = Current.Previous->Type; } } else if (canBeObjCSelectorComponent(Current) && - // FIXME(bug 36976): ObjC return types shouldn't use TT_CastRParen. + // FIXME(bug 36976): ObjC return types shouldn't use + // TT_CastRParen. Current.Previous && Current.Previous->is(TT_CastRParen) && Current.Previous->MatchingParen && Current.Previous->MatchingParen->Previous && @@ -1605,7 +1673,8 @@ private: FormatToken *TokenBeforeMatchingParen = PrevToken->MatchingParen->getPreviousNonComment(); if (TokenBeforeMatchingParen && - TokenBeforeMatchingParen->isOneOf(tok::kw_typeof, tok::kw_decltype)) + TokenBeforeMatchingParen->isOneOf(tok::kw_typeof, tok::kw_decltype, + TT_TypenameMacro)) return TT_PointerOrReference; } @@ -1912,12 +1981,15 @@ void TokenAnnotator::setCommentLineLevels( NextNonCommentLine->First->NewlinesBefore <= 1 && NextNonCommentLine->First->OriginalColumn == (*I)->First->OriginalColumn) { - // Align comments for preprocessor lines with the # in column 0. - // Otherwise, align with the next line. - (*I)->Level = (NextNonCommentLine->Type == LT_PreprocessorDirective || - NextNonCommentLine->Type == LT_ImportStatement) - ? 0 - : NextNonCommentLine->Level; + // Align comments for preprocessor lines with the # in column 0 if + // preprocessor lines are not indented. Otherwise, align with the next + // line. + (*I)->Level = + (Style.IndentPPDirectives != FormatStyle::PPDIS_BeforeHash && + (NextNonCommentLine->Type == LT_PreprocessorDirective || + NextNonCommentLine->Type == LT_ImportStatement)) + ? 0 + : NextNonCommentLine->Level; } else { NextNonCommentLine = (*I)->First->isNot(tok::r_brace) ? (*I) : nullptr; } @@ -2033,7 +2105,7 @@ static bool isFunctionDeclarationName(const FormatToken &Current, return true; for (const FormatToken *Tok = Next->Next; Tok && Tok != Next->MatchingParen; Tok = Tok->Next) { - if (Tok->is(tok::l_paren) && Tok->MatchingParen) { + if (Tok->isOneOf(tok::l_paren, TT_TemplateOpener) && Tok->MatchingParen) { Tok = Tok->MatchingParen; continue; } @@ -2245,6 +2317,9 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line, return 500; } + if (Left.is(tok::coloncolon) || + (Right.is(tok::period) && Style.Language == FormatStyle::LK_Proto)) + return 500; if (Right.isOneOf(TT_StartOfName, TT_FunctionDeclarationName) || Right.is(tok::kw_operator)) { if (Line.startsWith(tok::kw_for) && Right.PartOfMultiVariableDeclStmt) @@ -2263,9 +2338,6 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line, return 160; if (Left.is(TT_CastRParen)) return 100; - if (Left.is(tok::coloncolon) || - (Right.is(tok::period) && Style.Language == FormatStyle::LK_Proto)) - return 500; if (Left.isOneOf(tok::kw_class, tok::kw_struct)) return 5000; if (Left.is(tok::comment)) @@ -2391,6 +2463,12 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line, return 3; } +bool TokenAnnotator::spaceRequiredBeforeParens(const FormatToken &Right) const { + return Style.SpaceBeforeParens == FormatStyle::SBPO_Always || + (Style.SpaceBeforeParens == FormatStyle::SBPO_NonEmptyParentheses && + Right.ParameterCount > 0); +} + bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, const FormatToken &Left, const FormatToken &Right) { @@ -2415,9 +2493,9 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, if (Right.isOneOf(tok::semi, tok::comma)) return false; if (Right.is(tok::less) && Line.Type == LT_ObjCDecl) { - bool IsLightweightGeneric = - Right.MatchingParen && Right.MatchingParen->Next && - Right.MatchingParen->Next->is(tok::colon); + bool IsLightweightGeneric = Right.MatchingParen && + Right.MatchingParen->Next && + Right.MatchingParen->Next->is(tok::colon); return !IsLightweightGeneric && Style.ObjCSpaceBeforeProtocolList; } if (Right.is(tok::less) && Left.is(tok::kw_template)) @@ -2456,7 +2534,8 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, FormatToken *TokenBeforeMatchingParen = Left.MatchingParen->getPreviousNonComment(); if (!TokenBeforeMatchingParen || - !TokenBeforeMatchingParen->isOneOf(tok::kw_typeof, tok::kw_decltype)) + !TokenBeforeMatchingParen->isOneOf(tok::kw_typeof, tok::kw_decltype, + TT_TypenameMacro)) return true; } return (Left.Tok.isLiteral() || @@ -2537,9 +2616,11 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, (Left.isOneOf(tok::kw_try, Keywords.kw___except, tok::kw_catch, tok::kw_new, tok::kw_delete) && (!Left.Previous || Left.Previous->isNot(tok::period))))) || - (Style.SpaceBeforeParens == FormatStyle::SBPO_Always && + (spaceRequiredBeforeParens(Right) && (Left.is(tok::identifier) || Left.isFunctionLikeKeyword() || - Left.is(tok::r_paren)) && + Left.is(tok::r_paren) || Left.isSimpleTypeSpecifier() || + (Left.is(tok::r_square) && Left.MatchingParen && + Left.MatchingParen->is(TT_LambdaLSquare))) && Line.Type != LT_PreprocessorDirective); } if (Left.is(tok::at) && Right.Tok.getObjCKeywordID() != tok::objc_not_keyword) @@ -2602,7 +2683,8 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, // Slashes occur in text protocol extension syntax: [type/type] { ... }. if (Left.is(tok::slash) || Right.is(tok::slash)) return false; - if (Left.MatchingParen && Left.MatchingParen->is(TT_ProtoExtensionLSquare) && + if (Left.MatchingParen && + Left.MatchingParen->is(TT_ProtoExtensionLSquare) && Right.isOneOf(tok::l_brace, tok::less)) return !Style.Cpp11BracedListStyle; // A percent is probably part of a formatting specification, such as %lld. @@ -2612,7 +2694,7 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, // and "%d %d" if (Left.is(tok::numeric_constant) && Right.is(tok::percent)) return Right.WhitespaceRange.getEnd() != Right.WhitespaceRange.getBegin(); - } else if (Style.Language == FormatStyle::LK_JavaScript) { + } else if (Style.Language == FormatStyle::LK_JavaScript || Style.isCSharp()) { if (Left.is(TT_JsFatArrow)) return true; // for await ( ... @@ -2730,7 +2812,7 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, Left.isOneOf(TT_TrailingReturnArrow, TT_LambdaArrow)) return true; if (Right.is(TT_OverloadedOperatorLParen)) - return Style.SpaceBeforeParens == FormatStyle::SBPO_Always; + return spaceRequiredBeforeParens(Right); if (Left.is(tok::comma)) return true; if (Right.is(tok::comma)) @@ -2761,7 +2843,8 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, return true; } if (Left.is(TT_UnaryOperator)) - return Right.is(TT_BinaryOperator); + return (Style.SpaceAfterLogicalNot && Left.is(tok::exclaim)) || + Right.is(TT_BinaryOperator); // If the next token is a binary operator or a selector name, we have // incorrectly classified the parenthesis as a cast. FIXME: Detect correctly. @@ -2814,7 +2897,7 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, return true; if (Left.is(TT_TemplateCloser) && Right.is(tok::l_paren) && Right.isNot(TT_FunctionTypeLParen)) - return Style.SpaceBeforeParens == FormatStyle::SBPO_Always; + return spaceRequiredBeforeParens(Right); if (Right.is(TT_TemplateOpener) && Left.is(tok::r_paren) && Left.MatchingParen && Left.MatchingParen->is(TT_OverloadedOperatorLParen)) return false; @@ -2831,7 +2914,7 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, // Returns 'true' if 'Tok' is a brace we'd want to break before in Allman style. static bool isAllmanBrace(const FormatToken &Tok) { return Tok.is(tok::l_brace) && Tok.BlockKind == BK_Block && - !Tok.isOneOf(TT_ObjCBlockLBrace, TT_DictLiteral); + !Tok.isOneOf(TT_ObjCBlockLBrace, TT_LambdaLBrace, TT_DictLiteral); } bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, @@ -2959,6 +3042,27 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, if (Left.is(TT_ObjCBlockLBrace) && !Style.AllowShortBlocksOnASingleLine) return true; + if (Left.is(TT_LambdaLBrace)) { + if (Left.MatchingParen && Left.MatchingParen->Next && + Left.MatchingParen->Next->isOneOf(tok::comma, tok::r_paren) && + Style.AllowShortLambdasOnASingleLine == FormatStyle::SLS_Inline) + return false; + + if (Style.AllowShortLambdasOnASingleLine == FormatStyle::SLS_None || + Style.AllowShortLambdasOnASingleLine == FormatStyle::SLS_Inline || + (!Left.Children.empty() && + Style.AllowShortLambdasOnASingleLine == FormatStyle::SLS_Empty)) + return true; + } + + // Put multiple C# attributes on a new line. + if (Style.isCSharp() && + ((Left.is(TT_AttributeSquare) && Left.is(tok::r_square)) || + (Left.is(tok::r_square) && Right.is(TT_AttributeSquare) && + Right.is(tok::l_square)))) + return true; + + // Put multiple Java annotation on a new line. if ((Style.Language == FormatStyle::LK_Java || Style.Language == FormatStyle::LK_JavaScript) && Left.is(TT_LeadingJavaAnnotation) && @@ -3119,7 +3223,7 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line, // function f(): a is B { ... } // Do not break before is in these cases. if (Right.is(Keywords.kw_is)) { - const FormatToken* Next = Right.getNextNonComment(); + const FormatToken *Next = Right.getNextNonComment(); // If `is` is followed by a colon, it's likely that it's a dict key, so // ignore it for this check. // For example this is common in Polymer: @@ -3159,6 +3263,11 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line, return false; // must not break in "module foo { ...}" if (Right.is(TT_TemplateString) && Right.closesScope()) return false; + // Don't split tagged template literal so there is a break between the tag + // identifier and template string. + if (Left.is(tok::identifier) && Right.is(TT_TemplateString)) { + return false; + } if (Left.is(TT_TemplateString) && Left.opensScope()) return true; } diff --git a/lib/Format/TokenAnnotator.h b/lib/Format/TokenAnnotator.h index e2f2c469d267..702ac6c0d76e 100644 --- a/lib/Format/TokenAnnotator.h +++ b/lib/Format/TokenAnnotator.h @@ -1,9 +1,8 @@ //===--- TokenAnnotator.h - Format C++ code ---------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -100,14 +99,23 @@ public: /// function declaration. Asserts MightBeFunctionDecl. bool mightBeFunctionDefinition() const { assert(MightBeFunctionDecl); - // FIXME: Line.Last points to other characters than tok::semi - // and tok::lbrace. - return !Last->isOneOf(tok::semi, tok::comment); + // Try to determine if the end of a stream of tokens is either the + // Definition or the Declaration for a function. It does this by looking for + // the ';' in foo(); and using that it ends with a ; to know this is the + // Definition, however the line could end with + // foo(); /* comment */ + // or + // foo(); // comment + // or + // foo() // comment + // endsWith() ignores the comment. + return !endsWith(tok::semi); } /// \c true if this line starts a namespace definition. bool startsWithNamespace() const { return startsWith(tok::kw_namespace) || + startsWith(TT_NamespaceMacro) || startsWith(tok::kw_inline, tok::kw_namespace) || startsWith(tok::kw_export, tok::kw_namespace); } @@ -165,6 +173,8 @@ private: unsigned splitPenalty(const AnnotatedLine &Line, const FormatToken &Tok, bool InFunctionDecl); + bool spaceRequiredBeforeParens(const FormatToken &Right) const; + bool spaceRequiredBetween(const AnnotatedLine &Line, const FormatToken &Left, const FormatToken &Right); diff --git a/lib/Format/UnwrappedLineFormatter.cpp b/lib/Format/UnwrappedLineFormatter.cpp index 6b6a9aff461a..3f3c80bc1ccf 100644 --- a/lib/Format/UnwrappedLineFormatter.cpp +++ b/lib/Format/UnwrappedLineFormatter.cpp @@ -1,14 +1,13 @@ //===--- UnwrappedLineFormatter.cpp - Format C++ code ---------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// -#include "NamespaceEndCommentsFixer.h" #include "UnwrappedLineFormatter.h" +#include "NamespaceEndCommentsFixer.h" #include "WhitespaceManager.h" #include "llvm/Support/Debug.h" #include <queue> @@ -95,7 +94,7 @@ private: /// characters to the left from their level. int getIndentOffset(const FormatToken &RootToken) { if (Style.Language == FormatStyle::LK_Java || - Style.Language == FormatStyle::LK_JavaScript) + Style.Language == FormatStyle::LK_JavaScript || Style.isCSharp()) return 0; if (RootToken.isAccessSpecifier(false) || RootToken.isObjCAccessSpecifier() || @@ -135,20 +134,29 @@ private: unsigned Indent = 0; }; -bool isNamespaceDeclaration(const AnnotatedLine *Line) { - const FormatToken *NamespaceTok = Line->First; - return NamespaceTok && NamespaceTok->getNamespaceToken(); -} - -bool isEndOfNamespace(const AnnotatedLine *Line, - const SmallVectorImpl<AnnotatedLine *> &AnnotatedLines) { +const FormatToken *getMatchingNamespaceToken( + const AnnotatedLine *Line, + const SmallVectorImpl<AnnotatedLine *> &AnnotatedLines) { if (!Line->startsWith(tok::r_brace)) - return false; + return nullptr; size_t StartLineIndex = Line->MatchingOpeningBlockLineIndex; if (StartLineIndex == UnwrappedLine::kInvalidIndex) - return false; + return nullptr; assert(StartLineIndex < AnnotatedLines.size()); - return isNamespaceDeclaration(AnnotatedLines[StartLineIndex]); + return AnnotatedLines[StartLineIndex]->First->getNamespaceToken(); +} + +StringRef getNamespaceTokenText(const AnnotatedLine *Line) { + const FormatToken *NamespaceToken = Line->First->getNamespaceToken(); + return NamespaceToken ? NamespaceToken->TokenText : StringRef(); +} + +StringRef getMatchingNamespaceTokenText( + const AnnotatedLine *Line, + const SmallVectorImpl<AnnotatedLine *> &AnnotatedLines) { + const FormatToken *NamespaceToken = + getMatchingNamespaceToken(Line, AnnotatedLines); + return NamespaceToken ? NamespaceToken->TokenText : StringRef(); } class LineJoiner { @@ -250,10 +258,11 @@ private: TheLine->Level != 0); if (Style.CompactNamespaces) { - if (isNamespaceDeclaration(TheLine)) { + if (auto nsToken = TheLine->First->getNamespaceToken()) { int i = 0; unsigned closingLine = TheLine->MatchingClosingBlockLineIndex - 1; - for (; I + 1 + i != E && isNamespaceDeclaration(I[i + 1]) && + for (; I + 1 + i != E && + nsToken->TokenText == getNamespaceTokenText(I[i + 1]) && closingLine == I[i + 1]->MatchingClosingBlockLineIndex && I[i + 1]->Last->TotalLength < Limit; i++, closingLine--) { @@ -265,10 +274,12 @@ private: return i; } - if (isEndOfNamespace(TheLine, AnnotatedLines)) { + if (auto nsToken = getMatchingNamespaceToken(TheLine, AnnotatedLines)) { int i = 0; unsigned openingLine = TheLine->MatchingOpeningBlockLineIndex - 1; - for (; I + 1 + i != E && isEndOfNamespace(I[i + 1], AnnotatedLines) && + for (; I + 1 + i != E && + nsToken->TokenText == + getMatchingNamespaceTokenText(I[i + 1], AnnotatedLines) && openingLine == I[i + 1]->MatchingOpeningBlockLineIndex; i++, openingLine--) { // No space between consecutive braces @@ -414,10 +425,12 @@ private: if (I[1]->First->isOneOf(tok::semi, tok::kw_if, tok::kw_for, tok::kw_while, TT_LineComment)) return 0; - // Only inline simple if's (no nested if or else). - if (I + 2 != E && Line.startsWith(tok::kw_if) && - I[2]->First->is(tok::kw_else)) - return 0; + // Only inline simple if's (no nested if or else), unless specified + if (Style.AllowShortIfStatementsOnASingleLine != FormatStyle::SIS_Always) { + if (I + 2 != E && Line.startsWith(tok::kw_if) && + I[2]->First->is(tok::kw_else)) + return 0; + } return 1; } @@ -691,10 +704,8 @@ public: /// Formats an \c AnnotatedLine and returns the penalty. /// /// If \p DryRun is \c false, directly applies the changes. - virtual unsigned formatLine(const AnnotatedLine &Line, - unsigned FirstIndent, - unsigned FirstStartColumn, - bool DryRun) = 0; + virtual unsigned formatLine(const AnnotatedLine &Line, unsigned FirstIndent, + unsigned FirstStartColumn, bool DryRun) = 0; protected: /// If the \p State's next token is an r_brace closing a nested block, @@ -822,7 +833,7 @@ public: LineState State = Indenter->getInitialState(FirstIndent, FirstStartColumn, &Line, DryRun); while (State.NextToken) { - formatChildren(State, /*Newline=*/false, DryRun, Penalty); + formatChildren(State, /*NewLine=*/false, DryRun, Penalty); Indenter->addTokenToState( State, /*Newline=*/State.NextToken->MustBreakBefore, DryRun); } @@ -1009,13 +1020,10 @@ private: } // anonymous namespace -unsigned -UnwrappedLineFormatter::format(const SmallVectorImpl<AnnotatedLine *> &Lines, - bool DryRun, int AdditionalIndent, - bool FixBadIndentation, - unsigned FirstStartColumn, - unsigned NextStartColumn, - unsigned LastStartColumn) { +unsigned UnwrappedLineFormatter::format( + const SmallVectorImpl<AnnotatedLine *> &Lines, bool DryRun, + int AdditionalIndent, bool FixBadIndentation, unsigned FirstStartColumn, + unsigned NextStartColumn, unsigned LastStartColumn) { LineJoiner Joiner(Style, Keywords, Lines); // Try to look up already computed penalty in DryRun-mode. @@ -1077,7 +1085,9 @@ UnwrappedLineFormatter::format(const SmallVectorImpl<AnnotatedLine *> &Lines, TheLine.Last->TotalLength + Indent <= ColumnLimit || (TheLine.Type == LT_ImportStatement && (Style.Language != FormatStyle::LK_JavaScript || - !Style.JavaScriptWrapImports)); + !Style.JavaScriptWrapImports)) || + (Style.isCSharp() && + TheLine.InPPDirective); // don't split #regions in C# if (Style.ColumnLimit == 0) NoColumnLimitLineFormatter(Indenter, Whitespaces, Style, this) .formatLine(TheLine, NextStartColumn + Indent, @@ -1182,8 +1192,10 @@ void UnwrappedLineFormatter::formatFirstToken( if (Newlines) Indent = NewlineIndent; - // Preprocessor directives get indented after the hash, if indented. - if (Line.Type == LT_PreprocessorDirective || Line.Type == LT_ImportStatement) + // Preprocessor directives get indented before the hash only if specified + if (Style.IndentPPDirectives != FormatStyle::PPDIS_BeforeHash && + (Line.Type == LT_PreprocessorDirective || + Line.Type == LT_ImportStatement)) Indent = 0; Whitespaces->replaceWhitespace(RootToken, Newlines, Indent, Indent, diff --git a/lib/Format/UnwrappedLineFormatter.h b/lib/Format/UnwrappedLineFormatter.h index dac210ea62b1..a1ff16999589 100644 --- a/lib/Format/UnwrappedLineFormatter.h +++ b/lib/Format/UnwrappedLineFormatter.h @@ -1,9 +1,8 @@ //===--- UnwrappedLineFormatter.h - Format C++ code -------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -40,10 +39,8 @@ public: /// Format the current block and return the penalty. unsigned format(const SmallVectorImpl<AnnotatedLine *> &Lines, bool DryRun = false, int AdditionalIndent = 0, - bool FixBadIndentation = false, - unsigned FirstStartColumn = 0, - unsigned NextStartColumn = 0, - unsigned LastStartColumn = 0); + bool FixBadIndentation = false, unsigned FirstStartColumn = 0, + unsigned NextStartColumn = 0, unsigned LastStartColumn = 0); private: /// Add a new line and the required indent before the first Token diff --git a/lib/Format/UnwrappedLineParser.cpp b/lib/Format/UnwrappedLineParser.cpp index 3cd3c8f9cdf6..a35e98ae5503 100644 --- a/lib/Format/UnwrappedLineParser.cpp +++ b/lib/Format/UnwrappedLineParser.cpp @@ -1,9 +1,8 @@ //===--- UnwrappedLineParser.cpp - Format C++ code ------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -173,10 +172,16 @@ class CompoundStatementIndenter { public: CompoundStatementIndenter(UnwrappedLineParser *Parser, const FormatStyle &Style, unsigned &LineLevel) + : CompoundStatementIndenter(Parser, LineLevel, + Style.BraceWrapping.AfterControlStatement, + Style.BraceWrapping.IndentBraces) { + } + CompoundStatementIndenter(UnwrappedLineParser *Parser, unsigned &LineLevel, + bool WrapBrace, bool IndentBrace) : LineLevel(LineLevel), OldLineLevel(LineLevel) { - if (Style.BraceWrapping.AfterControlStatement) + if (WrapBrace) Parser->addUnwrappedLine(); - if (Style.BraceWrapping.IndentBraces) + if (IndentBrace) ++LineLevel; } ~CompoundStatementIndenter() { LineLevel = OldLineLevel; } @@ -482,7 +487,7 @@ void UnwrappedLineParser::calculateBraceTypes(bool ExpectClassBody) { break; case tok::identifier: if (!Tok->is(TT_StatementMacro)) - break; + break; LLVM_FALLTHROUGH; case tok::at: case tok::semi: @@ -625,7 +630,7 @@ static bool isIIFE(const UnwrappedLine &Line, static bool ShouldBreakBeforeBrace(const FormatStyle &Style, const FormatToken &InitialToken) { - if (InitialToken.is(tok::kw_namespace)) + if (InitialToken.isOneOf(tok::kw_namespace, TT_NamespaceMacro)) return Style.BraceWrapping.AfterNamespace; if (InitialToken.is(tok::kw_class)) return Style.BraceWrapping.AfterClass; @@ -656,6 +661,7 @@ void UnwrappedLineParser::parseChildBlock() { void UnwrappedLineParser::parsePPDirective() { assert(FormatTok->Tok.is(tok::hash) && "'#' expected"); ScopedMacroState MacroState(*Line, Tokens, FormatTok); + nextToken(); if (!FormatTok->Tok.getIdentifierInfo()) { @@ -799,7 +805,7 @@ void UnwrappedLineParser::parsePPEndIf() { void UnwrappedLineParser::parsePPDefine() { nextToken(); - if (FormatTok->Tok.getKind() != tok::identifier) { + if (!FormatTok->Tok.getIdentifierInfo()) { IncludeGuard = IG_Rejected; IncludeGuardToken = nullptr; parsePPUnknown(); @@ -824,7 +830,7 @@ void UnwrappedLineParser::parsePPDefine() { FormatTok->WhitespaceRange.getEnd()) { parseParens(); } - if (Style.IndentPPDirectives == FormatStyle::PPDIS_AfterHash) + if (Style.IndentPPDirectives != FormatStyle::PPDIS_None) Line->Level += PPBranchLevel + 1; addUnwrappedLine(); ++Line->Level; @@ -841,7 +847,7 @@ void UnwrappedLineParser::parsePPUnknown() { do { nextToken(); } while (!eof()); - if (Style.IndentPPDirectives == FormatStyle::PPDIS_AfterHash) + if (Style.IndentPPDirectives != FormatStyle::PPDIS_None) Line->Level += PPBranchLevel + 1; addUnwrappedLine(); } @@ -1000,7 +1006,7 @@ void UnwrappedLineParser::parseStructuralElement() { case tok::kw_protected: case tok::kw_private: if (Style.Language == FormatStyle::LK_Java || - Style.Language == FormatStyle::LK_JavaScript) + Style.Language == FormatStyle::LK_JavaScript || Style.isCSharp()) nextToken(); else parseAccessSpecifier(); @@ -1116,6 +1122,10 @@ void UnwrappedLineParser::parseStructuralElement() { parseStatementMacro(); return; } + if (Style.isCpp() && FormatTok->is(TT_NamespaceMacro)) { + parseNamespace(); + return; + } // In all other cases, parse the declaration. break; default: @@ -1167,8 +1177,8 @@ void UnwrappedLineParser::parseStructuralElement() { case tok::objc_synchronized: nextToken(); if (FormatTok->Tok.is(tok::l_paren)) - // Skip synchronization object - parseParens(); + // Skip synchronization object + parseParens(); if (FormatTok->Tok.is(tok::l_brace)) { if (Style.BraceWrapping.AfterControlStatement) addUnwrappedLine(); @@ -1214,9 +1224,9 @@ void UnwrappedLineParser::parseStructuralElement() { // parseRecord falls through and does not yet add an unwrapped line as a // record declaration or definition can start a structural element. parseRecord(); - // This does not apply for Java and JavaScript. + // This does not apply for Java, JavaScript and C#. if (Style.Language == FormatStyle::LK_Java || - Style.Language == FormatStyle::LK_JavaScript) { + Style.Language == FormatStyle::LK_JavaScript || Style.isCSharp()) { if (FormatTok->is(tok::semi)) nextToken(); addUnwrappedLine(); @@ -1329,10 +1339,15 @@ void UnwrappedLineParser::parseStructuralElement() { // See if the following token should start a new unwrapped line. StringRef Text = FormatTok->TokenText; nextToken(); - if (Line->Tokens.size() == 1 && - // JS doesn't have macros, and within classes colons indicate fields, - // not labels. - Style.Language != FormatStyle::LK_JavaScript) { + + // JS doesn't have macros, and within classes colons indicate fields, not + // labels. + if (Style.Language == FormatStyle::LK_JavaScript) + break; + + TokenCount = Line->Tokens.size(); + if (TokenCount == 1 || + (TokenCount == 2 && Line->Tokens.front().Tok->is(tok::comment))) { if (FormatTok->Tok.is(tok::colon) && !Line->MustBeDeclaration) { Line->Tokens.begin()->Tok->MustBreakBefore = true; parseLabel(); @@ -1402,6 +1417,8 @@ bool UnwrappedLineParser::tryToParseLambda() { if (!tryToParseLambdaIntroducer()) return false; + bool SeenArrow = false; + while (FormatTok->isNot(tok::l_brace)) { if (FormatTok->isSimpleTypeSpecifier()) { nextToken(); @@ -1423,16 +1440,57 @@ bool UnwrappedLineParser::tryToParseLambda() { case tok::numeric_constant: case tok::coloncolon: case tok::kw_mutable: + case tok::kw_noexcept: nextToken(); break; + // Specialization of a template with an integer parameter can contain + // arithmetic, logical, comparison and ternary operators. + // + // FIXME: This also accepts sequences of operators that are not in the scope + // of a template argument list. + // + // In a C++ lambda a template type can only occur after an arrow. We use + // this as an heuristic to distinguish between Objective-C expressions + // followed by an `a->b` expression, such as: + // ([obj func:arg] + a->b) + // Otherwise the code below would parse as a lambda. + case tok::plus: + case tok::minus: + case tok::exclaim: + case tok::tilde: + case tok::slash: + case tok::percent: + case tok::lessless: + case tok::pipe: + case tok::pipepipe: + case tok::ampamp: + case tok::caret: + case tok::equalequal: + case tok::exclaimequal: + case tok::greaterequal: + case tok::lessequal: + case tok::question: + case tok::colon: + case tok::kw_true: + case tok::kw_false: + if (SeenArrow) { + nextToken(); + break; + } + return true; case tok::arrow: + // This might or might not actually be a lambda arrow (this could be an + // ObjC method invocation followed by a dereferencing arrow). We might + // reset this back to TT_Unknown in TokenAnnotator. FormatTok->Type = TT_LambdaArrow; + SeenArrow = true; nextToken(); break; default: return true; } } + FormatTok->Type = TT_LambdaLBrace; LSquare.Type = TT_LambdaLSquare; parseChildBlock(); return true; @@ -1806,12 +1864,17 @@ void UnwrappedLineParser::parseTryCatch() { } void UnwrappedLineParser::parseNamespace() { - assert(FormatTok->Tok.is(tok::kw_namespace) && "'namespace' expected"); + assert(FormatTok->isOneOf(tok::kw_namespace, TT_NamespaceMacro) && + "'namespace' expected"); const FormatToken &InitialToken = *FormatTok; nextToken(); - while (FormatTok->isOneOf(tok::identifier, tok::coloncolon)) - nextToken(); + if (InitialToken.is(TT_NamespaceMacro)) { + parseParens(); + } else { + while (FormatTok->isOneOf(tok::identifier, tok::coloncolon)) + nextToken(); + } if (FormatTok->Tok.is(tok::l_brace)) { if (ShouldBreakBeforeBrace(Style, InitialToken)) addUnwrappedLine(); @@ -1907,7 +1970,9 @@ void UnwrappedLineParser::parseLabel() { if (Line->Level > 1 || (!Line->InPPDirective && Line->Level > 0)) --Line->Level; if (CommentsBeforeNextToken.empty() && FormatTok->Tok.is(tok::l_brace)) { - CompoundStatementIndenter Indenter(this, Style, Line->Level); + CompoundStatementIndenter Indenter(this, Line->Level, + Style.BraceWrapping.AfterCaseLabel, + Style.BraceWrapping.IndentBraces); parseBlock(/*MustBeDeclaration=*/false); if (FormatTok->Tok.is(tok::kw_break)) { if (Style.BraceWrapping.AfterControlStatement) @@ -1976,6 +2041,10 @@ bool UnwrappedLineParser::parseEnum() { FormatTok->isOneOf(tok::colon, tok::question)) return false; + // In protobuf, "enum" can be used as a field name. + if (Style.Language == FormatStyle::LK_Proto && FormatTok->is(tok::equal)) + return false; + // Eat up enum class ... if (FormatTok->Tok.is(tok::kw_class) || FormatTok->Tok.is(tok::kw_struct)) nextToken(); @@ -2347,8 +2416,7 @@ void UnwrappedLineParser::parseJavaScriptEs6ImportExport() { } } -void UnwrappedLineParser::parseStatementMacro() -{ +void UnwrappedLineParser::parseStatementMacro() { nextToken(); if (FormatTok->is(tok::l_paren)) parseParens(); @@ -2631,6 +2699,9 @@ void UnwrappedLineParser::readToken(int LevelDifference) { // Comments stored before the preprocessor directive need to be output // before the preprocessor directive, at the same level as the // preprocessor directive, as we consider them to apply to the directive. + if (Style.IndentPPDirectives == FormatStyle::PPDIS_BeforeHash && + PPBranchLevel > 0) + Line->Level += PPBranchLevel; flushComments(isOnNewLine(*FormatTok)); parsePPDirective(); } diff --git a/lib/Format/UnwrappedLineParser.h b/lib/Format/UnwrappedLineParser.h index 55d60dff9152..e1b35317c7c0 100644 --- a/lib/Format/UnwrappedLineParser.h +++ b/lib/Format/UnwrappedLineParser.h @@ -1,9 +1,8 @@ //===--- UnwrappedLineParser.h - Format C++ code ----------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -77,8 +76,7 @@ class UnwrappedLineParser { public: UnwrappedLineParser(const FormatStyle &Style, const AdditionalKeywords &Keywords, - unsigned FirstStartColumn, - ArrayRef<FormatToken *> Tokens, + unsigned FirstStartColumn, ArrayRef<FormatToken *> Tokens, UnwrappedLineConsumer &Callback); void parse(); diff --git a/lib/Format/UsingDeclarationsSorter.cpp b/lib/Format/UsingDeclarationsSorter.cpp index 9e49e7913033..b6559db61d0c 100644 --- a/lib/Format/UsingDeclarationsSorter.cpp +++ b/lib/Format/UsingDeclarationsSorter.cpp @@ -1,9 +1,8 @@ //===--- UsingDeclarationsSorter.cpp ----------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -128,8 +127,7 @@ void endUsingDeclarationBlock( } SmallVector<UsingDeclaration, 4> SortedUsingDeclarations( UsingDeclarations->begin(), UsingDeclarations->end()); - std::stable_sort(SortedUsingDeclarations.begin(), - SortedUsingDeclarations.end()); + llvm::stable_sort(SortedUsingDeclarations); SortedUsingDeclarations.erase( std::unique(SortedUsingDeclarations.begin(), SortedUsingDeclarations.end(), diff --git a/lib/Format/UsingDeclarationsSorter.h b/lib/Format/UsingDeclarationsSorter.h index 7e5cf7610d67..4285a1ca03cf 100644 --- a/lib/Format/UsingDeclarationsSorter.h +++ b/lib/Format/UsingDeclarationsSorter.h @@ -1,9 +1,8 @@ //===--- UsingDeclarationsSorter.h ------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// diff --git a/lib/Format/WhitespaceManager.cpp b/lib/Format/WhitespaceManager.cpp index 032b1333322d..23fbf94c7588 100644 --- a/lib/Format/WhitespaceManager.cpp +++ b/lib/Format/WhitespaceManager.cpp @@ -1,9 +1,8 @@ //===--- WhitespaceManager.cpp - Format C++ code --------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -92,6 +91,7 @@ const tooling::Replacements &WhitespaceManager::generateReplacements() { llvm::sort(Changes, Change::IsBeforeInFile(SourceMgr)); calculateLineBreakInformation(); + alignConsecutiveMacros(); alignConsecutiveDeclarations(); alignConsecutiveAssignments(); alignTrailingComments(); @@ -429,23 +429,148 @@ static unsigned AlignTokens(const FormatStyle &Style, F &&Matches, return i; } +// Aligns a sequence of matching tokens, on the MinColumn column. +// +// Sequences start from the first matching token to align, and end at the +// first token of the first line that doesn't need to be aligned. +// +// We need to adjust the StartOfTokenColumn of each Change that is on a line +// containing any matching token to be aligned and located after such token. +static void AlignMacroSequence( + unsigned &StartOfSequence, unsigned &EndOfSequence, unsigned &MinColumn, + unsigned &MaxColumn, bool &FoundMatchOnLine, + std::function<bool(const WhitespaceManager::Change &C)> AlignMacrosMatches, + SmallVector<WhitespaceManager::Change, 16> &Changes) { + if (StartOfSequence > 0 && StartOfSequence < EndOfSequence) { + + FoundMatchOnLine = false; + int Shift = 0; + + for (unsigned I = StartOfSequence; I != EndOfSequence; ++I) { + if (Changes[I].NewlinesBefore > 0) { + Shift = 0; + FoundMatchOnLine = false; + } + + // If this is the first matching token to be aligned, remember by how many + // spaces it has to be shifted, so the rest of the changes on the line are + // shifted by the same amount + if (!FoundMatchOnLine && AlignMacrosMatches(Changes[I])) { + FoundMatchOnLine = true; + Shift = MinColumn - Changes[I].StartOfTokenColumn; + Changes[I].Spaces += Shift; + } + + assert(Shift >= 0); + Changes[I].StartOfTokenColumn += Shift; + if (I + 1 != Changes.size()) + Changes[I + 1].PreviousEndOfTokenColumn += Shift; + } + } + + MinColumn = 0; + MaxColumn = UINT_MAX; + StartOfSequence = 0; + EndOfSequence = 0; +} + +void WhitespaceManager::alignConsecutiveMacros() { + if (!Style.AlignConsecutiveMacros) + return; + + auto AlignMacrosMatches = [](const Change &C) { + const FormatToken *Current = C.Tok; + unsigned SpacesRequiredBefore = 1; + + if (Current->SpacesRequiredBefore == 0 || !Current->Previous) + return false; + + Current = Current->Previous; + + // If token is a ")", skip over the parameter list, to the + // token that precedes the "(" + if (Current->is(tok::r_paren) && Current->MatchingParen) { + Current = Current->MatchingParen->Previous; + SpacesRequiredBefore = 0; + } + + if (!Current || !Current->is(tok::identifier)) + return false; + + if (!Current->Previous || !Current->Previous->is(tok::pp_define)) + return false; + + // For a macro function, 0 spaces are required between the + // identifier and the lparen that opens the parameter list. + // For a simple macro, 1 space is required between the + // identifier and the first token of the defined value. + return Current->Next->SpacesRequiredBefore == SpacesRequiredBefore; + }; + + unsigned MinColumn = 0; + unsigned MaxColumn = UINT_MAX; + + // Start and end of the token sequence we're processing. + unsigned StartOfSequence = 0; + unsigned EndOfSequence = 0; + + // Whether a matching token has been found on the current line. + bool FoundMatchOnLine = false; + + unsigned I = 0; + for (unsigned E = Changes.size(); I != E; ++I) { + if (Changes[I].NewlinesBefore != 0) { + EndOfSequence = I; + // If there is a blank line, or if the last line didn't contain any + // matching token, the sequence ends here. + if (Changes[I].NewlinesBefore > 1 || !FoundMatchOnLine) + AlignMacroSequence(StartOfSequence, EndOfSequence, MinColumn, MaxColumn, + FoundMatchOnLine, AlignMacrosMatches, Changes); + + FoundMatchOnLine = false; + } + + if (!AlignMacrosMatches(Changes[I])) + continue; + + FoundMatchOnLine = true; + + if (StartOfSequence == 0) + StartOfSequence = I; + + unsigned ChangeMinColumn = Changes[I].StartOfTokenColumn; + int LineLengthAfter = -Changes[I].Spaces; + for (unsigned j = I; j != E && Changes[j].NewlinesBefore == 0; ++j) + LineLengthAfter += Changes[j].Spaces + Changes[j].TokenLength; + unsigned ChangeMaxColumn = Style.ColumnLimit - LineLengthAfter; + + MinColumn = std::max(MinColumn, ChangeMinColumn); + MaxColumn = std::min(MaxColumn, ChangeMaxColumn); + } + + EndOfSequence = I; + AlignMacroSequence(StartOfSequence, EndOfSequence, MinColumn, MaxColumn, + FoundMatchOnLine, AlignMacrosMatches, Changes); +} + void WhitespaceManager::alignConsecutiveAssignments() { if (!Style.AlignConsecutiveAssignments) return; - AlignTokens(Style, - [&](const Change &C) { - // Do not align on equal signs that are first on a line. - if (C.NewlinesBefore > 0) - return false; + AlignTokens( + Style, + [&](const Change &C) { + // Do not align on equal signs that are first on a line. + if (C.NewlinesBefore > 0) + return false; - // Do not align on equal signs that are last on a line. - if (&C != &Changes.back() && (&C + 1)->NewlinesBefore > 0) - return false; + // Do not align on equal signs that are last on a line. + if (&C != &Changes.back() && (&C + 1)->NewlinesBefore > 0) + return false; - return C.Tok->is(tok::equal); - }, - Changes, /*StartAt=*/0); + return C.Tok->is(tok::equal); + }, + Changes, /*StartAt=*/0); } void WhitespaceManager::alignConsecutiveDeclarations() { @@ -458,15 +583,28 @@ void WhitespaceManager::alignConsecutiveDeclarations() { // const char* const* v1; // float const* v2; // SomeVeryLongType const& v3; - AlignTokens(Style, - [](Change const &C) { - // tok::kw_operator is necessary for aligning operator overload - // definitions. - return C.Tok->is(TT_StartOfName) || - C.Tok->is(TT_FunctionDeclarationName) || - C.Tok->is(tok::kw_operator); - }, - Changes, /*StartAt=*/0); + AlignTokens( + Style, + [](Change const &C) { + // tok::kw_operator is necessary for aligning operator overload + // definitions. + if (C.Tok->isOneOf(TT_FunctionDeclarationName, tok::kw_operator)) + return true; + if (C.Tok->isNot(TT_StartOfName)) + return false; + // Check if there is a subsequent name that starts the same declaration. + for (FormatToken *Next = C.Tok->Next; Next; Next = Next->Next) { + if (Next->is(tok::comment)) + continue; + if (!Next->Tok.getIdentifierInfo()) + break; + if (Next->isOneOf(TT_StartOfName, TT_FunctionDeclarationName, + tok::kw_operator)) + return false; + } + return true; + }, + Changes, /*StartAt=*/0); } void WhitespaceManager::alignTrailingComments() { @@ -542,11 +680,10 @@ void WhitespaceManager::alignTrailingComments() { MinColumn = std::max(MinColumn, ChangeMinColumn); MaxColumn = std::min(MaxColumn, ChangeMaxColumn); } - BreakBeforeNext = - (i == 0) || (Changes[i].NewlinesBefore > 1) || - // Never start a sequence with a comment at the beginning of - // the line. - (Changes[i].NewlinesBefore == 1 && StartOfSequence == i); + BreakBeforeNext = (i == 0) || (Changes[i].NewlinesBefore > 1) || + // Never start a sequence with a comment at the beginning + // of the line. + (Changes[i].NewlinesBefore == 1 && StartOfSequence == i); Newlines = 0; } alignTrailingComments(StartOfSequence, Changes.size(), MinColumn); @@ -680,11 +817,15 @@ void WhitespaceManager::appendIndentText(std::string &Text, case FormatStyle::UT_Always: { unsigned FirstTabWidth = Style.TabWidth - WhitespaceStartColumn % Style.TabWidth; - // Indent with tabs only when there's at least one full tab. - if (FirstTabWidth + Style.TabWidth <= Spaces) { - Spaces -= FirstTabWidth; - Text.append("\t"); + // Insert only spaces when we want to end up before the next tab. + if (Spaces < FirstTabWidth || Spaces == 1) { + Text.append(Spaces, ' '); + break; } + // Align to the next tab. + Spaces -= FirstTabWidth; + Text.append("\t"); + Text.append(Spaces / Style.TabWidth, '\t'); Text.append(Spaces % Style.TabWidth, ' '); break; diff --git a/lib/Format/WhitespaceManager.h b/lib/Format/WhitespaceManager.h index db90343f7294..f47bf40204b3 100644 --- a/lib/Format/WhitespaceManager.h +++ b/lib/Format/WhitespaceManager.h @@ -1,9 +1,8 @@ //===--- WhitespaceManager.h - Format C++ code ------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -41,6 +40,8 @@ public: bool UseCRLF) : SourceMgr(SourceMgr), Style(Style), UseCRLF(UseCRLF) {} + bool useCRLF() const { return UseCRLF; } + /// Replaces the whitespace in front of \p Tok. Only call once for /// each \c AnnotatedToken. /// @@ -170,6 +171,9 @@ private: /// \c EscapedNewlineColumn for the first tokens or token parts in a line. void calculateLineBreakInformation(); + /// \brief Align consecutive C/C++ preprocessor macros over all \c Changes. + void alignConsecutiveMacros(); + /// Align consecutive assignments over all \c Changes. void alignConsecutiveAssignments(); |