Index: docs/ClangFormatStyleOptions.rst =================================================================== --- docs/ClangFormatStyleOptions.rst +++ docs/ClangFormatStyleOptions.rst @@ -2001,6 +2001,10 @@ #include "b.h" vs. #include "a.h" #include "a.h" #include "b.h" +**SortNetBSDIncludes** (``bool``) + If ``true``, clang-format will sort ``#includes`` according to NetBSD style. + + **SortUsingDeclarations** (``bool``) If ``true``, clang-format will sort using declarations. Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -1681,6 +1681,15 @@ /// \endcode bool SortIncludes; +/// SortIncludes Style. + enum IncludeHeadersStyle{ + ///Sorts includes lexicographically. + SIS_None, + ///Sorts includes according to NetBSD Style Guide. + SIS_NetBSD + }; + IncludeHeadersStyle SortIncludesStyle; + /// If ``true``, clang-format will sort using declarations. /// /// The order of using declarations is defined as follows: @@ -1995,6 +2004,7 @@ R.PenaltyBreakTemplateDeclaration && PointerAlignment == R.PointerAlignment && RawStringFormats == R.RawStringFormats && + SortIncludesStyle == R.SortIncludesStyle && SpaceAfterCStyleCast == R.SpaceAfterCStyleCast && SpaceAfterLogicalNot == R.SpaceAfterLogicalNot && SpaceAfterTemplateKeyword == R.SpaceAfterTemplateKeyword && @@ -2083,6 +2093,10 @@ /// http://www.gnu.org/prep/standards/standards.html FormatStyle getGNUStyle(); +/// Returns a format style complying with NetBSD Coding Standards: +/// http://cvsweb.netbsd.org/bsdweb.cgi/src/share/misc/style?rev=HEAD&content-type=text/x-cvsweb-markup +FormatStyle getNetBSDStyle(); + /// Returns style indicating formatting should be not applied at all. FormatStyle getNoStyle(); Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -278,8 +278,15 @@ } }; -template <> -struct ScalarEnumerationTraits { +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &IO, + FormatStyle::IncludeHeadersStyle &Value) { + IO.enumCase(Value, "None", FormatStyle::SIS_None); + IO.enumCase(Value, "NetBSD", FormatStyle::SIS_NetBSD); + } +}; + +template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, FormatStyle::SpaceBeforeParensOptions &Value) { IO.enumCase(Value, "Never", FormatStyle::SBPO_Never); @@ -479,6 +486,7 @@ IO.mapOptional("RawStringFormats", Style.RawStringFormats); IO.mapOptional("ReflowComments", Style.ReflowComments); IO.mapOptional("SortIncludes", Style.SortIncludes); + IO.mapOptional("SortIncludesStyle", Style.SortIncludesStyle); IO.mapOptional("SortUsingDeclarations", Style.SortUsingDeclarations); IO.mapOptional("SpaceAfterCStyleCast", Style.SpaceAfterCStyleCast); IO.mapOptional("SpaceAfterLogicalNot", Style.SpaceAfterLogicalNot); @@ -609,7 +617,7 @@ return Style; FormatStyle Expanded = Style; Expanded.BraceWrapping = {false, false, false, false, false, false, - false, false, false, false, false, + false, false, false, false, false, false, false, true, true, true}; switch (Style.BreakBeforeBraces) { case FormatStyle::BS_Linux: @@ -759,6 +767,7 @@ LLVMStyle.DisableFormat = false; LLVMStyle.SortIncludes = true; + LLVMStyle.SortIncludesStyle = FormatStyle::SIS_None; LLVMStyle.SortUsingDeclarations = true; LLVMStyle.StatementMacros.push_back("Q_UNUSED"); LLVMStyle.StatementMacros.push_back("QT_REQUIRE_VERSION"); @@ -919,6 +928,7 @@ "javax", }; ChromiumStyle.SortIncludes = true; + ChromiumStyle.SortIncludesStyle = FormatStyle::SIS_None; } else if (Language == FormatStyle::LK_JavaScript) { ChromiumStyle.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never; ChromiumStyle.AllowShortLoopsOnASingleLine = false; @@ -1023,6 +1033,30 @@ return Style; } +FormatStyle getNetBSDStyle() { + FormatStyle NetBSDStyle = getLLVMStyle(); + NetBSDStyle.AlignTrailingComments = true; + NetBSDStyle.AlwaysBreakAfterReturnType = FormatStyle::RTBS_AllDefinitions; + NetBSDStyle.AlignConsecutiveMacros = true; + NetBSDStyle.BreakBeforeBraces = FormatStyle::BS_Mozilla; + NetBSDStyle.ColumnLimit = 80; + NetBSDStyle.ContinuationIndentWidth = 4; + NetBSDStyle.Cpp11BracedListStyle = false; + NetBSDStyle.FixNamespaceComments = true; + NetBSDStyle.IndentCaseLabels = false; + NetBSDStyle.IndentWidth = 8; + NetBSDStyle.IncludeStyle.IncludeBlocks = tooling::IncludeStyle::IBS_Regroup; + NetBSDStyle.IncludeStyle.IncludeCategories = { + {"^", 2}, + {"^\"\w.*\.h\"$", 1}, {".*", 0}}; + NetBSDStyle.SortIncludes = true; + NetBSDStyle.SortIncludesStyle = FormatStyle::SIS_NetBSD; + NetBSDStyle.TabWidth = 8; + NetBSDStyle.UseTab = FormatStyle::UT_Always; + return NetBSDStyle; +} + FormatStyle getNoStyle() { FormatStyle NoStyle = getLLVMStyle(); NoStyle.DisableFormat = true; @@ -1047,6 +1081,8 @@ *Style = getGNUStyle(); } else if (Name.equals_lower("microsoft")) { *Style = getMicrosoftStyle(Language); + } else if (Name.equals_lower("netbsd")) { + *Style = getNetBSDStyle(); } else if (Name.equals_lower("none")) { *Style = getNoStyle(); } else { @@ -1763,6 +1799,81 @@ } return std::make_pair(CursorIndex, OffsetToEOL); } +enum CppIncludeHeadersKind { + IHK_KERNELHEADERS, + IHK_NETWORKHEADERS, + IHK_FILESYSHEADERS, + IHK_MACHINESHEADERS, + IHK_ARCHHEADERS, + IHK_USERHEADERS, + IHK_INCLUDESWITHQUOTES, +}; + +CppIncludeHeadersKind getHeadersKind(std::string Filename) { + SmallVector Matches; + const char KernelHeaderPattern[] = R"(^<(sys.*|uvm|dev)/)"; + const char NetworkHeaderPattern[] = R"(^<(net.*|protocols)/)"; + const char FilesSystemHeaderPattern[] = + R"(^<(fs|miscfs|msdosfs|nfs|ntfs|ufs))"; + const char MachineHeaderPattern[] = R"(^)"; + const char IncludeWithQuotesPattern[] = R"(^\"*$\")"; + llvm::Regex MatchKernel = llvm::Regex(KernelHeaderPattern); + llvm::Regex MatchNetwork = llvm::Regex(NetworkHeaderPattern); + llvm::Regex MatchFileSys = llvm::Regex(FilesSystemHeaderPattern); + llvm::Regex MatchMachine = llvm::Regex(MachineHeaderPattern); + llvm::Regex MatchArch = llvm::Regex(ArchHeadersPattern); + llvm::Regex MatchUser = llvm::Regex(UserHeaderPattern); + llvm::Regex MatchQuotes = llvm::Regex(IncludeWithQuotesPattern); + if (MatchKernel.match(Filename)) + return IHK_KERNELHEADERS; + else if (MatchNetwork.match(Filename)) + return IHK_NETWORKHEADERS; + else if (MatchArch.match(Filename)) + return IHK_ARCHHEADERS; + else if (MatchQuotes.match(Filename)) + return IHK_INCLUDESWITHQUOTES; + else if (MatchUser.match(Filename)) + return IHK_USERHEADERS; + else if (MatchFileSys.match(Filename)) + return IHK_FILESYSHEADERS; + else if (MatchMachine.match(Filename)) + return IHK_MACHINESHEADERS; + return IHK_INCLUDESWITHQUOTES; +} + +unsigned getNetBSDIncludePriority(std::string Filename) { + CppIncludeHeadersKind CK = getHeadersKind(Filename); + switch (CK) { + case IHK_KERNELHEADERS: + return llvm::StringSwitch(Filename) + .Case("", 0) + .Case("", 1) + .StartsWith("(Filename) + .StartsWith("(Filename) + .StartsWith(" &Includes, ArrayRef Ranges, StringRef FileName, - StringRef Code, - tooling::Replacements &Replaces, unsigned *Cursor) { + StringRef Code, tooling::Replacements &Replaces, + unsigned *Cursor) { unsigned IncludesBeginOffset = Includes.front().Offset; unsigned IncludesEndOffset = Includes.back().Offset + Includes.back().Text.size(); @@ -1783,11 +1894,22 @@ if (!affectsRange(Ranges, IncludesBeginOffset, IncludesEndOffset)) return; SmallVector Indices; - for (unsigned i = 0, e = Includes.size(); i != e; ++i) + SmallVector IncludesPriority; + for (unsigned i = 0, e = Includes.size(); i != e; ++i){ + if(Style.SortIncludesStyle == FormatStyle::SIS_NetBSD) + IncludesPriority.push_back(getNetBSDIncludePriority(Includes[i].Filename)); + else + IncludesPriority.push_back(i); Indices.push_back(i); + } + 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); + if (Style.SortIncludesStyle == FormatStyle::SIS_NetBSD) + return std::tie(IncludesPriority[LHSI], Includes[LHSI].Filename) < + std::tie(IncludesPriority[RHSI], Includes[RHSI].Filename); + else + 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. @@ -1906,8 +2028,8 @@ MainIncludeFound = true; IncludesInBlock.push_back({IncludeName, Line, Prev, Category}); } else if (!IncludesInBlock.empty() && !EmptyLineSkipped) { - sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Code, - Replaces, Cursor); + sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Code, + Replaces, Cursor); IncludesInBlock.clear(); FirstIncludeBlock = false; } @@ -1918,8 +2040,8 @@ SearchFrom = Pos + 1; } if (!IncludesInBlock.empty()) { - sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Code, Replaces, - Cursor); + sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Code, Replaces, + Cursor); } return Replaces; } Index: unittests/Format/SortIncludesTest.cpp =================================================================== --- unittests/Format/SortIncludesTest.cpp +++ unittests/Format/SortIncludesTest.cpp @@ -665,6 +665,67 @@ "#include \"a.h\"")); } +TEST_F(SortIncludesTest, ParamAndTypesCheck) { + FmtStyle = getNetBSDStyle(); + EXPECT_EQ("#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n", + sort("#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n")); + +} + +TEST_F(SortIncludesTest, SortedIncludesInSingleBlockReGroupWithNetBSDSpecifications) { + FmtStyle = getNetBSDStyle(); + EXPECT_EQ("#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "\n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "\n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "\n" + "#include \n" + "\n" + "#include \"pathnames.h\"\n", + sort("#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \"pathnames.h\"\n" + "#include \n" + "#include \n" + "#include \n" + "#include \n")); +} + } // end namespace } // end namespace format } // end namespace clang