Skip to content

Commit 1653a09

Browse files
authored
[clang-format] Add IgnoreExtension to SortIncludes (#137840)
Sorting by stem gives nicer results when various header file names are substrings of other header file names. For example, a CLI application with a main header named analyze.h and an analyze-xxx.h header for each subcommand currently will always put analyze.h last after all the analyze-xxx.h headers, but putting analyze.h first instead is arguably nicer to read. TLDR; Instead of ``` #include "analyze-blame.h" #include "analyze.h" ``` You'd get ``` #include "analyze.h" #include "analyze-blame.h" ``` Let's allow sorting by stem instead of full path by adding IgnoreExtension to SortIncludes.
1 parent 3cb0c7f commit 1653a09

File tree

5 files changed

+83
-26
lines changed

5 files changed

+83
-26
lines changed

clang/docs/ClangFormatStyleOptions.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6015,6 +6015,16 @@ the configuration (without a prefix: ``Auto``).
60156015
#include "B/A.h" #include "B/a.h"
60166016
#include "B/a.h" #include "a/b.h"
60176017

6018+
* ``bool IgnoreExtension`` When sorting includes in each block, only take file extensions into
6019+
account if two includes compare equal otherwise.
6020+
6021+
.. code-block:: c++
6022+
6023+
true: false:
6024+
# include "A.h" vs. # include "A-util.h"
6025+
# include "A.inc" # include "A.h"
6026+
# include "A-util.h" # include "A.inc"
6027+
60186028

60196029
.. _SortJavaStaticImport:
60206030

clang/include/clang/Format/Format.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4385,8 +4385,18 @@ struct FormatStyle {
43854385
/// #include "B/a.h" #include "a/b.h"
43864386
/// \endcode
43874387
bool IgnoreCase;
4388+
/// When sorting includes in each block, only take file extensions into
4389+
/// account if two includes compare equal otherwise.
4390+
/// \code
4391+
/// true: false:
4392+
/// # include "A.h" vs. # include "A-util.h"
4393+
/// # include "A.inc" # include "A.h"
4394+
/// # include "A-util.h" # include "A.inc"
4395+
/// \endcode
4396+
bool IgnoreExtension;
43884397
bool operator==(const SortIncludesOptions &R) const {
4389-
return Enabled == R.Enabled && IgnoreCase == R.IgnoreCase;
4398+
return Enabled == R.Enabled && IgnoreCase == R.IgnoreCase &&
4399+
IgnoreExtension == R.IgnoreExtension;
43904400
}
43914401
bool operator!=(const SortIncludesOptions &R) const {
43924402
return !(*this == R);

clang/lib/Format/Format.cpp

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -665,21 +665,25 @@ template <> struct MappingTraits<FormatStyle::SortIncludesOptions> {
665665
IO.enumCase(Value, "Never", FormatStyle::SortIncludesOptions({}));
666666
IO.enumCase(Value, "CaseInsensitive",
667667
FormatStyle::SortIncludesOptions({/*Enabled=*/true,
668-
/*IgnoreCase=*/true}));
668+
/*IgnoreCase=*/true,
669+
/*IgnoreExtension=*/false}));
669670
IO.enumCase(Value, "CaseSensitive",
670671
FormatStyle::SortIncludesOptions({/*Enabled=*/true,
671-
/*IgnoreCase=*/false}));
672+
/*IgnoreCase=*/false,
673+
/*IgnoreExtension=*/false}));
672674

673675
// For backward compatibility.
674676
IO.enumCase(Value, "false", FormatStyle::SortIncludesOptions({}));
675677
IO.enumCase(Value, "true",
676678
FormatStyle::SortIncludesOptions({/*Enabled=*/true,
677-
/*IgnoreCase=*/false}));
679+
/*IgnoreCase=*/false,
680+
/*IgnoreExtension=*/false}));
678681
}
679682

680683
static void mapping(IO &IO, FormatStyle::SortIncludesOptions &Value) {
681684
IO.mapOptional("Enabled", Value.Enabled);
682685
IO.mapOptional("IgnoreCase", Value.IgnoreCase);
686+
IO.mapOptional("IgnoreExtension", Value.IgnoreExtension);
683687
}
684688
};
685689

