diff options
author | Daan De Meyer <daan.j.demeyer@gmail.com> | 2025-07-17 09:36:52 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-07-17 00:36:52 -0700 |
commit | 1653a093def10543d8f958fe7d4cde9ab1dd8bca (patch) | |
tree | 8d8d5a7b48603f0644c0dde5c300878e44fb917d /clang | |
parent | 3cb0c7f45b97802ddc13a15560fbbca2efb75326 (diff) | |
download | llvm-1653a093def10543d8f958fe7d4cde9ab1dd8bca.zip llvm-1653a093def10543d8f958fe7d4cde9ab1dd8bca.tar.gz llvm-1653a093def10543d8f958fe7d4cde9ab1dd8bca.tar.bz2 |
[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.
Diffstat (limited to 'clang')
-rw-r--r-- | clang/docs/ClangFormatStyleOptions.rst | 10 | ||||
-rw-r--r-- | clang/include/clang/Format/Format.h | 12 | ||||
-rw-r--r-- | clang/lib/Format/Format.cpp | 45 | ||||
-rw-r--r-- | clang/unittests/Format/ConfigParseTest.cpp | 22 | ||||
-rw-r--r-- | 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 0e21ef0..bfc8094 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 b4f2a87..7677604 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 78c09be..62feb3d 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 d17109a..65d8b36 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 994227e..5194d65 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 |