Commit 3395a336 authored by Lukas Barth's avatar Lukas Barth Committed by Marek Kurdej
Browse files

[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
parent 9390b85a
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -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.

+20 −0
Original line number Diff line number Diff line
@@ -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
+20 −4
Original line number Diff line number Diff line
@@ -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);
  }

  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;
+2 −0
Original line number Diff line number Diff line
@@ -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);
+43 −0
Original line number Diff line number Diff line
@@ -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))?$";