https://github.com/DaanDeMeyer updated https://github.com/llvm/llvm-project/pull/137840
>From 22a095db11bf132dd9fd5c5bcc887397b2766206 Mon Sep 17 00:00:00 2001 From: Daan De Meyer <daan.j.deme...@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 | 10 +++++ clang/include/clang/Format/Format.h | 12 +++++- clang/lib/Format/Format.cpp | 45 +++++++++++++-------- clang/unittests/Format/ConfigParseTest.cpp | 22 +++++----- clang/unittests/Format/SortIncludesTest.cpp | 20 +++++++++ 5 files changed, 83 insertions(+), 26 deletions(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 0e21ef0244f78..4827cdb471690 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -6015,6 +6015,16 @@ the configuration (without a prefix: ``Auto``). #include "B/A.h" #include "B/a.h" #include "B/a.h" #include "a/b.h" + * ``bool IgnoreExtension`` 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" vs. # 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 b4f2a87fe7e83..70406d06d48c6 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -4385,8 +4385,18 @@ 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" vs. # include "A-util.h" + /// # include "A.inc" # include "A.h" + /// # include "A-util.h" # include "A.inc" + /// \endcode + 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 78c09be458f0a..62feb3db0ed5e 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -665,21 +665,25 @@ template <> struct MappingTraits<FormatStyle::SortIncludesOptions> { IO.enumCase(Value, "Never", FormatStyle::SortIncludesOptions({})); IO.enumCase(Value, "CaseInsensitive", FormatStyle::SortIncludesOptions({/*Enabled=*/true, - /*IgnoreCase=*/true})); + /*IgnoreCase=*/true, + /*IgnoreExtension=*/false})); IO.enumCase(Value, "CaseSensitive", FormatStyle::SortIncludesOptions({/*Enabled=*/true, - /*IgnoreCase=*/false})); + /*IgnoreCase=*/false, + /*IgnoreExtension=*/false})); // For backward compatibility. IO.enumCase(Value, "false", FormatStyle::SortIncludesOptions({})); IO.enumCase(Value, "true", FormatStyle::SortIncludesOptions({/*Enabled=*/true, - /*IgnoreCase=*/false})); + /*IgnoreCase=*/false, + /*IgnoreExtension=*/false})); } static void mapping(IO &IO, FormatStyle::SortIncludesOptions &Value) { IO.mapOptional("Enabled", Value.Enabled); IO.mapOptional("IgnoreCase", Value.IgnoreCase); + IO.mapOptional("IgnoreExtension", Value.IgnoreExtension); } }; @@ -1650,7 +1654,8 @@ 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; @@ -3239,19 +3244,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/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index d17109aebc0f8..65d8b36c677bd 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -259,6 +259,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) { CHECK_PARSE_NESTED_BOOL(SpacesInParensOptions, Other); CHECK_PARSE_NESTED_BOOL(SortIncludes, Enabled); CHECK_PARSE_NESTED_BOOL(SortIncludes, IgnoreCase); + CHECK_PARSE_NESTED_BOOL(SortIncludes, IgnoreExtension); } #undef CHECK_PARSE_BOOL @@ -980,17 +981,20 @@ TEST(ConfigParseTest, ParsesConfiguration) { IncludeStyle.IncludeIsMainSourceRegex, "abc$"); Style.SortIncludes = {}; - CHECK_PARSE("SortIncludes: true", SortIncludes, - FormatStyle::SortIncludesOptions( - {/*Enabled=*/true, /*IgnoreCase=*/false})); + CHECK_PARSE( + "SortIncludes: true", SortIncludes, + FormatStyle::SortIncludesOptions( + {/*Enabled=*/true, /*IgnoreCase=*/false, /*IgnoreExtension=*/false})); CHECK_PARSE("SortIncludes: false", SortIncludes, FormatStyle::SortIncludesOptions({})); - CHECK_PARSE("SortIncludes: CaseInsensitive", SortIncludes, - FormatStyle::SortIncludesOptions( - {/*Enabled=*/true, /*IgnoreCase=*/true})); - CHECK_PARSE("SortIncludes: CaseSensitive", SortIncludes, - FormatStyle::SortIncludesOptions( - {/*Enabled=*/true, /*IgnoreCase=*/false})); + CHECK_PARSE( + "SortIncludes: CaseInsensitive", SortIncludes, + FormatStyle::SortIncludesOptions( + {/*Enabled=*/true, /*IgnoreCase=*/true, /*IgnoreExtension=*/false})); + CHECK_PARSE( + "SortIncludes: CaseSensitive", SortIncludes, + FormatStyle::SortIncludesOptions( + {/*Enabled=*/true, /*IgnoreCase=*/false, /*IgnoreExtension=*/false})); CHECK_PARSE("SortIncludes: Never", SortIncludes, FormatStyle::SortIncludesOptions({})); diff --git a/clang/unittests/Format/SortIncludesTest.cpp b/clang/unittests/Format/SortIncludesTest.cpp index 994227efdd4f8..5194d65ed3637 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) { + 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")); + + verifyFormat("#include <ab.h>\n" + "#include <ab-beta.h>\n" + "#include <ab-data.h>", + sort("#include <ab-data.h>\n" + "#include <ab.h>\n" + "#include <ab-beta.h>", + "input.h")); +} + } // end namespace } // end namespace format } // end namespace clang _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits