Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -1313,6 +1313,7 @@ struct IncludeDirective { StringRef Filename; + bool IsNotStatic; // False for Java's `import static`. StringRef Text; unsigned Offset; int Category; @@ -1366,10 +1367,11 @@ // first #include in the duplicate #includes remains. If the `Cursor` is // provided and put on a deleted #include, it will be moved to the remaining // #include in the duplicate #includes. -static void sortCppIncludes(const FormatStyle &Style, - const SmallVectorImpl &Includes, - ArrayRef Ranges, StringRef FileName, - tooling::Replacements &Replaces, unsigned *Cursor) { +static void +sortCppJavaIncludes(const FormatStyle &Style, + const SmallVectorImpl &Includes, + ArrayRef Ranges, StringRef FileName, + tooling::Replacements &Replaces, unsigned *Cursor) { unsigned IncludesBeginOffset = Includes.front().Offset; unsigned IncludesEndOffset = Includes.back().Offset + Includes.back().Text.size(); @@ -1381,8 +1383,10 @@ 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); + return std::tie(Includes[LHSI].IsNotStatic, Includes[LHSI].Category, + Includes[LHSI].Filename) < + std::tie(Includes[RHSI].IsNotStatic, Includes[RHSI].Category, + Includes[RHSI].Filename); }); // The index of the include on which the cursor will be put after // sorting/deduplicating. @@ -1481,19 +1485,23 @@ SmallVector CategoryRegexs; }; -const char IncludeRegexPattern[] = +// FIXME: PR34739 +const char CppIncludeRegexPattern[] = R"(^[\t\ ]*#[\t\ ]*(import|include)[^"<]*(["<][^">]*[">]))"; +const char JavaIncludeRegexPattern[] = + R"(^ *import *(static *)?([^ ]*) *;)"; } // anonymous namespace -tooling::Replacements sortCppIncludes(const FormatStyle &Style, StringRef Code, - ArrayRef Ranges, - StringRef FileName, - tooling::Replacements &Replaces, - unsigned *Cursor) { +tooling::Replacements +sortCppJavaIncludes(const FormatStyle &Style, StringRef Code, + ArrayRef Ranges, StringRef FileName, + tooling::Replacements &Replaces, unsigned *Cursor) { unsigned Prev = 0; unsigned SearchFrom = 0; - llvm::Regex IncludeRegex(IncludeRegexPattern); + llvm::Regex IncludeRegex(Style.Language == FormatStyle::LK_Java + ? JavaIncludeRegexPattern + : CppIncludeRegexPattern); SmallVector Matches; SmallVector IncludesInBlock; @@ -1522,16 +1530,20 @@ if (!FormattingOff && !Line.endswith("\\")) { if (IncludeRegex.match(Line, &Matches)) { + // FIXME: import static for java, in Matches[1] if present StringRef IncludeName = Matches[2]; + bool IsStatic = + Style.Language == FormatStyle::LK_Java && !Matches[1].empty(); int Category = Categories.getIncludePriority( IncludeName, /*CheckMainHeader=*/!MainIncludeFound && FirstIncludeBlock); if (Category == 0) MainIncludeFound = true; - IncludesInBlock.push_back({IncludeName, Line, Prev, Category}); + IncludesInBlock.push_back( + {IncludeName, !IsStatic, Line, Prev, Category}); } else if (!IncludesInBlock.empty()) { - sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Replaces, - Cursor); + sortCppJavaIncludes(Style, IncludesInBlock, Ranges, FileName, Replaces, + Cursor); IncludesInBlock.clear(); FirstIncludeBlock = false; } @@ -1542,7 +1554,8 @@ SearchFrom = Pos + 1; } if (!IncludesInBlock.empty()) - sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Replaces, Cursor); + sortCppJavaIncludes(Style, IncludesInBlock, Ranges, FileName, Replaces, + Cursor); return Replaces; } @@ -1568,7 +1581,7 @@ return Replaces; if (Style.Language == FormatStyle::LanguageKind::LK_JavaScript) return sortJavaScriptImports(Style, Code, Ranges, FileName); - sortCppIncludes(Style, Code, Ranges, FileName, Replaces, Cursor); + sortCppJavaIncludes(Style, Code, Ranges, FileName, Replaces, Cursor); return Replaces; } @@ -1619,9 +1632,10 @@ namespace { -inline bool isHeaderInsertion(const tooling::Replacement &Replace) { +inline bool isCppHeaderInsertion(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) { @@ -1755,7 +1769,7 @@ std::set HeadersToDelete; tooling::Replacements Result; for (const auto &R : Replaces) { - if (isHeaderInsertion(R)) { + if (isCppHeaderInsertion(R)) { // Replacements from \p Replaces must be conflict-free already, so we can // simply consume the error. llvm::consumeError(HeaderInsertions.add(R)); @@ -1772,7 +1786,7 @@ if (HeaderInsertions.empty() && HeadersToDelete.empty()) return Replaces; - llvm::Regex IncludeRegex(IncludeRegexPattern); + llvm::Regex IncludeRegex(CppIncludeRegexPattern); llvm::Regex DefineRegex(R"(^[\t\ ]*#[\t\ ]*define[\t\ ]*[^\\]*$)"); SmallVector Matches;