diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:04:05 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:04:05 +0000 |
commit | 676fbe8105eeb6ff4bb2ed261cb212fcfdbe7b63 (patch) | |
tree | 02a1ac369cb734d0abfa5000dd86e5b7797e6a74 /lib/Format | |
parent | c7e70c433efc6953dc3888b9fbf9f3512d7da2b0 (diff) |
Diffstat (limited to 'lib/Format')
-rw-r--r-- | lib/Format/BreakableToken.cpp | 38 | ||||
-rw-r--r-- | lib/Format/ContinuationIndenter.cpp | 54 | ||||
-rw-r--r-- | lib/Format/Format.cpp | 216 | ||||
-rw-r--r-- | lib/Format/FormatToken.h | 29 | ||||
-rw-r--r-- | lib/Format/FormatTokenLexer.cpp | 11 | ||||
-rw-r--r-- | lib/Format/FormatTokenLexer.h | 4 | ||||
-rw-r--r-- | lib/Format/NamespaceEndCommentsFixer.cpp | 7 | ||||
-rw-r--r-- | lib/Format/TokenAnnotator.cpp | 70 | ||||
-rw-r--r-- | lib/Format/TokenAnnotator.h | 7 | ||||
-rw-r--r-- | lib/Format/UnwrappedLineFormatter.cpp | 23 | ||||
-rw-r--r-- | lib/Format/UnwrappedLineParser.cpp | 53 | ||||
-rw-r--r-- | lib/Format/UnwrappedLineParser.h | 1 | ||||
-rw-r--r-- | lib/Format/WhitespaceManager.cpp | 10 |
13 files changed, 441 insertions, 82 deletions
diff --git a/lib/Format/BreakableToken.cpp b/lib/Format/BreakableToken.cpp index fc2f891e0857e..e6ce01b520b5a 100644 --- a/lib/Format/BreakableToken.cpp +++ b/lib/Format/BreakableToken.cpp @@ -67,10 +67,11 @@ static BreakableToken::Split getCommentSplit(StringRef Text, unsigned ContentStartColumn, unsigned ColumnLimit, unsigned TabWidth, - encoding::Encoding Encoding) { - LLVM_DEBUG(llvm::dbgs() << "Comment split: \"" << Text << ", " << ColumnLimit - << "\", Content start: " << ContentStartColumn - << "\n"); + encoding::Encoding Encoding, + const FormatStyle &Style) { + LLVM_DEBUG(llvm::dbgs() << "Comment split: \"" << Text + << "\", Column limit: " << ColumnLimit + << ", Content start: " << ContentStartColumn << "\n"); if (ColumnLimit <= ContentStartColumn + 1) return BreakableToken::Split(StringRef::npos, 0); @@ -89,12 +90,21 @@ static BreakableToken::Split getCommentSplit(StringRef Text, StringRef::size_type SpaceOffset = Text.find_last_of(Blanks, MaxSplitBytes); - // Do not split before a number followed by a dot: this would be interpreted - // as a numbered list, which would prevent re-flowing in subsequent passes. static auto *const kNumberedListRegexp = new llvm::Regex("^[1-9][0-9]?\\."); - if (SpaceOffset != StringRef::npos && - kNumberedListRegexp->match(Text.substr(SpaceOffset).ltrim(Blanks))) - SpaceOffset = Text.find_last_of(Blanks, SpaceOffset); + while (SpaceOffset != StringRef::npos) { + // Do not split before a number followed by a dot: this would be interpreted + // as a numbered list, which would prevent re-flowing in subsequent passes. + if (kNumberedListRegexp->match(Text.substr(SpaceOffset).ltrim(Blanks))) + SpaceOffset = Text.find_last_of(Blanks, SpaceOffset); + // In JavaScript, some @tags can be followed by {, and machinery that parses + // these comments will fail to understand the comment if followed by a line + // break. So avoid ever breaking before a {. + else if (Style.Language == FormatStyle::LK_JavaScript && + SpaceOffset + 1 < Text.size() && Text[SpaceOffset + 1] == '{') + SpaceOffset = Text.find_last_of(Blanks, SpaceOffset); + else + break; + } if (SpaceOffset == StringRef::npos || // Don't break at leading whitespace. @@ -109,6 +119,12 @@ static BreakableToken::Split getCommentSplit(StringRef Text, Blanks, std::max<unsigned>(MaxSplitBytes, FirstNonWhitespace)); } if (SpaceOffset != StringRef::npos && SpaceOffset != 0) { + // adaptStartOfLine will break after lines starting with /** if the comment + // is broken anywhere. Avoid emitting this break twice here. + // Example: in /** longtextcomesherethatbreaks */ (with ColumnLimit 20) will + // insert a break after /**, so this code must not insert the same break. + 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); return BreakableToken::Split(BeforeCut.size(), @@ -260,7 +276,7 @@ BreakableComment::getSplit(unsigned LineIndex, unsigned TailOffset, return Split(StringRef::npos, 0); return getCommentSplit(Content[LineIndex].substr(TailOffset), ContentStartColumn, ColumnLimit, Style.TabWidth, - Encoding); + Encoding, Style); } void BreakableComment::compressWhitespace( @@ -620,6 +636,8 @@ void BreakableBlockComment::adaptStartOfLine( if (DelimitersOnNewline) { // Since we're breaking at index 1 below, the break position and the // break length are the same. + // Note: this works because getCommentSplit is careful never to split at + // the beginning of a line. size_t BreakLength = Lines[0].substr(1).find_first_not_of(Blanks); if (BreakLength != StringRef::npos) insertBreak(LineIndex, 0, Split(1, BreakLength), /*ContentIndent=*/0, diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index 7ca588a675b5a..c369b94b99878 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -403,7 +403,9 @@ bool ContinuationIndenter::mustBreak(const LineState &State) { // }.bind(...)); // FIXME: We should find a more generic solution to this problem. !(State.Column <= NewLineColumn && - Style.Language == FormatStyle::LK_JavaScript)) + Style.Language == FormatStyle::LK_JavaScript) && + !(Previous.closesScopeAfterBlock() && + State.Column <= NewLineColumn)) return true; // If the template declaration spans multiple lines, force wrap before the @@ -700,7 +702,8 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun, // Indent relative to the RHS of the expression unless this is a simple // assignment without binary expression on the RHS. Also indent relative to // unary operators and the colons of constructor initializers. - State.Stack.back().LastSpace = State.Column; + if (Style.BreakBeforeBinaryOperators == FormatStyle::BOS_None) + State.Stack.back().LastSpace = State.Column; } else if (Previous.is(TT_InheritanceColon)) { State.Stack.back().Indent = State.Column; State.Stack.back().LastSpace = State.Column; @@ -1132,7 +1135,8 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State, // }, a, b, c); if (Current.isNot(tok::comment) && Previous && Previous->isOneOf(tok::l_brace, TT_ArrayInitializerLSquare) && - !Previous->is(TT_DictLiteral) && State.Stack.size() > 1) { + !Previous->is(TT_DictLiteral) && State.Stack.size() > 1 && + !State.Stack.back().HasMultipleNestedBlocks) { if (State.Stack[State.Stack.size() - 2].NestedBlockInlined && Newline) for (unsigned i = 0, e = State.Stack.size() - 1; i != e; ++i) State.Stack[i].NoLineBreak = true; @@ -1499,10 +1503,25 @@ unsigned ContinuationIndenter::reformatRawStringLiteral( // violate the rectangle rule and visually flows within the surrounding // source. bool ContentStartsOnNewline = Current.TokenText[OldPrefixSize] == '\n'; - unsigned NextStartColumn = - ContentStartsOnNewline - ? State.Stack.back().NestedBlockIndent + Style.IndentWidth - : FirstStartColumn; + // 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: + // fffffffffff(1, 2, 3, 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 NextStartColumn = ContentStartsOnNewline + ? CurrentIndent + Style.IndentWidth + : FirstStartColumn; // The last start column is the column the raw string suffix starts if it is // put on a newline. @@ -1514,7 +1533,7 @@ unsigned ContinuationIndenter::reformatRawStringLiteral( // indent. unsigned LastStartColumn = Current.NewlinesBefore ? FirstStartColumn - NewPrefixSize - : State.Stack.back().NestedBlockIndent; + : CurrentIndent; std::pair<tooling::Replacements, unsigned> Fixes = internal::reformat( RawStringStyle, RawText, {tooling::Range(0, RawText.size())}, @@ -1524,8 +1543,7 @@ unsigned ContinuationIndenter::reformatRawStringLiteral( auto NewCode = applyAllReplacements(RawText, Fixes.first); tooling::Replacements NoFixes; if (!NewCode) { - State.Column += Current.ColumnWidth; - return 0; + return addMultilineToken(Current, State); } if (!DryRun) { if (NewDelimiter != OldDelimiter) { @@ -1574,6 +1592,13 @@ unsigned ContinuationIndenter::reformatRawStringLiteral( unsigned PrefixExcessCharacters = StartColumn + NewPrefixSize > Style.ColumnLimit ? StartColumn + NewPrefixSize - Style.ColumnLimit : 0; + bool IsMultiline = + ContentStartsOnNewline || (NewCode->find('\n') != std::string::npos); + if (IsMultiline) { + // Break before further function parameters on all levels. + for (unsigned i = 0, e = State.Stack.size(); i != e; ++i) + State.Stack[i].BreakBeforeParameter = true; + } return Fixes.second + PrefixExcessCharacters * Style.PenaltyExcessCharacter; } @@ -1840,7 +1865,8 @@ ContinuationIndenter::breakProtrudingToken(const FormatToken &Current, // No break opportunity - update the penalty and continue with the next // logical line. if (LineIndex < EndIndex - 1) - // The last line's penalty is handled in addNextStateToQueue(). + // The last line's penalty is handled in addNextStateToQueue() or when + // calling replaceWhitespaceAfterLastLine below. Penalty += Style.PenaltyExcessCharacter * (ContentStartColumn + RemainingTokenColumns - ColumnLimit); LLVM_DEBUG(llvm::dbgs() << " No break opportunity.\n"); @@ -2095,6 +2121,12 @@ ContinuationIndenter::breakProtrudingToken(const FormatToken &Current, Token->getSplitAfterLastLine(TailOffset); if (SplitAfterLastLine.first != StringRef::npos) { LLVM_DEBUG(llvm::dbgs() << "Replacing whitespace after last line.\n"); + + // We add the last line's penalty here, since that line is going to be split + // now. + Penalty += Style.PenaltyExcessCharacter * + (ContentStartColumn + RemainingTokenColumns - ColumnLimit); + if (!DryRun) Token->replaceWhitespaceAfterLastLine(TailOffset, SplitAfterLastLine, Whitespaces); diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index 9a2da69e89b1f..2c4f8760540a4 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -29,7 +29,6 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/SourceManager.h" -#include "clang/Basic/VirtualFileSystem.h" #include "clang/Lex/Lexer.h" #include "clang/Tooling/Inclusions/HeaderIncludes.h" #include "llvm/ADT/STLExtras.h" @@ -38,6 +37,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/Path.h" #include "llvm/Support/Regex.h" +#include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/YAMLTraits.h" #include <algorithm> #include <memory> @@ -414,6 +414,7 @@ template <> struct MappingTraits<FormatStyle> { IO.mapOptional("IndentWidth", Style.IndentWidth); IO.mapOptional("IndentWrappedFunctionNames", Style.IndentWrappedFunctionNames); + IO.mapOptional("JavaImportGroups", Style.JavaImportGroups); IO.mapOptional("JavaScriptQuotes", Style.JavaScriptQuotes); IO.mapOptional("JavaScriptWrapImports", Style.JavaScriptWrapImports); IO.mapOptional("KeepEmptyLinesAtTheStartOfBlocks", @@ -469,6 +470,7 @@ template <> struct MappingTraits<FormatStyle> { IO.mapOptional("SpacesInParentheses", Style.SpacesInParentheses); IO.mapOptional("SpacesInSquareBrackets", Style.SpacesInSquareBrackets); IO.mapOptional("Standard", Style.Standard); + IO.mapOptional("StatementMacros", Style.StatementMacros); IO.mapOptional("TabWidth", Style.TabWidth); IO.mapOptional("UseTab", Style.UseTab); } @@ -714,6 +716,8 @@ FormatStyle getLLVMStyle() { LLVMStyle.DisableFormat = false; LLVMStyle.SortIncludes = true; LLVMStyle.SortUsingDeclarations = true; + LLVMStyle.StatementMacros.push_back("Q_UNUSED"); + LLVMStyle.StatementMacros.push_back("QT_REQUIRE_VERSION"); return LLVMStyle; } @@ -819,7 +823,7 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) { GoogleStyle.JavaScriptQuotes = FormatStyle::JSQS_Single; GoogleStyle.JavaScriptWrapImports = false; } else if (Language == FormatStyle::LK_Proto) { - GoogleStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; + GoogleStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Empty; GoogleStyle.AlwaysBreakBeforeMultilineStrings = false; GoogleStyle.SpacesInContainerLiterals = false; GoogleStyle.Cpp11BracedListStyle = false; @@ -844,6 +848,20 @@ FormatStyle getChromiumStyle(FormatStyle::LanguageKind Language) { ChromiumStyle.BreakAfterJavaFieldAnnotations = true; ChromiumStyle.ContinuationIndentWidth = 8; ChromiumStyle.IndentWidth = 4; + // See styleguide for import groups: + // https://chromium.googlesource.com/chromium/src/+/master/styleguide/java/java.md#Import-Order + ChromiumStyle.JavaImportGroups = { + "android", + "com", + "dalvik", + "junit", + "org", + "com.google.android.apps.chrome", + "org.chromium", + "java", + "javax", + }; + ChromiumStyle.SortIncludes = true; } else if (Language == FormatStyle::LK_JavaScript) { ChromiumStyle.AllowShortIfStatementsOnASingleLine = false; ChromiumStyle.AllowShortLoopsOnASingleLine = false; @@ -1309,8 +1327,7 @@ private: std::set<unsigned> DeletedLines; for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) { auto &Line = *AnnotatedLines[i]; - if (Line.startsWith(tok::kw_namespace) || - Line.startsWith(tok::kw_inline, tok::kw_namespace)) { + if (Line.startsWithNamespace()) { checkEmptyNamespace(AnnotatedLines, i, i, DeletedLines); } } @@ -1347,9 +1364,7 @@ private: if (AnnotatedLines[CurrentLine]->startsWith(tok::r_brace)) break; - if (AnnotatedLines[CurrentLine]->startsWith(tok::kw_namespace) || - AnnotatedLines[CurrentLine]->startsWith(tok::kw_inline, - tok::kw_namespace)) { + if (AnnotatedLines[CurrentLine]->startsWithNamespace()) { if (!checkEmptyNamespace(AnnotatedLines, CurrentLine, NewLine, DeletedLines)) return false; @@ -1489,7 +1504,8 @@ public: SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, FormatTokenLexer &Tokens) override { assert(Style.Language == FormatStyle::LK_Cpp); - IsObjC = guessIsObjC(AnnotatedLines, Tokens.getKeywords()); + IsObjC = guessIsObjC(Env.getSourceManager(), AnnotatedLines, + Tokens.getKeywords()); tooling::Replacements Result; return {Result, 0}; } @@ -1497,8 +1513,10 @@ public: bool isObjC() { return IsObjC; } private: - static bool guessIsObjC(const SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, - const AdditionalKeywords &Keywords) { + static bool + guessIsObjC(const SourceManager &SourceManager, + const SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, + const AdditionalKeywords &Keywords) { // Keep this array sorted, since we are binary searching over it. static constexpr llvm::StringLiteral FoundationIdentifiers[] = { "CGFloat", @@ -1589,9 +1607,15 @@ private: TT_ObjCBlockLBrace, TT_ObjCBlockLParen, TT_ObjCDecl, TT_ObjCForIn, TT_ObjCMethodExpr, TT_ObjCMethodSpecifier, TT_ObjCProperty)) { + LLVM_DEBUG(llvm::dbgs() + << "Detected ObjC at location " + << FormatTok->Tok.getLocation().printToString( + SourceManager) + << " token: " << FormatTok->TokenText << " token type: " + << getTokenTypeName(FormatTok->Type) << "\n"); return true; } - if (guessIsObjC(Line->Children, Keywords)) + if (guessIsObjC(SourceManager, Line->Children, Keywords)) return true; } } @@ -1608,6 +1632,14 @@ struct IncludeDirective { int Category; }; +struct JavaImportDirective { + StringRef Identifier; + StringRef Text; + unsigned Offset; + std::vector<StringRef> AssociatedCommentLines; + bool IsStatic; +}; + } // end anonymous namespace // Determines whether 'Ranges' intersects with ('Start', 'End'). @@ -1726,7 +1758,7 @@ static void sortCppIncludes(const FormatStyle &Style, namespace { -const char IncludeRegexPattern[] = +const char CppIncludeRegexPattern[] = R"(^[\t\ ]*#[\t\ ]*(import|include)[^"<]*(["<][^">]*[">]))"; } // anonymous namespace @@ -1738,7 +1770,7 @@ tooling::Replacements sortCppIncludes(const FormatStyle &Style, StringRef Code, unsigned *Cursor) { unsigned Prev = 0; unsigned SearchFrom = 0; - llvm::Regex IncludeRegex(IncludeRegexPattern); + llvm::Regex IncludeRegex(CppIncludeRegexPattern); SmallVector<StringRef, 4> Matches; SmallVector<IncludeDirective, 16> IncludesInBlock; @@ -1797,6 +1829,149 @@ tooling::Replacements sortCppIncludes(const FormatStyle &Style, StringRef Code, return Replaces; } +// Returns group number to use as a first order sort on imports. Gives UINT_MAX +// if the import does not match any given groups. +static unsigned findJavaImportGroup(const FormatStyle &Style, + StringRef ImportIdentifier) { + unsigned LongestMatchIndex = UINT_MAX; + unsigned LongestMatchLength = 0; + for (unsigned I = 0; I < Style.JavaImportGroups.size(); I++) { + std::string GroupPrefix = Style.JavaImportGroups[I]; + if (ImportIdentifier.startswith(GroupPrefix) && + GroupPrefix.length() > LongestMatchLength) { + LongestMatchIndex = I; + LongestMatchLength = GroupPrefix.length(); + } + } + return LongestMatchIndex; +} + +// Sorts and deduplicates a block of includes given by 'Imports' based on +// JavaImportGroups, then adding the necessary replacement to 'Replaces'. +// Import declarations with the same text will be deduplicated. Between each +// import group, a newline is inserted, and within each import group, a +// lexicographic sort based on ASCII value is performed. +static void sortJavaImports(const FormatStyle &Style, + const SmallVectorImpl<JavaImportDirective> &Imports, + ArrayRef<tooling::Range> Ranges, StringRef FileName, + tooling::Replacements &Replaces) { + unsigned ImportsBeginOffset = Imports.front().Offset; + unsigned ImportsEndOffset = + Imports.back().Offset + Imports.back().Text.size(); + unsigned ImportsBlockSize = ImportsEndOffset - ImportsBeginOffset; + if (!affectsRange(Ranges, ImportsBeginOffset, ImportsEndOffset)) + return; + SmallVector<unsigned, 16> Indices; + SmallVector<unsigned, 16> JavaImportGroups; + for (unsigned i = 0, e = Imports.size(); i != e; ++i) { + Indices.push_back(i); + 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); + }); + + // Deduplicate imports. + Indices.erase(std::unique(Indices.begin(), Indices.end(), + [&](unsigned LHSI, unsigned RHSI) { + return Imports[LHSI].Text == Imports[RHSI].Text; + }), + Indices.end()); + + bool CurrentIsStatic = Imports[Indices.front()].IsStatic; + unsigned CurrentImportGroup = JavaImportGroups[Indices.front()]; + + std::string result; + for (unsigned Index : Indices) { + if (!result.empty()) { + result += "\n"; + if (CurrentIsStatic != Imports[Index].IsStatic || + CurrentImportGroup != JavaImportGroups[Index]) + result += "\n"; + } + for (StringRef CommentLine : Imports[Index].AssociatedCommentLines) { + result += CommentLine; + result += "\n"; + } + result += Imports[Index].Text; + CurrentIsStatic = Imports[Index].IsStatic; + CurrentImportGroup = JavaImportGroups[Index]; + } + + auto Err = Replaces.add(tooling::Replacement(FileName, Imports.front().Offset, + ImportsBlockSize, result)); + // FIXME: better error handling. For now, just skip the replacement for the + // release version. + if (Err) { + llvm::errs() << llvm::toString(std::move(Err)) << "\n"; + assert(false); + } +} + +namespace { + +const char JavaImportRegexPattern[] = + "^[\t ]*import[\t ]*(static[\t ]*)?([^\t ]*)[\t ]*;"; + +} // anonymous namespace + +tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code, + ArrayRef<tooling::Range> Ranges, + StringRef FileName, + tooling::Replacements &Replaces) { + unsigned Prev = 0; + unsigned SearchFrom = 0; + llvm::Regex ImportRegex(JavaImportRegexPattern); + SmallVector<StringRef, 4> Matches; + SmallVector<JavaImportDirective, 16> ImportsInBlock; + std::vector<StringRef> AssociatedCommentLines; + + bool FormattingOff = false; + + for (;;) { + auto Pos = Code.find('\n', SearchFrom); + StringRef Line = + Code.substr(Prev, (Pos != StringRef::npos ? Pos : Code.size()) - Prev); + + StringRef Trimmed = Line.trim(); + if (Trimmed == "// clang-format off") + FormattingOff = true; + else if (Trimmed == "// clang-format on") + FormattingOff = false; + + if (ImportRegex.match(Line, &Matches)) { + if (FormattingOff) { + // If at least one import line has formatting turned off, turn off + // formatting entirely. + return Replaces; + } + StringRef Static = Matches[1]; + StringRef Identifier = Matches[2]; + bool IsStatic = false; + if (Static.contains("static")) { + IsStatic = true; + } + 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 + AssociatedCommentLines.push_back(Line); + } + Prev = Pos + 1; + if (Pos == StringRef::npos || Pos + 1 == Code.size()) + break; + SearchFrom = Pos + 1; + } + if (!ImportsInBlock.empty()) + sortJavaImports(Style, ImportsInBlock, Ranges, FileName, Replaces); + return Replaces; +} + bool isMpegTS(StringRef Code) { // MPEG transport streams use the ".ts" file extension. clang-format should // not attempt to format those. MPEG TS' frame format starts with 0x47 every @@ -1819,6 +1994,8 @@ tooling::Replacements sortIncludes(const FormatStyle &Style, StringRef Code, return Replaces; if (Style.Language == FormatStyle::LanguageKind::LK_JavaScript) return sortJavaScriptImports(Style, Code, Ranges, FileName); + if (Style.Language == FormatStyle::LanguageKind::LK_Java) + return sortJavaImports(Style, Code, Ranges, FileName, Replaces); sortCppIncludes(Style, Code, Ranges, FileName, Replaces, Cursor); return Replaces; } @@ -1872,7 +2049,8 @@ namespace { inline bool isHeaderInsertion(const tooling::Replacement &Replace) { return Replace.getOffset() == UINT_MAX && Replace.getLength() == 0 && - llvm::Regex(IncludeRegexPattern).match(Replace.getReplacementText()); + llvm::Regex(CppIncludeRegexPattern) + .match(Replace.getReplacementText()); } inline bool isHeaderDeletion(const tooling::Replacement &Replace) { @@ -1925,7 +2103,7 @@ fixCppIncludeInsertions(StringRef Code, const tooling::Replacements &Replaces, } } - llvm::Regex IncludeRegex = llvm::Regex(IncludeRegexPattern); + llvm::Regex IncludeRegex = llvm::Regex(CppIncludeRegexPattern); llvm::SmallVector<StringRef, 4> Matches; for (const auto &R : HeaderInsertions) { auto IncludeDirective = R.getReplacementText(); @@ -2095,8 +2273,7 @@ LangOptions getFormattingLangOpts(const FormatStyle &Style) { bool AlternativeOperators = Style.isCpp(); LangOpts.CXXOperatorNames = AlternativeOperators ? 1 : 0; LangOpts.Bool = 1; - LangOpts.ObjC1 = 1; - LangOpts.ObjC2 = 1; + LangOpts.ObjC = 1; LangOpts.MicrosoftExt = 1; // To get kw___try, kw___finally. LangOpts.DeclSpecKeyword = 1; // To get __declspec. return LangOpts; @@ -2157,9 +2334,10 @@ const char *DefaultFallbackStyle = "LLVM"; llvm::Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName, StringRef FallbackStyleName, - StringRef Code, vfs::FileSystem *FS) { + StringRef Code, + llvm::vfs::FileSystem *FS) { if (!FS) { - FS = vfs::getRealFileSystem().get(); + FS = llvm::vfs::getRealFileSystem().get(); } FormatStyle Style = getLLVMStyle(); Style.Language = guessLanguage(FileName, Code); diff --git a/lib/Format/FormatToken.h b/lib/Format/FormatToken.h index 9094e7689e1de..10390c42911b8 100644 --- a/lib/Format/FormatToken.h +++ b/lib/Format/FormatToken.h @@ -86,6 +86,7 @@ namespace format { TYPE(RegexLiteral) \ TYPE(SelectorName) \ TYPE(StartOfName) \ + TYPE(StatementMacro) \ TYPE(StructuredBindingLSquare) \ TYPE(TemplateCloser) \ TYPE(TemplateOpener) \ @@ -188,10 +189,6 @@ struct FormatToken { bool ClosesTemplateDeclaration = false; /// Number of parameters, if this is "(", "[" or "<". - /// - /// This is initialized to 1 as we don't need to distinguish functions with - /// 0 parameters from functions with 1 parameter. Thus, we can simply count - /// the number of commas. unsigned ParameterCount = 0; /// Number of parameters that are nested blocks, @@ -268,7 +265,7 @@ struct FormatToken { /// \c true if this token ends a binary expression. bool EndsBinaryExpression = false; - /// Is this is an operator (or "."/"->") in a sequence of operators + /// If this is an operator (or "."/"->") in a sequence of operators /// with the same precedence, contains the 0-based operator index. unsigned OperatorIndex = 0; @@ -325,6 +322,14 @@ struct FormatToken { } template <typename T> bool isNot(T Kind) const { return !is(Kind); } + bool closesScopeAfterBlock() const { + if (BlockKind == BK_Block) + return true; + if (closesScope()) + return Previous->closesScopeAfterBlock(); + return false; + } + /// \c true if this token starts a sequence with the given tokens in order, /// following the ``Next`` pointers, ignoring comments. template <typename A, typename... Ts> @@ -520,8 +525,8 @@ struct FormatToken { const FormatToken *NamespaceTok = this; if (is(tok::comment)) NamespaceTok = NamespaceTok->getNextNonComment(); - // Detect "(inline)? namespace" in the beginning of a line. - if (NamespaceTok && NamespaceTok->is(tok::kw_inline)) + // 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; @@ -594,6 +599,8 @@ public: /// Notifies the \c Role that a comma was found. virtual void CommaFound(const FormatToken *Token) {} + virtual const FormatToken *lastComma() { return nullptr; } + protected: const FormatStyle &Style; }; @@ -616,6 +623,12 @@ public: Commas.push_back(Token); } + const FormatToken *lastComma() override { + if (Commas.empty()) + return nullptr; + return Commas.back(); + } + private: /// A struct that holds information on how to format a given list with /// a specific number of columns. @@ -672,6 +685,7 @@ struct AdditionalKeywords { kw_function = &IdentTable.get("function"); kw_get = &IdentTable.get("get"); kw_import = &IdentTable.get("import"); + kw_infer = &IdentTable.get("infer"); kw_is = &IdentTable.get("is"); kw_let = &IdentTable.get("let"); kw_module = &IdentTable.get("module"); @@ -743,6 +757,7 @@ struct AdditionalKeywords { IdentifierInfo *kw_function; IdentifierInfo *kw_get; IdentifierInfo *kw_import; + IdentifierInfo *kw_infer; IdentifierInfo *kw_is; IdentifierInfo *kw_let; IdentifierInfo *kw_module; diff --git a/lib/Format/FormatTokenLexer.cpp b/lib/Format/FormatTokenLexer.cpp index c7f720a443d35..146f5d68b559f 100644 --- a/lib/Format/FormatTokenLexer.cpp +++ b/lib/Format/FormatTokenLexer.cpp @@ -37,8 +37,9 @@ FormatTokenLexer::FormatTokenLexer(const SourceManager &SourceMgr, FileID ID, Lex->SetKeepWhitespaceMode(true); for (const std::string &ForEachMacro : Style.ForEachMacros) - ForEachMacros.push_back(&IdentTable.get(ForEachMacro)); - llvm::sort(ForEachMacros.begin(), ForEachMacros.end()); + Macros.insert({&IdentTable.get(ForEachMacro), TT_ForEachMacro}); + for (const std::string &StatementMacro : Style.StatementMacros) + Macros.insert({&IdentTable.get(StatementMacro), TT_StatementMacro}); } ArrayRef<FormatToken *> FormatTokenLexer::lex() { @@ -657,12 +658,12 @@ FormatToken *FormatTokenLexer::getNextToken() { } if (Style.isCpp()) { + auto it = Macros.find(FormatTok->Tok.getIdentifierInfo()); if (!(Tokens.size() > 0 && Tokens.back()->Tok.getIdentifierInfo() && Tokens.back()->Tok.getIdentifierInfo()->getPPKeywordID() == tok::pp_define) && - std::find(ForEachMacros.begin(), ForEachMacros.end(), - FormatTok->Tok.getIdentifierInfo()) != ForEachMacros.end()) { - FormatTok->Type = TT_ForEachMacro; + it != Macros.end()) { + FormatTok->Type = it->second; } else if (FormatTok->is(tok::identifier)) { if (MacroBlockBeginRegex.match(Text)) { FormatTok->Type = TT_MacroBlockBegin; diff --git a/lib/Format/FormatTokenLexer.h b/lib/Format/FormatTokenLexer.h index 3b79d27480e34..0cf357c85f3b1 100644 --- a/lib/Format/FormatTokenLexer.h +++ b/lib/Format/FormatTokenLexer.h @@ -22,6 +22,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Format/Format.h" #include "llvm/Support/Regex.h" +#include "llvm/ADT/MapVector.h" #include <stack> @@ -99,7 +100,8 @@ private: // Index (in 'Tokens') of the last token that starts a new line. unsigned FirstInLineIndex; SmallVector<FormatToken *, 16> Tokens; - SmallVector<IdentifierInfo *, 8> ForEachMacros; + + llvm::SmallMapVector<IdentifierInfo *, TokenType, 8> Macros; bool FormattingDisabled; diff --git a/lib/Format/NamespaceEndCommentsFixer.cpp b/lib/Format/NamespaceEndCommentsFixer.cpp index 995b3219a1f4a..dd364866d1ceb 100644 --- a/lib/Format/NamespaceEndCommentsFixer.cpp +++ b/lib/Format/NamespaceEndCommentsFixer.cpp @@ -125,12 +125,7 @@ getNamespaceToken(const AnnotatedLine *Line, if (StartLineIndex > 0) NamespaceTok = AnnotatedLines[StartLineIndex - 1]->First; } - // Detect "(inline)? namespace" in the beginning of a line. - if (NamespaceTok->is(tok::kw_inline)) - NamespaceTok = NamespaceTok->getNextNonComment(); - if (!NamespaceTok || NamespaceTok->isNot(tok::kw_namespace)) - return nullptr; - return NamespaceTok; + return NamespaceTok->getNamespaceToken(); } NamespaceEndCommentsFixer::NamespaceEndCommentsFixer(const Environment &Env, diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index 3a19215e18037..24c2f998c388a 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -366,7 +366,8 @@ private: // specifier parameter, although this is technically valid: // [[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)) return true; @@ -398,9 +399,11 @@ private: bool IsCpp11AttributeSpecifier = isCpp11AttributeSpecifier(*Left) || Contexts.back().InCpp11AttributeSpecifier; + bool InsideInlineASM = Line.startsWith(tok::kw_asm); bool StartsObjCMethodExpr = - !CppArrayTemplates && Style.isCpp() && !IsCpp11AttributeSpecifier && - Contexts.back().CanBeExpression && Left->isNot(TT_LambdaLSquare) && + !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, @@ -1120,6 +1123,7 @@ private: (Tok.Next->Next->TokenText == "module" || Tok.Next->Next->TokenText == "provide" || Tok.Next->Next->TokenText == "require" || + Tok.Next->Next->TokenText == "requireType" || Tok.Next->Next->TokenText == "forwardDeclare") && Tok.Next->Next->Next && Tok.Next->Next->Next->is(tok::l_paren); } @@ -2517,7 +2521,9 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, Right.MatchingParen->BlockKind != BK_Block)) return !Style.Cpp11BracedListStyle; if (Left.is(TT_BlockComment)) - return !Left.TokenText.endswith("=*/"); + // No whitespace in x(/*foo=*/1), except for JavaScript. + return Style.Language == FormatStyle::LK_JavaScript || + !Left.TokenText.endswith("=*/"); if (Right.is(tok::l_paren)) { if ((Left.is(tok::r_paren) && Left.is(TT_AttributeParen)) || (Left.is(tok::r_square) && Left.is(TT_AttributeSquare))) @@ -2553,8 +2559,11 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, return false; if (Left.is(TT_TemplateCloser) && Left.MatchingParen && Left.MatchingParen->Previous && - Left.MatchingParen->Previous->is(tok::period)) + (Left.MatchingParen->Previous->is(tok::period) || + Left.MatchingParen->Previous->is(tok::coloncolon))) + // Java call to generic function with explicit type: // A.<B<C<...>>>DoSomething(); + // A::<B<C<...>>>DoSomething(); // With a Java 8 method reference. return false; if (Left.is(TT_TemplateCloser) && Right.is(tok::l_square)) return false; @@ -2774,6 +2783,9 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, if (!Style.SpaceBeforeAssignmentOperators && Right.getPrecedence() == prec::Assignment) return false; + if (Style.Language == FormatStyle::LK_Java && Right.is(tok::coloncolon) && + (Left.is(tok::identifier) || Left.is(tok::kw_this))) + return false; if (Right.is(tok::coloncolon) && Left.is(tok::identifier)) // Generally don't remove existing spaces between an identifier and "::". // The identifier might actually be a macro name such as ALWAYS_INLINE. If @@ -2866,6 +2878,7 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, } else if (Style.Language == FormatStyle::LK_Cpp || Style.Language == FormatStyle::LK_ObjC || Style.Language == FormatStyle::LK_Proto || + Style.Language == FormatStyle::LK_TableGen || Style.Language == FormatStyle::LK_TextProto) { if (Left.isStringLiteral() && Right.isStringLiteral()) return true; @@ -3041,6 +3054,30 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, return true; } + // Deal with lambda arguments in C++ - we want consistent line breaks whether + // they happen to be at arg0, arg1 or argN. The selection is a bit nuanced + // as aggressive line breaks are placed when the lambda is not the last arg. + if ((Style.Language == FormatStyle::LK_Cpp || + Style.Language == FormatStyle::LK_ObjC) && + Left.is(tok::l_paren) && Left.BlockParameterCount > 0 && + !Right.isOneOf(tok::l_paren, TT_LambdaLSquare)) { + // Multiple lambdas in the same function call force line breaks. + if (Left.BlockParameterCount > 1) + return true; + + // A lambda followed by another arg forces a line break. + if (!Left.Role) + return false; + auto Comma = Left.Role->lastComma(); + if (!Comma) + return false; + auto Next = Comma->getNextNonComment(); + if (!Next) + return false; + if (!Next->isOneOf(TT_LambdaLSquare, tok::l_brace, tok::caret)) + return true; + } + return false; } @@ -3078,14 +3115,33 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line, // Don't wrap between ":" and "!" of a strict prop init ("field!: type;"). if (Left.is(tok::exclaim) && Right.is(tok::colon)) return false; - if (Right.is(Keywords.kw_is)) - return false; + // Look for is type annotations like: + // function f(): a is B { ... } + // Do not break before is in these cases. + if (Right.is(Keywords.kw_is)) { + 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: + // Polymer({ + // is: 'name', + // ... + // }); + if (!Next || !Next->is(tok::colon)) + return false; + } if (Left.is(Keywords.kw_in)) return Style.BreakBeforeBinaryOperators == FormatStyle::BOS_None; if (Right.is(Keywords.kw_in)) return Style.BreakBeforeBinaryOperators != FormatStyle::BOS_None; if (Right.is(Keywords.kw_as)) return false; // must not break before as in 'x as type' casts + if (Right.isOneOf(Keywords.kw_extends, Keywords.kw_infer)) { + // extends and infer can appear as keywords in conditional types: + // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#conditional-types + // do not break before them, as the expressions are subject to ASI. + return false; + } if (Left.is(Keywords.kw_as)) return true; if (Left.is(TT_JsNonNullAssertion)) diff --git a/lib/Format/TokenAnnotator.h b/lib/Format/TokenAnnotator.h index a3124fcb3d65b..e2f2c469d2675 100644 --- a/lib/Format/TokenAnnotator.h +++ b/lib/Format/TokenAnnotator.h @@ -105,6 +105,13 @@ public: return !Last->isOneOf(tok::semi, tok::comment); } + /// \c true if this line starts a namespace definition. + bool startsWithNamespace() const { + return startsWith(tok::kw_namespace) || + startsWith(tok::kw_inline, tok::kw_namespace) || + startsWith(tok::kw_export, tok::kw_namespace); + } + FormatToken *First; FormatToken *Last; diff --git a/lib/Format/UnwrappedLineFormatter.cpp b/lib/Format/UnwrappedLineFormatter.cpp index 906dae40cbee7..6b6a9aff461ab 100644 --- a/lib/Format/UnwrappedLineFormatter.cpp +++ b/lib/Format/UnwrappedLineFormatter.cpp @@ -323,6 +323,10 @@ private: kwId == clang::tok::objc_synchronized) return 0; } + // Don't merge block with left brace wrapped after case labels + if (TheLine->First->is(tok::l_brace) && I != AnnotatedLines.begin() && + I[-1]->First->isOneOf(tok::kw_case, tok::kw_default)) + return 0; // Try to merge a block with left brace wrapped that wasn't yet covered if (TheLine->Last->is(tok::l_brace)) { return !Style.BraceWrapping.AfterFunction || @@ -424,6 +428,8 @@ private: if (Limit == 0 || I + 1 == E || I[1]->First->isOneOf(tok::kw_case, tok::kw_default)) return 0; + if (I[0]->Last->is(tok::l_brace) || I[1]->First->is(tok::l_brace)) + return 0; unsigned NumStmts = 0; unsigned Length = 0; bool EndsWithComment = false; @@ -483,6 +489,12 @@ private: if (Line.First->isOneOf(tok::kw_else, tok::kw_case) || (Line.First->Next && Line.First->Next->is(tok::kw_else))) return 0; + // default: in switch statement + if (Line.First->is(tok::kw_default)) { + const FormatToken *Tok = Line.First->getNextNonComment(); + if (Tok && Tok->is(tok::colon)) + return 0; + } if (Line.First->isOneOf(tok::kw_if, tok::kw_while, tok::kw_do, tok::kw_try, tok::kw___try, tok::kw_catch, tok::kw___finally, tok::kw_for, tok::r_brace, Keywords.kw___except)) { @@ -529,7 +541,7 @@ private: Tok->SpacesRequiredBefore = 0; Tok->CanBreakBefore = true; return 1; - } else if (Limit != 0 && !Line.startsWith(tok::kw_namespace) && + } else if (Limit != 0 && !Line.startsWithNamespace() && !startsExternCBlock(Line)) { // We don't merge short records. FormatToken *RecordTok = Line.First; @@ -976,8 +988,7 @@ private: Path.push_front(Best); Best = Best->Previous; } - for (std::deque<StateNode *>::iterator I = Path.begin(), E = Path.end(); - I != E; ++I) { + for (auto I = Path.begin(), E = Path.end(); I != E; ++I) { unsigned Penalty = 0; formatChildren(State, (*I)->NewLine, /*DryRun=*/false, Penalty); Penalty += Indenter->addTokenToState(State, (*I)->NewLine, false); @@ -986,8 +997,8 @@ private: printLineState((*I)->Previous->State); if ((*I)->NewLine) { llvm::dbgs() << "Penalty for placing " - << (*I)->Previous->State.NextToken->Tok.getName() << ": " - << Penalty << "\n"; + << (*I)->Previous->State.NextToken->Tok.getName() + << " on a new line: " << Penalty << "\n"; } }); } @@ -1154,7 +1165,7 @@ void UnwrappedLineFormatter::formatFirstToken( // Remove empty lines after "{". if (!Style.KeepEmptyLinesAtTheStartOfBlocks && PreviousLine && PreviousLine->Last->is(tok::l_brace) && - PreviousLine->First->isNot(tok::kw_namespace) && + !PreviousLine->startsWithNamespace() && !startsExternCBlock(*PreviousLine)) Newlines = 1; diff --git a/lib/Format/UnwrappedLineParser.cpp b/lib/Format/UnwrappedLineParser.cpp index e5afa1264abb5..3cd3c8f9cdf69 100644 --- a/lib/Format/UnwrappedLineParser.cpp +++ b/lib/Format/UnwrappedLineParser.cpp @@ -350,7 +350,10 @@ void UnwrappedLineParser::parseLevel(bool HasOpeningBrace) { break; case tok::kw_default: { unsigned StoredPosition = Tokens->getPosition(); - FormatToken *Next = Tokens->getNextToken(); + FormatToken *Next; + do { + Next = Tokens->getNextToken(); + } while (Next && Next->is(tok::comment)); FormatTok = Tokens->setPosition(StoredPosition); if (Next && Next->isNot(tok::colon)) { // default not followed by ':' is not a case label; treat it like @@ -477,6 +480,10 @@ void UnwrappedLineParser::calculateBraceTypes(bool ExpectClassBody) { } LBraceStack.pop_back(); break; + case tok::identifier: + if (!Tok->is(TT_StatementMacro)) + break; + LLVM_FALLTHROUGH; case tok::at: case tok::semi: case tok::kw_if: @@ -989,13 +996,6 @@ void UnwrappedLineParser::parseStructuralElement() { case tok::kw_namespace: parseNamespace(); return; - case tok::kw_inline: - nextToken(); - if (FormatTok->Tok.is(tok::kw_namespace)) { - parseNamespace(); - return; - } - break; case tok::kw_public: case tok::kw_protected: case tok::kw_private: @@ -1063,6 +1063,16 @@ void UnwrappedLineParser::parseStructuralElement() { parseJavaScriptEs6ImportExport(); return; } + if (!Style.isCpp()) + break; + // Handle C++ "(inline|export) namespace". + LLVM_FALLTHROUGH; + case tok::kw_inline: + nextToken(); + if (FormatTok->Tok.is(tok::kw_namespace)) { + parseNamespace(); + return; + } break; case tok::identifier: if (FormatTok->is(TT_ForEachMacro)) { @@ -1102,6 +1112,10 @@ void UnwrappedLineParser::parseStructuralElement() { return; } } + if (Style.isCpp() && FormatTok->is(TT_StatementMacro)) { + parseStatementMacro(); + return; + } // In all other cases, parse the declaration. break; default: @@ -1116,6 +1130,10 @@ void UnwrappedLineParser::parseStructuralElement() { nextToken(); parseBracedList(); break; + } else if (Style.Language == FormatStyle::LK_Java && + FormatTok->is(Keywords.kw_interface)) { + nextToken(); + break; } switch (FormatTok->Tok.getObjCKeywordID()) { case tok::objc_public: @@ -1260,6 +1278,8 @@ void UnwrappedLineParser::parseStructuralElement() { break; case tok::kw_try: // We arrive here when parsing function-try blocks. + if (Style.BraceWrapping.AfterFunction) + addUnwrappedLine(); parseTryCatch(); return; case tok::identifier: { @@ -1301,6 +1321,11 @@ void UnwrappedLineParser::parseStructuralElement() { return; } + if (Style.isCpp() && FormatTok->is(TT_StatementMacro)) { + parseStatementMacro(); + return; + } + // See if the following token should start a new unwrapped line. StringRef Text = FormatTok->TokenText; nextToken(); @@ -2143,6 +2168,8 @@ void UnwrappedLineParser::parseObjCMethod() { addUnwrappedLine(); return; } else if (FormatTok->Tok.is(tok::l_brace)) { + if (Style.BraceWrapping.AfterFunction) + addUnwrappedLine(); parseBlock(/*MustBeDeclaration=*/false); addUnwrappedLine(); return; @@ -2320,6 +2347,16 @@ void UnwrappedLineParser::parseJavaScriptEs6ImportExport() { } } +void UnwrappedLineParser::parseStatementMacro() +{ + nextToken(); + if (FormatTok->is(tok::l_paren)) + parseParens(); + if (FormatTok->is(tok::semi)) + nextToken(); + addUnwrappedLine(); +} + LLVM_ATTRIBUTE_UNUSED static void printDebugInfo(const UnwrappedLine &Line, StringRef Prefix = "") { llvm::dbgs() << Prefix << "Line(" << Line.Level diff --git a/lib/Format/UnwrappedLineParser.h b/lib/Format/UnwrappedLineParser.h index 87254832c635e..55d60dff91523 100644 --- a/lib/Format/UnwrappedLineParser.h +++ b/lib/Format/UnwrappedLineParser.h @@ -126,6 +126,7 @@ private: void parseObjCInterfaceOrImplementation(); bool parseObjCProtocol(); void parseJavaScriptEs6ImportExport(); + void parseStatementMacro(); bool tryToParseLambda(); bool tryToParseLambdaIntroducer(); void tryToParseJSFunction(); diff --git a/lib/Format/WhitespaceManager.cpp b/lib/Format/WhitespaceManager.cpp index 7070ce03c864e..032b1333322d1 100644 --- a/lib/Format/WhitespaceManager.cpp +++ b/lib/Format/WhitespaceManager.cpp @@ -90,7 +90,7 @@ const tooling::Replacements &WhitespaceManager::generateReplacements() { if (Changes.empty()) return Replaces; - llvm::sort(Changes.begin(), Changes.end(), Change::IsBeforeInFile(SourceMgr)); + llvm::sort(Changes, Change::IsBeforeInFile(SourceMgr)); calculateLineBreakInformation(); alignConsecutiveDeclarations(); alignConsecutiveAssignments(); @@ -255,8 +255,14 @@ AlignTokenSequence(unsigned Start, unsigned End, unsigned Column, F &&Matches, Changes[ScopeStack.back()].indentAndNestingLevel()) ScopeStack.pop_back(); + // Compare current token to previous non-comment token to ensure whether + // it is in a deeper scope or not. + unsigned PreviousNonComment = i - 1; + while (PreviousNonComment > Start && + Changes[PreviousNonComment].Tok->is(tok::comment)) + PreviousNonComment--; if (i != Start && Changes[i].indentAndNestingLevel() > - Changes[i - 1].indentAndNestingLevel()) + Changes[PreviousNonComment].indentAndNestingLevel()) ScopeStack.push_back(i); bool InsideNestedScope = ScopeStack.size() != 0; |