diff options
Diffstat (limited to 'lib/Format/Format.cpp')
-rw-r--r-- | lib/Format/Format.cpp | 216 |
1 files changed, 197 insertions, 19 deletions
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); |