[llvm-branch-commits] [clang] 3395a33 - [clang-format] add case aware include sorting

Marek Kurdej via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Jan 25 09:57:39 PST 2021


Author: Lukas Barth
Date: 2021-01-25T18:53:22+01:00
New Revision: 3395a336b02538d0bb768ccfae11c9b6151b102e

URL: https://github.com/llvm/llvm-project/commit/3395a336b02538d0bb768ccfae11c9b6151b102e
DIFF: https://github.com/llvm/llvm-project/commit/3395a336b02538d0bb768ccfae11c9b6151b102e.diff

LOG: [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

Added: 
    

Modified: 
    clang/docs/ClangFormatStyleOptions.rst
    clang/include/clang/Tooling/Inclusions/IncludeStyle.h
    clang/lib/Format/Format.cpp
    clang/unittests/Format/FormatTest.cpp
    clang/unittests/Format/SortIncludesTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 3141dd5510fc..d5ce1b7b2e8e 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -2282,6 +2282,26 @@ the configuration (without a prefix: ``Auto``).
   ``ClassImpl.hpp`` would not have the main include file put on top
   before any other include.
 
+**IncludeSortAlphabetically** (``bool``)
+  Specify if sorting should be done in an alphabetical and
+  case sensitive fashion.
+
+  When ``false``, includes are sorted in an ASCIIbetical
+  fashion.
+  When ``true``, includes are sorted in an alphabetical
+  fashion with case used as a tie-breaker.
+
+  .. code-block:: c++
+
+     false:                                   true:
+     #include "A/B.h"               vs.       #include "A/B.h"
+     #include "A/b.h"                         #include "A/b.h"
+     #include "B/A.h"                         #include "a/b.h"
+     #include "B/a.h"                         #include "B/A.h"
+     #include "a/b.h"                         #include "B/a.h"
+
+  This option is off by default.
+
 **IndentCaseBlocks** (``bool``)
   Indent case label blocks one level from the case label.
 

diff  --git a/clang/include/clang/Tooling/Inclusions/IncludeStyle.h b/clang/include/clang/Tooling/Inclusions/IncludeStyle.h
index 4caaf4121f15..652a7b61a0a4 100644
--- a/clang/include/clang/Tooling/Inclusions/IncludeStyle.h
+++ b/clang/include/clang/Tooling/Inclusions/IncludeStyle.h
@@ -147,6 +147,26 @@ struct IncludeStyle {
   /// ``ClassImpl.hpp`` would not have the main include file put on top
   /// before any other include.
   std::string IncludeIsMainSourceRegex;
+
+  /// Specify if sorting should be done in an alphabetical and
+  /// case sensitive fashion.
+  ///
+  /// When ``false``, includes are sorted in an ASCIIbetical
+  /// fashion.
+  /// When ``true``, includes are sorted in an alphabetical
+  /// fashion with case used as a tie-breaker.
+  ///
+  /// \code
+  ///   false:                                   true:
+  ///   #include "A/B.h"               vs.       #include "A/B.h"
+  ///   #include "A/b.h"                         #include "A/b.h"
+  ///   #include "B/A.h"                         #include "a/b.h"
+  ///   #include "B/a.h"                         #include "B/A.h"
+  ///   #include "a/b.h"                         #include "B/a.h"
+  /// \endcode
+  ///
+  /// This option is off by default.
+  bool IncludeSortAlphabetically;
 };
 
 } // namespace tooling

diff  --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index cd1c6e4f6023..0986ef845a4e 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -571,6 +571,8 @@ template <> struct MappingTraits<FormatStyle> {
     IO.mapOptional("IncludeIsMainRegex", Style.IncludeStyle.IncludeIsMainRegex);
     IO.mapOptional("IncludeIsMainSourceRegex",
                    Style.IncludeStyle.IncludeIsMainSourceRegex);
+    IO.mapOptional("IncludeSortAlphabetically",
+                   Style.IncludeStyle.IncludeSortAlphabetically);
     IO.mapOptional("IndentCaseLabels", Style.IndentCaseLabels);
     IO.mapOptional("IndentCaseBlocks", Style.IndentCaseBlocks);
     IO.mapOptional("IndentGotoLabels", Style.IndentGotoLabels);
@@ -940,6 +942,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
       {".*", 1, 0, false}};
   LLVMStyle.IncludeStyle.IncludeIsMainRegex = "(Test)?$";
   LLVMStyle.IncludeStyle.IncludeBlocks = tooling::IncludeStyle::IBS_Preserve;
