Skip to content

Commit 3395a33

Browse files
kentsommermkurdej
authored andcommitted
[clang-format] add case aware include sorting
* Adds an option to [clang-format] which sorts headers in an alphabetical manner using case only for tie-breakers. The options is off by default in favor of the current ASCIIbetical sorting style. Reviewed By: curdeius, HazardyKnusperkeks Differential Revision: https://reviews.llvm.org/D95017
1 parent 9390b85 commit 3395a33

File tree

5 files changed

+105
-4
lines changed

5 files changed

+105
-4
lines changed

clang/docs/ClangFormatStyleOptions.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2282,6 +2282,26 @@ the configuration (without a prefix: ``Auto``).
22822282
``ClassImpl.hpp`` would not have the main include file put on top
22832283
before any other include.
22842284

2285+
**IncludeSortAlphabetically** (``bool``)
2286+
Specify if sorting should be done in an alphabetical and
2287+
case sensitive fashion.
2288+
2289+
When ``false``, includes are sorted in an ASCIIbetical
2290+
fashion.
2291+
When ``true``, includes are sorted in an alphabetical
2292+
fashion with case used as a tie-breaker.
2293+
2294+
.. code-block:: c++
2295+
2296+
false: true:
2297+
#include "A/B.h" vs. #include "A/B.h"
2298+
#include "A/b.h" #include "A/b.h"
2299+
#include "B/A.h" #include "a/b.h"
2300+
#include "B/a.h" #include "B/A.h"
2301+
#include "a/b.h" #include "B/a.h"
2302+
2303+
This option is off by default.
2304+
22852305
**IndentCaseBlocks** (``bool``)
22862306
Indent case label blocks one level from the case label.
22872307

clang/include/clang/Tooling/Inclusions/IncludeStyle.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,26 @@ struct IncludeStyle {
147147
/// ``ClassImpl.hpp`` would not have the main include file put on top
148148
/// before any other include.
149149
std::string IncludeIsMainSourceRegex;
150+
151+
/// Specify if sorting should be done in an alphabetical and
152+
/// case sensitive fashion.
153+
///
154+
/// When ``false``, includes are sorted in an ASCIIbetical
155+
/// fashion.
156+
/// When ``true``, includes are sorted in an alphabetical
157+
/// fashion with case used as a tie-breaker.
158+
///
159+
/// \code
160+
/// false: true:
161+
/// #include "A/B.h" vs. #include "A/B.h"
162+
/// #include "A/b.h" #include "A/b.h"
163+
/// #include "B/A.h" #include "a/b.h"
164+
/// #include "B/a.h" #include "B/A.h"
165+
/// #include "a/b.h" #include "B/a.h"
166+
/// \endcode
167+
///
168+
/// This option is off by default.
169+
bool IncludeSortAlphabetically;
150170
};
151171

152172
} // namespace tooling

clang/lib/Format/Format.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,8 @@ template <> struct MappingTraits<FormatStyle> {
571571
IO.mapOptional("IncludeIsMainRegex", Style.IncludeStyle.IncludeIsMainRegex);
572572
IO.mapOptional("IncludeIsMainSourceRegex",
573573
Style.IncludeStyle.IncludeIsMainSourceRegex);
574+
IO.mapOptional("IncludeSortAlphabetically",
575+
Style.IncludeStyle.IncludeSortAlphabetically);
574576
IO.mapOptional("IndentCaseLabels", Style.IndentCaseLabels);
575577
IO.mapOptional("IndentCaseBlocks", Style.IndentCaseBlocks);
576578
IO.mapOptional("IndentGotoLabels", Style.IndentGotoLabels);
@@ -940,6 +942,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
940942
{".*", 1, 0, false}};
941943
LLVMStyle.IncludeStyle.IncludeIsMainRegex = "(Test)?$";
942944
LLVMStyle.IncludeStyle.IncludeBlocks = tooling::IncludeStyle::IBS_Preserve;
945+
LLVMStyle.IncludeStyle.IncludeSortAlphabetically = false;
943946
LLVMStyle.IndentCaseLabels = false;
944947
LLVMStyle.IndentCaseBlocks = false;
945948
LLVMStyle.IndentGotoLabels = true;
@@ -2194,10 +2197,23 @@ static void sortCppIncludes(const FormatStyle &Style,
21942197
for (unsigned i = 0, e = Includes.size(); i != e; ++i) {
21952198
Indices.push_back(i);
21962199
}
2197-
llvm::stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) {
2198-
return std::tie(Includes[LHSI].Priority, Includes[LHSI].Filename) <
2199-
std::tie(Includes[RHSI].Priority, Includes[RHSI].Filename);
2200-
});
2200+
2201+
if (Style.IncludeStyle.IncludeSortAlphabetically) {
2202+
llvm::stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) {
2203+
const auto LHSFilenameLower = Includes[LHSI].Filename.lower();
2204+
const auto RHSFilenameLower = Includes[RHSI].Filename.lower();
2205+
return std::tie(Includes[LHSI].Priority, LHSFilenameLower,
2206+
Includes[LHSI].Filename) <
2207+
std::tie(Includes[RHSI].Priority, RHSFilenameLower,
2208+
Includes[RHSI].Filename);
2209+
});
2210+
} else {
2211+
llvm::stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) {
2212+
return std::tie(Includes[LHSI].Priority, Includes[LHSI].Filename) <
2213+
std::tie(Includes[RHSI].Priority, Includes[RHSI].Filename);
2214+
});
2215+
}
2216+
22012217
// The index of the include on which the cursor will be put after
22022218
// sorting/deduplicating.
22032219
unsigned CursorIndex;

