[clang] clang-format: Add IncludeSortKey option (PR #137840)
Daan De Meyer via cfe-commits
cfe-commits at lists.llvm.org
Mon May 19 04:44:12 PDT 2025
https://github.com/DaanDeMeyer updated https://github.com/llvm/llvm-project/pull/137840
>From 33f84b217caab01a5ebbf93e4e5ac2182810c8db Mon Sep 17 00:00:00 2001
From: Daan De Meyer <daan.j.demeyer at gmail.com>
Date: Tue, 29 Apr 2025 18:26:36 +0200
Subject: [PATCH] clang-format: Add IgnoreExtension option to
SortIncludesOptions
Sorting without taking the file extension into account 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 a
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
of last is arguable 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 without taking the file extension into account unless two
headers otherwise compare equal by introducing a new boolean option IgnoreExtension
for SortIncludesOptions.
---
clang/docs/ClangFormatStyleOptions.rst | 11 +++++++
clang/include/clang/Format/Format.h | 11 ++++++-
clang/lib/Format/Format.cpp | 34 +++++++++++++--------
clang/unittests/Format/SortIncludesTest.cpp | 20 ++++++++++++
4 files changed, 62 insertions(+), 14 deletions(-)
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 83716cc049ee3..8ec658ef4b855 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -6010,6 +6010,17 @@ the configuration (without a prefix: ``Auto``).
#include "B/A.h" #include "B/a.h"
#include "B/a.h" #include "a/b.h"
+ * ``bool IgnoreExtension`` :versionbadge:`clang-format 21`
+ When sorting includes in each block, Only take file extensions into
+ account if two includes compare equal otherwise.
+
+ .. code-block:: c++
+
+ true: false:
+ # include "A.h" # include "A-util.h"
+ # include "A.inc" # include "A.h"
+ # include "A-util.h" # include "A.inc"
+
.. _SortJavaStaticImport:
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 3ac4318824ac0..18e314426c1ae 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -4382,8 +4382,17 @@ struct FormatStyle {
/// #include "B/a.h" #include "a/b.h"
/// \endcode
bool IgnoreCase;
+ /// When sorting includes in each block, Only take file extensions into
+ /// account if two includes compare equal otherwise.
+ /// \code
+ /// true: false:
+ /// # include "A.h" # include "A-util.h"
+ /// # include "A.inc" # include "A.h"
+ /// # include "A-util.h" # include "A.inc"
+ /// \version 21
+ bool IgnoreExtension;
bool operator==(const SortIncludesOptions &R) const {
- return Enabled == R.Enabled && IgnoreCase == R.IgnoreCase;
+ return Enabled == R.Enabled && IgnoreCase == R.IgnoreCase && IgnoreExtension == R.IgnoreExtension;
}
bool operator!=(const SortIncludesOptions &R) const {
return !(*this == R);
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index b41a98ecb5be1..6bf951ff61bf0 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -1647,7 +1647,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Leave;
LLVMStyle.ShortNamespaceLines = 1;
LLVMStyle.SkipMacroDefinitionBody = false;
- LLVMStyle.SortIncludes = {/*Enabled=*/true, /*IgnoreCase=*/false};
+ LLVMStyle.SortIncludes = {/*Enabled=*/true, /*IgnoreCase=*/false, /*IgnoreExtension=*/false};
LLVMStyle.SortJavaStaticImport = FormatStyle::SJSIO_Before;
LLVMStyle.SortUsingDeclarations = FormatStyle::SUD_LexicographicNumeric;
LLVMStyle.SpaceAfterCStyleCast = false;
@@ -3230,19 +3230,27 @@ static void sortCppIncludes(const FormatStyle &Style,
SmallVector<unsigned, 16> Indices =
llvm::to_vector<16>(llvm::seq<unsigned>(0, Includes.size()));
- if (Style.SortIncludes.Enabled && Style.SortIncludes.IgnoreCase) {
+ if (Style.SortIncludes.Enabled) {
stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) {
- const auto LHSFilenameLower = Includes[LHSI].Filename.lower();
- const auto RHSFilenameLower = Includes[RHSI].Filename.lower();
- return std::tie(Includes[LHSI].Priority, LHSFilenameLower,
- Includes[LHSI].Filename) <
- std::tie(Includes[RHSI].Priority, RHSFilenameLower,
- Includes[RHSI].Filename);
- });
- } else {
- stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) {
- return std::tie(Includes[LHSI].Priority, Includes[LHSI].Filename) <
- std::tie(Includes[RHSI].Priority, Includes[RHSI].Filename);
+ SmallString<128> LHSStem, RHSStem;
+ if (Style.SortIncludes.IgnoreExtension) {
+ LHSStem = Includes[LHSI].Filename;
+ RHSStem = Includes[RHSI].Filename;
+ llvm::sys::path::replace_extension(LHSStem, "");
+ llvm::sys::path::replace_extension(RHSStem, "");
+ }
+ std::string LHSStemLower, RHSStemLower;
+ std::string LHSFilenameLower, RHSFilenameLower;
+ if (Style.SortIncludes.IgnoreCase) {
+ LHSStemLower = LHSStem.str().lower();
+ RHSStemLower = RHSStem.str().lower();
+ LHSFilenameLower = Includes[LHSI].Filename.lower();
+ RHSFilenameLower = Includes[RHSI].Filename.lower();
+ }
+ return std::tie(Includes[LHSI].Priority, LHSStemLower, LHSStem,
+ LHSFilenameLower, Includes[LHSI].Filename) <
+ std::tie(Includes[RHSI].Priority, RHSStemLower, RHSStem,
+ RHSFilenameLower, Includes[RHSI].Filename);
});
}
diff --git a/clang/unittests/Format/SortIncludesTest.cpp b/clang/unittests/Format/SortIncludesTest.cpp
index 994227efdd4f8..3e69ce56d98c5 100644
--- a/clang/unittests/Format/SortIncludesTest.cpp
+++ b/clang/unittests/Format/SortIncludesTest.cpp
@@ -1483,6 +1483,26 @@ TEST_F(SortIncludesTest, BlockCommentedOutIncludes) {
verifyFormat(Code, sort(Code, "input.cpp", 0));
}
+TEST_F(SortIncludesTest, IgnoreExtension) {
+ verifyFormat("#include <a-util.h>\n"
+ "#include <a.h>\n"
+ "#include <a.inc>",
+ sort("#include <a.inc>\n"
+ "#include <a-util.h>\n"
+ "#include <a.h>",
+ "input.h", 1));
+
+ FmtStyle.SortIncludes.IgnoreExtension = true;
+
+ verifyFormat("#include <a.h>\n"
+ "#include <a.inc>\n"
+ "#include <a-util.h>",
+ sort("#include <a.inc>\n"
+ "#include <a-util.h>\n"
+ "#include <a.h>",
+ "input.h", 1));
+}
+
} // end namespace
} // end namespace format
} // end namespace clang
More information about the cfe-commits
mailing list