diff options
author | Krasimir Georgiev <krasimir@google.com> | 2017-11-27 13:23:45 +0000 |
---|---|---|
committer | Krasimir Georgiev <krasimir@google.com> | 2017-11-27 13:23:45 +0000 |
commit | 4c2c9c3620e3bed9cb6fa709ec3b3cc454eb5748 (patch) | |
tree | e12bf4b790ef7ae6324b31ce60e65548998ca680 /clang | |
parent | 6e39e68983045fe48fb955f24afe376b46eb3600 (diff) | |
download | bcm5719-llvm-4c2c9c3620e3bed9cb6fa709ec3b3cc454eb5748.tar.gz bcm5719-llvm-4c2c9c3620e3bed9cb6fa709ec3b3cc454eb5748.zip |
[clang-format] Add option to group multiple #include blocks when sorting includes
Summary:
This patch allows grouping multiple #include blocks together and sort all includes as one big block.
Additionally, sorted includes can be regrouped after sorting based on configured categories.
Contributed by @KrzysztofKapusta!
Reviewers: krasimir
Reviewed By: krasimir
Subscribers: cfe-commits, klimek
Differential Revision: https://reviews.llvm.org/D40288
llvm-svn: 319024
Diffstat (limited to 'clang')
-rw-r--r-- | clang/docs/ClangFormatStyleOptions.rst | 39 | ||||
-rw-r--r-- | clang/include/clang/Format/Format.h | 35 | ||||
-rw-r--r-- | clang/lib/Format/Format.cpp | 28 | ||||
-rw-r--r-- | clang/unittests/Format/SortIncludesTest.cpp | 182 |
4 files changed, 281 insertions, 3 deletions
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index cf885d33f68..04267928a92 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -1173,6 +1173,45 @@ the configuration (without a prefix: ``Auto``). For example: BOOST_FOREACH. +**IncludeBlocks** (``IncludeBlocksStyle``) + Dependent on the value, multiple ``#include`` blocks can be sorted + as one and divided based on category. + + Possible values: + + * ``IBS_Preserve`` (in configuration: ``Preserve``) + Sort each ``#include`` block separately. + + .. code-block:: c++ + + #include "b.h" into #include "b.h" + + #include <lib/main.h> #include "a.h" + #include "a.h" #include <lib/main.h> + + * ``IBS_Merge`` (in configuration: ``Merge``) + Merge multiple ``#include`` blocks together and sort as one. + + .. code-block:: c++ + + #include "b.h" into #include "a.h" + #include "b.h" + #include <lib/main.h> #include <lib/main.h> + #include "a.h" + + * ``IBS_Regroup`` (in configuration: ``Regroup``) + Merge multiple ``#include`` blocks together and sort as one. + Then split into groups based on category priority. See ``IncludeCategories``. + + .. code-block:: c++ + + #include "b.h" into #include "a.h" + #include "b.h" + #include <lib/main.h> + #include "a.h" #include <lib/main.h> + + + **IncludeCategories** (``std::vector<IncludeCategory>``) Regular expressions denoting the different ``#include`` categories used for ordering ``#includes``. diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index fcca42d620f..d27d934f767 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -985,6 +985,40 @@ struct FormatStyle { /// For example: BOOST_FOREACH. std::vector<std::string> ForEachMacros; + /// \brief Styles for sorting multiple ``#include`` blocks. + enum IncludeBlocksStyle { + /// \brief Sort each ``#include`` block separately. + /// \code + /// #include "b.h" into #include "b.h" + /// + /// #include <lib/main.h> #include "a.h" + /// #include "a.h" #include <lib/main.h> + /// \endcode + IBS_Preserve, + /// \brief Merge multiple ``#include`` blocks together and sort as one. + /// \code + /// #include "b.h" into #include "a.h" + /// #include "b.h" + /// #include <lib/main.h> #include <lib/main.h> + /// #include "a.h" + /// \endcode + IBS_Merge, + /// \brief Merge multiple ``#include`` blocks together and sort as one. + /// Then split into groups based on category priority. See + /// ``IncludeCategories``. + /// \code + /// #include "b.h" into #include "a.h" + /// #include "b.h" + /// #include <lib/main.h> + /// #include "a.h" #include <lib/main.h> + /// \endcode + IBS_Regroup, + }; + + /// \brief Dependent on the value, multiple ``#include`` blocks can be sorted + /// as one and divided based on category. + IncludeBlocksStyle IncludeBlocks; + /// \brief See documentation of ``IncludeCategories``. struct IncludeCategory { /// \brief The regular expression that this category matches. @@ -1609,6 +1643,7 @@ struct FormatStyle { R.ExperimentalAutoDetectBinPacking && FixNamespaceComments == R.FixNamespaceComments && ForEachMacros == R.ForEachMacros && + IncludeBlocks == R.IncludeBlocks && IncludeCategories == R.IncludeCategories && IndentCaseLabels == R.IndentCaseLabels && IndentPPDirectives == R.IndentPPDirectives && diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 200f2b27cd4..c2f0a7d7803 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -361,6 +361,7 @@ template <> struct MappingTraits<FormatStyle> { Style.ExperimentalAutoDetectBinPacking); IO.mapOptional("FixNamespaceComments", Style.FixNamespaceComments); IO.mapOptional("ForEachMacros", Style.ForEachMacros); + IO.mapOptional("IncludeBlocks", Style.IncludeBlocks); IO.mapOptional("IncludeCategories", Style.IncludeCategories); IO.mapOptional("IncludeIsMainRegex", Style.IncludeIsMainRegex); IO.mapOptional("IndentCaseLabels", Style.IndentCaseLabels); @@ -444,6 +445,14 @@ template <> struct MappingTraits<FormatStyle::IncludeCategory> { } }; +template <> struct ScalarEnumerationTraits<FormatStyle::IncludeBlocksStyle> { + static void enumeration(IO &IO, FormatStyle::IncludeBlocksStyle &Value) { + IO.enumCase(Value, "Preserve", FormatStyle::IBS_Preserve); + IO.enumCase(Value, "Merge", FormatStyle::IBS_Merge); + IO.enumCase(Value, "Regroup", FormatStyle::IBS_Regroup); + } +}; + template <> struct MappingTraits<FormatStyle::RawStringFormat> { static void mapping(IO &IO, FormatStyle::RawStringFormat &Format) { IO.mapOptional("Delimiter", Format.Delimiter); @@ -614,6 +623,7 @@ FormatStyle getLLVMStyle() { {"^(<|\"(gtest|gmock|isl|json)/)", 3}, {".*", 1}}; LLVMStyle.IncludeIsMainRegex = "(Test)?$"; + LLVMStyle.IncludeBlocks = FormatStyle::IBS_Preserve; LLVMStyle.IndentCaseLabels = false; LLVMStyle.IndentPPDirectives = FormatStyle::PPDIS_None; LLVMStyle.IndentWrappedFunctionNames = false; @@ -1420,19 +1430,27 @@ static void sortCppIncludes(const FormatStyle &Style, }), Indices.end()); + int CurrentCategory = Includes.front().Category; + // If the #includes are out of order, we generate a single replacement fixing // the entire block. Otherwise, no replacement is generated. if (Indices.size() == Includes.size() && - std::is_sorted(Indices.begin(), Indices.end())) + std::is_sorted(Indices.begin(), Indices.end()) && + Style.IncludeBlocks == FormatStyle::IBS_Preserve) return; std::string result; for (unsigned Index : Indices) { - if (!result.empty()) + if (!result.empty()) { result += "\n"; + if (Style.IncludeBlocks == FormatStyle::IBS_Regroup && + CurrentCategory != Includes[Index].Category) + result += "\n"; + } result += Includes[Index].Text; if (Cursor && CursorIndex == Index) *Cursor = IncludesBeginOffset + result.size() - CursorToEOLOffset; + CurrentCategory = Includes[Index].Category; } auto Err = Replaces.add(tooling::Replacement( @@ -1540,6 +1558,10 @@ tooling::Replacements sortCppIncludes(const FormatStyle &Style, StringRef Code, else if (Trimmed == "// clang-format on") FormattingOff = false; + const bool EmptyLineSkipped = + Trimmed.empty() && (Style.IncludeBlocks == FormatStyle::IBS_Merge || + Style.IncludeBlocks == FormatStyle::IBS_Regroup); + if (!FormattingOff && !Line.endswith("\\")) { if (IncludeRegex.match(Line, &Matches)) { StringRef IncludeName = Matches[2]; @@ -1549,7 +1571,7 @@ tooling::Replacements sortCppIncludes(const FormatStyle &Style, StringRef Code, if (Category == 0) MainIncludeFound = true; IncludesInBlock.push_back({IncludeName, Line, Prev, Category}); - } else if (!IncludesInBlock.empty()) { + } else if (!IncludesInBlock.empty() && !EmptyLineSkipped) { sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Replaces, Cursor); IncludesInBlock.clear(); diff --git a/clang/unittests/Format/SortIncludesTest.cpp b/clang/unittests/Format/SortIncludesTest.cpp index ff3988776bd..09fc0703d42 100644 --- a/clang/unittests/Format/SortIncludesTest.cpp +++ b/clang/unittests/Format/SortIncludesTest.cpp @@ -77,6 +77,28 @@ TEST_F(SortIncludesTest, NoReplacementsForValidIncludes) { EXPECT_TRUE(sortIncludes(Style, Code, GetCodeRange(Code), "a.cc").empty()); } +TEST_F(SortIncludesTest, SortedIncludesInMultipleBlocksAreMerged) { + Style.IncludeBlocks = FormatStyle::IBS_Merge; + EXPECT_EQ("#include \"a.h\"\n" + "#include \"b.h\"\n" + "#include \"c.h\"\n", + sort("#include \"a.h\"\n" + "#include \"c.h\"\n" + "\n" + "\n" + "#include \"b.h\"\n")); + + Style.IncludeBlocks = FormatStyle::IBS_Regroup; + EXPECT_EQ("#include \"a.h\"\n" + "#include \"b.h\"\n" + "#include \"c.h\"\n", + sort("#include \"a.h\"\n" + "#include \"c.h\"\n" + "\n" + "\n" + "#include \"b.h\"\n")); +} + TEST_F(SortIncludesTest, SupportClangFormatOff) { EXPECT_EQ("#include <a>\n" "#include <b>\n" @@ -159,6 +181,48 @@ TEST_F(SortIncludesTest, SortsLocallyInEachBlock) { "#include \"b.h\"\n")); } +TEST_F(SortIncludesTest, SortsAllBlocksWhenMerging) { + Style.IncludeBlocks = FormatStyle::IBS_Merge; + EXPECT_EQ("#include \"a.h\"\n" + "#include \"b.h\"\n" + "#include \"c.h\"\n", + sort("#include \"a.h\"\n" + "#include \"c.h\"\n" + "\n" + "#include \"b.h\"\n")); +} + +TEST_F(SortIncludesTest, CommentsAlwaysSeparateGroups) { + EXPECT_EQ("#include \"a.h\"\n" + "#include \"c.h\"\n" + "// comment\n" + "#include \"b.h\"\n", + sort("#include \"c.h\"\n" + "#include \"a.h\"\n" + "// comment\n" + "#include \"b.h\"\n")); + + Style.IncludeBlocks = FormatStyle::IBS_Merge; + EXPECT_EQ("#include \"a.h\"\n" + "#include \"c.h\"\n" + "// comment\n" + "#include \"b.h\"\n", + sort("#include \"c.h\"\n" + "#include \"a.h\"\n" + "// comment\n" + "#include \"b.h\"\n")); + + Style.IncludeBlocks = FormatStyle::IBS_Regroup; + EXPECT_EQ("#include \"a.h\"\n" + "#include \"c.h\"\n" + "// comment\n" + "#include \"b.h\"\n", + sort("#include \"c.h\"\n" + "#include \"a.h\"\n" + "// comment\n" + "#include \"b.h\"\n")); +} + TEST_F(SortIncludesTest, HandlesAngledIncludesAsSeparateBlocks) { EXPECT_EQ("#include \"a.h\"\n" "#include \"c.h\"\n" @@ -180,6 +244,19 @@ TEST_F(SortIncludesTest, HandlesAngledIncludesAsSeparateBlocks) { "#include \"a.h\"\n")); } +TEST_F(SortIncludesTest, RegroupsAngledIncludesInSeparateBlocks) { + Style.IncludeBlocks = FormatStyle::IBS_Regroup; + EXPECT_EQ("#include \"a.h\"\n" + "#include \"c.h\"\n" + "\n" + "#include <b.h>\n" + "#include <d.h>\n", + sort("#include <d.h>\n" + "#include <b.h>\n" + "#include \"c.h\"\n" + "#include \"a.h\"\n")); +} + TEST_F(SortIncludesTest, HandlesMultilineIncludes) { EXPECT_EQ("#include \"a.h\"\n" "#include \"b.h\"\n" @@ -266,6 +343,35 @@ TEST_F(SortIncludesTest, LeavesMainHeaderFirst) { "a.cc")); } +TEST_F(SortIncludesTest, RecognizeMainHeaderInAllGroups) { + Style.IncludeIsMainRegex = "([-_](test|unittest))?$"; + Style.IncludeBlocks = FormatStyle::IBS_Merge; + + EXPECT_EQ("#include \"c.h\"\n" + "#include \"a.h\"\n" + "#include \"b.h\"\n", + sort("#include \"b.h\"\n" + "\n" + "#include \"a.h\"\n" + "#include \"c.h\"\n", + "c.cc")); +} + +TEST_F(SortIncludesTest, MainHeaderIsSeparatedWhenRegroupping) { + Style.IncludeIsMainRegex = "([-_](test|unittest))?$"; + Style.IncludeBlocks = FormatStyle::IBS_Regroup; + + EXPECT_EQ("#include \"a.h\"\n" + "\n" + "#include \"b.h\"\n" + "#include \"c.h\"\n", + sort("#include \"b.h\"\n" + "\n" + "#include \"a.h\"\n" + "#include \"c.h\"\n", + "a.cc")); +} + TEST_F(SortIncludesTest, SupportCaseInsensitiveMatching) { // Setup an regex for main includes so we can cover those as well. Style.IncludeIsMainRegex = "([-_](test|unittest))?$"; @@ -309,6 +415,34 @@ TEST_F(SortIncludesTest, NegativePriorities) { "c_main.cc")); } +TEST_F(SortIncludesTest, PriorityGroupsAreSeparatedWhenRegroupping) { + Style.IncludeCategories = {{".*important_os_header.*", -1}, {".*", 1}}; + Style.IncludeBlocks = FormatStyle::IBS_Regroup; + + EXPECT_EQ("#include \"important_os_header.h\"\n" + "\n" + "#include \"c_main.h\"\n" + "\n" + "#include \"a_other.h\"\n", + sort("#include \"c_main.h\"\n" + "#include \"a_other.h\"\n" + "#include \"important_os_header.h\"\n", + "c_main.cc")); + + // check stable when re-run + EXPECT_EQ("#include \"important_os_header.h\"\n" + "\n" + "#include \"c_main.h\"\n" + "\n" + "#include \"a_other.h\"\n", + sort("#include \"important_os_header.h\"\n" + "\n" + "#include \"c_main.h\"\n" + "\n" + "#include \"a_other.h\"\n", + "c_main.cc")); +} + TEST_F(SortIncludesTest, CalculatesCorrectCursorPosition) { std::string Code = "#include <ccc>\n" // Start of line: 0 "#include <bbbbbb>\n" // Start of line: 15 @@ -332,6 +466,30 @@ TEST_F(SortIncludesTest, DeduplicateIncludes) { "#include <b>\n" "#include <b>\n" "#include <c>\n")); + + Style.IncludeBlocks = FormatStyle::IBS_Merge; + EXPECT_EQ("#include <a>\n" + "#include <b>\n" + "#include <c>\n", + sort("#include <a>\n" + "#include <b>\n" + "\n" + "#include <b>\n" + "\n" + "#include <b>\n" + "#include <c>\n")); + + Style.IncludeBlocks = FormatStyle::IBS_Regroup; + EXPECT_EQ("#include <a>\n" + "#include <b>\n" + "#include <c>\n", + sort("#include <a>\n" + "#include <b>\n" + "\n" + "#include <b>\n" + "\n" + "#include <b>\n" + "#include <c>\n")); } TEST_F(SortIncludesTest, SortAndDeduplicateIncludes) { @@ -344,6 +502,30 @@ TEST_F(SortIncludesTest, SortAndDeduplicateIncludes) { "#include <b>\n" "#include <c>\n" "#include <b>\n")); + + Style.IncludeBlocks = FormatStyle::IBS_Merge; + EXPECT_EQ("#include <a>\n" + "#include <b>\n" + "#include <c>\n", + sort("#include <b>\n" + "#include <a>\n" + "\n" + "#include <b>\n" + "\n" + "#include <c>\n" + "#include <b>\n")); + + Style.IncludeBlocks = FormatStyle::IBS_Regroup; + EXPECT_EQ("#include <a>\n" + "#include <b>\n" + "#include <c>\n", + sort("#include <b>\n" + "#include <a>\n" + "\n" + "#include <b>\n" + "\n" + "#include <c>\n" + "#include <b>\n")); } TEST_F(SortIncludesTest, CalculatesCorrectCursorPositionAfterDeduplicate) { |