@@ -1650,7 +1654,8 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
16501654
LLVMStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Leave;
16511655
LLVMStyle.ShortNamespaceLines = 1;
16521656
LLVMStyle.SkipMacroDefinitionBody = false;
1653-
LLVMStyle.SortIncludes = {/*Enabled=*/true, /*IgnoreCase=*/false};
1657+
LLVMStyle.SortIncludes = {/*Enabled=*/true, /*IgnoreCase=*/false,
1658+
/*IgnoreExtension=*/false};
16541659
LLVMStyle.SortJavaStaticImport = FormatStyle::SJSIO_Before;
16551660
LLVMStyle.SortUsingDeclarations = FormatStyle::SUD_LexicographicNumeric;
16561661
LLVMStyle.SpaceAfterCStyleCast = false;
@@ -3239,19 +3244,27 @@ static void sortCppIncludes(const FormatStyle &Style,
32393244
SmallVector<unsigned, 16> Indices =
32403245
llvm::to_vector<16>(llvm::seq<unsigned>(0, Includes.size()));
32413246

3242-
if (Style.SortIncludes.Enabled && Style.SortIncludes.IgnoreCase) {
3247+
if (Style.SortIncludes.Enabled) {
32433248
stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) {
3244-
const auto LHSFilenameLower = Includes[LHSI].Filename.lower();
3245-
const auto RHSFilenameLower = Includes[RHSI].Filename.lower();
3246-
return std::tie(Includes[LHSI].Priority, LHSFilenameLower,
3247-
Includes[LHSI].Filename) <
3248-
std::tie(Includes[RHSI].Priority, RHSFilenameLower,
3249-
Includes[RHSI].Filename);
3250-
});
3251-
} else {
3252-
stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) {
3253-
return std::tie(Includes[LHSI].Priority, Includes[LHSI].Filename) <
3254-
std::tie(Includes[RHSI].Priority, Includes[RHSI].Filename);
3249+
SmallString<128> LHSStem, RHSStem;
3250+
if (Style.SortIncludes.IgnoreExtension) {
3251+
LHSStem = Includes[LHSI].Filename;
3252+
RHSStem = Includes[RHSI].Filename;
3253+
llvm::sys::path::replace_extension(LHSStem, "");
3254+
llvm::sys::path::replace_extension(RHSStem, "");
3255+
}
3256+
std::string LHSStemLower, RHSStemLower;
3257+
std::string LHSFilenameLower, RHSFilenameLower;
3258+
if (Style.SortIncludes.IgnoreCase) {
3259+
LHSStemLower = LHSStem.str().lower();
3260+
RHSStemLower = RHSStem.str().lower();
3261+
LHSFilenameLower = Includes[LHSI].Filename.lower();
3262+
RHSFilenameLower = Includes[RHSI].Filename.lower();
3263+
}
3264+
return std::tie(Includes[LHSI].Priority, LHSStemLower, LHSStem,
3265+
LHSFilenameLower, Includes[LHSI].Filename) <
3266+
std::tie(Includes[RHSI].Priority, RHSStemLower, RHSStem,
3267+
RHSFilenameLower, Includes[RHSI].Filename);
32553268
});
32563269
}
32573270

clang/unittests/Format/ConfigParseTest.cpp

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
259259
CHECK_PARSE_NESTED_BOOL(SpacesInParensOptions, Other);
260260
CHECK_PARSE_NESTED_BOOL(SortIncludes, Enabled);
261261
CHECK_PARSE_NESTED_BOOL(SortIncludes, IgnoreCase);
262+
CHECK_PARSE_NESTED_BOOL(SortIncludes, IgnoreExtension);
262263
}
263264

264265
#undef CHECK_PARSE_BOOL
@@ -980,17 +981,20 @@ TEST(ConfigParseTest, ParsesConfiguration) {
980981
IncludeStyle.IncludeIsMainSourceRegex, "abc$");
981982

982983
Style.SortIncludes = {};
983-
CHECK_PARSE("SortIncludes: true", SortIncludes,
984-
FormatStyle::SortIncludesOptions(
985-
{/*Enabled=*/true, /*IgnoreCase=*/false}));
984+
CHECK_PARSE(
985+
"SortIncludes: true", SortIncludes,
986+
FormatStyle::SortIncludesOptions(
987+
{/*Enabled=*/true, /*IgnoreCase=*/false, /*IgnoreExtension=*/false}));
986988
CHECK_PARSE("SortIncludes: false", SortIncludes,
987989
FormatStyle::SortIncludesOptions({}));
988-
CHECK_PARSE("SortIncludes: CaseInsensitive", SortIncludes,
989-
FormatStyle::SortIncludesOptions(
990-
{/*Enabled=*/true, /*IgnoreCase=*/true}));
991-
CHECK_PARSE("SortIncludes: CaseSensitive", SortIncludes,
992-
FormatStyle::SortIncludesOptions(
993-
{/*Enabled=*/true, /*IgnoreCase=*/false}));
990+
CHECK_PARSE(
991+
"SortIncludes: CaseInsensitive", SortIncludes,
992+
FormatStyle::SortIncludesOptions(
993+
{/*Enabled=*/true, /*IgnoreCase=*/true, /*IgnoreExtension=*/false}));
994+
CHECK_PARSE(
995+
"SortIncludes: CaseSensitive", SortIncludes,
996+
FormatStyle::SortIncludesOptions(
997+
{/*Enabled=*/true, /*IgnoreCase=*/false, /*IgnoreExtension=*/false}));
994998
CHECK_PARSE("SortIncludes: Never", SortIncludes,
995999
FormatStyle::SortIncludesOptions({}));
9961000

clang/unittests/Format/SortIncludesTest.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,6 +1483,26 @@ TEST_F(SortIncludesTest, BlockCommentedOutIncludes) {
14831483
verifyFormat(Code, sort(Code, "input.cpp", 0));
14841484
}
14851485

1486+
TEST_F(SortIncludesTest, IgnoreExtension) {
1487+
FmtStyle.SortIncludes.IgnoreExtension = true;
1488+
1489+
verifyFormat("#include <a.h>\n"
1490+
"#include <a.inc>\n"
1491+
"#include <a-util.h>",
1492+
sort("#include <a.inc>\n"
1493+
"#include <a-util.h>\n"
1494+
"#include <a.h>",
1495+
"input.h"));
1496+
1497+
verifyFormat("#include <ab.h>\n"
1498+
"#include <ab-beta.h>\n"
1499+
"#include <ab-data.h>",
1500+
sort("#include <ab-data.h>\n"
1501+
"#include <ab.h>\n"
1502+
"#include <ab-beta.h>",
1503+
"input.h"));
1504+
}
1505+
14861506
} // end namespace
14871507
} // end namespace format
14881508
} // end namespace clang

0 commit comments

Comments
 (0)