summaryrefslogtreecommitdiffstats
path: root/clang
diff options
context:
space:
mode:
authorKrasimir Georgiev <krasimir@google.com>2017-11-27 13:23:45 +0000
committerKrasimir Georgiev <krasimir@google.com>2017-11-27 13:23:45 +0000
commit4c2c9c3620e3bed9cb6fa709ec3b3cc454eb5748 (patch)
treee12bf4b790ef7ae6324b31ce60e65548998ca680 /clang
parent6e39e68983045fe48fb955f24afe376b46eb3600 (diff)
downloadbcm5719-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.rst39
-rw-r--r--clang/include/clang/Format/Format.h35
-rw-r--r--clang/lib/Format/Format.cpp28
-rw-r--r--clang/unittests/Format/SortIncludesTest.cpp182
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) {
OpenPOWER on IntegriCloud