+  LLVMStyle.IncludeStyle.IncludeSortAlphabetically = false;
   LLVMStyle.IndentCaseLabels = false;
   LLVMStyle.IndentCaseBlocks = false;
   LLVMStyle.IndentGotoLabels = true;
@@ -2194,10 +2197,23 @@ static void sortCppIncludes(const FormatStyle &Style,
   for (unsigned i = 0, e = Includes.size(); i != e; ++i) {
     Indices.push_back(i);
   }
-  llvm::stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) {
-    return std::tie(Includes[LHSI].Priority, Includes[LHSI].Filename) <
-           std::tie(Includes[RHSI].Priority, Includes[RHSI].Filename);
-  });
+
+  if (Style.IncludeStyle.IncludeSortAlphabetically) {
+    llvm::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 {
+    llvm::stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) {
+      return std::tie(Includes[LHSI].Priority, Includes[LHSI].Filename) <
+             std::tie(Includes[RHSI].Priority, Includes[RHSI].Filename);
+    });
+  }
+
   // The index of the include on which the cursor will be put after
   // sorting/deduplicating.
   unsigned CursorIndex;

diff  --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 0c37d1188f15..cbb368d4f605 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -15145,6 +15145,8 @@ TEST_F(FormatTest, ParsesConfigurationBools) {
   CHECK_PARSE_BOOL(DeriveLineEnding);
   CHECK_PARSE_BOOL(DerivePointerAlignment);
   CHECK_PARSE_BOOL_FIELD(DerivePointerAlignment, "DerivePointerBinding");
+  CHECK_PARSE_BOOL_FIELD(IncludeStyle.IncludeSortAlphabetically,
+                         "IncludeSortAlphabetically");
   CHECK_PARSE_BOOL(DisableFormat);
   CHECK_PARSE_BOOL(IndentCaseLabels);
   CHECK_PARSE_BOOL(IndentCaseBlocks);

diff  --git a/clang/unittests/Format/SortIncludesTest.cpp b/clang/unittests/Format/SortIncludesTest.cpp
index 41ff7afad10e..a506218f537d 100644
--- a/clang/unittests/Format/SortIncludesTest.cpp
+++ b/clang/unittests/Format/SortIncludesTest.cpp
@@ -598,6 +598,49 @@ TEST_F(SortIncludesTest, MainHeaderIsSeparatedWhenRegroupping) {
                  "a.cc"));
 }
 
+TEST_F(SortIncludesTest, SupportOptionalAlphabeticalSorting) {
+  EXPECT_FALSE(Style.IncludeSortAlphabetically);
+
+  Style.IncludeSortAlphabetically = true;
+
+  EXPECT_EQ("#include \"A/B.h\"\n"
+            "#include \"A/b.h\"\n"
+            "#include \"a/b.h\"\n"
+            "#include \"B/A.h\"\n"
+            "#include \"B/a.h\"\n",
+            sort("#include \"B/a.h\"\n"
+                 "#include \"B/A.h\"\n"
+                 "#include \"A/B.h\"\n"
+                 "#include \"a/b.h\"\n"
+                 "#include \"A/b.h\"\n",
+                 "a.h"));
+
+  Style.IncludeBlocks = clang::tooling::IncludeStyle::IBS_Regroup;
+  Style.IncludeCategories = {
+      {"^\"", 1, 0, false}, {"^<.*\\.h>$", 2, 0, false}, {"^<", 3, 0, false}};
+
+  StringRef UnsortedCode = "#include \"qt.h\"\n"
+                           "#include <algorithm>\n"
+                           "#include <qtwhatever.h>\n"
+                           "#include <Qtwhatever.h>\n"
+                           "#include <Algorithm>\n"
+                           "#include \"vlib.h\"\n"
+                           "#include \"Vlib.h\"\n"
+                           "#include \"AST.h\"\n";
+
+  EXPECT_EQ("#include \"AST.h\"\n"
+            "#include \"qt.h\"\n"
+            "#include \"Vlib.h\"\n"
+            "#include \"vlib.h\"\n"
+            "\n"
+            "#include <Qtwhatever.h>\n"
+            "#include <qtwhatever.h>\n"
+            "\n"
+            "#include <Algorithm>\n"
+            "#include <algorithm>\n",
+            sort(UnsortedCode));
+}
+
 TEST_F(SortIncludesTest, SupportCaseInsensitiveMatching) {
   // Setup an regex for main includes so we can cover those as well.
   Style.IncludeIsMainRegex = "([-_](test|unittest))?$";


        


More information about the llvm-branch-commits mailing list