clang/unittests/Format/FormatTest.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15145,6 +15145,8 @@ TEST_F(FormatTest, ParsesConfigurationBools) {
1514515145
CHECK_PARSE_BOOL(DeriveLineEnding);
1514615146
CHECK_PARSE_BOOL(DerivePointerAlignment);
1514715147
CHECK_PARSE_BOOL_FIELD(DerivePointerAlignment, "DerivePointerBinding");
15148+
CHECK_PARSE_BOOL_FIELD(IncludeStyle.IncludeSortAlphabetically,
15149+
"IncludeSortAlphabetically");
1514815150
CHECK_PARSE_BOOL(DisableFormat);
1514915151
CHECK_PARSE_BOOL(IndentCaseLabels);
1515015152
CHECK_PARSE_BOOL(IndentCaseBlocks);

clang/unittests/Format/SortIncludesTest.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,49 @@ TEST_F(SortIncludesTest, MainHeaderIsSeparatedWhenRegroupping) {
598598
"a.cc"));
599599
}
600600

601+
TEST_F(SortIncludesTest, SupportOptionalAlphabeticalSorting) {
602+
EXPECT_FALSE(Style.IncludeSortAlphabetically);
603+
604+
Style.IncludeSortAlphabetically = true;
605+
606+
EXPECT_EQ("#include \"A/B.h\"\n"
607+
"#include \"A/b.h\"\n"
608+
"#include \"a/b.h\"\n"
609+
"#include \"B/A.h\"\n"
610+
"#include \"B/a.h\"\n",
611+
sort("#include \"B/a.h\"\n"
612+
"#include \"B/A.h\"\n"
613+
"#include \"A/B.h\"\n"
614+
"#include \"a/b.h\"\n"
615+
"#include \"A/b.h\"\n",
616+
"a.h"));
617+
618+
Style.IncludeBlocks = clang::tooling::IncludeStyle::IBS_Regroup;
619+
Style.IncludeCategories = {
620+
{"^\"", 1, 0, false}, {"^<.*\\.h>$", 2, 0, false}, {"^<", 3, 0, false}};
621+
622+
StringRef UnsortedCode = "#include \"qt.h\"\n"
623+
"#include <algorithm>\n"
624+
"#include <qtwhatever.h>\n"
625+
"#include <Qtwhatever.h>\n"
626+
"#include <Algorithm>\n"
627+
"#include \"vlib.h\"\n"
628+
"#include \"Vlib.h\"\n"
629+
"#include \"AST.h\"\n";
630+
631+
EXPECT_EQ("#include \"AST.h\"\n"
632+
"#include \"qt.h\"\n"
633+
"#include \"Vlib.h\"\n"
634+
"#include \"vlib.h\"\n"
635+
"\n"
636+
"#include <Qtwhatever.h>\n"
637+
"#include <qtwhatever.h>\n"
638+
"\n"
639+
"#include <Algorithm>\n"
640+
"#include <algorithm>\n",
641+
sort(UnsortedCode));
642+
}
643+
601644
TEST_F(SortIncludesTest, SupportCaseInsensitiveMatching) {
602645
// Setup an regex for main includes so we can cover those as well.
603646
Style.IncludeIsMainRegex = "([-_](test|unittest))?$";

0 commit comments

Comments
 (0)