[clang] 984dd15 - [clang-format] Add MainIncludeChar option. (#78752)

via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 6 01:06:37 PST 2024


Author: j-jorge
Date: 2024-02-06T01:06:33-08:00
New Revision: 984dd15d4da33337b2800d4776aa8ecc168b145e

URL: https://github.com/llvm/llvm-project/commit/984dd15d4da33337b2800d4776aa8ecc168b145e
DIFF: https://github.com/llvm/llvm-project/commit/984dd15d4da33337b2800d4776aa8ecc168b145e.diff

LOG: [clang-format] Add MainIncludeChar option. (#78752)

Resolves #27008, #39735, #53013, #63619.

Hello, this PR adds the MainIncludeChar option to clang-format, allowing
to select which include syntax must be considered when searching for the
main header: quotes (`#include "foo.hpp"`, the default), brackets
(`#include <foo.hpp>`), or both.

The lack of support for brackets has been reported many times, see the
linked issues, so I am pretty sure there is a need for it :)

A short note about why I did not implement a regex approach as discussed
in #53013: while a regex would have allowed many extra ways to describe
the main header, the bug descriptions listed above suggest a very simple
need: support brackets for the main header. This PR answers this needs
in a quite simple way, with a very simple style option. IMHO the feature
space covered by the regex (again, for which there is no demand :)) can
be implemented latter, in addition to the proposed option.

The PR also includes tests for the option with and without grouped
includes.

Added: 
    

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

Removed: 
    


################################################################################
diff  --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 976d9e2716ef3..f86be2c1246fb 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -4142,6 +4142,25 @@ the configuration (without a prefix: ``Auto``).
      A(z); -> z;
      A(a, b); // will not be expanded.
 
+.. _MainIncludeChar:
+
+**MainIncludeChar** (``MainIncludeCharDiscriminator``) :versionbadge:`clang-format 18` :ref:`¶ <MainIncludeChar>`
+  When guessing whether a #include is the "main" include, only the include
+  directives that use the specified character are considered.
+
+  Possible values:
+
+  * ``MICD_Quote`` (in configuration: ``Quote``)
+    Main include uses quotes: ``#include "foo.hpp"`` (the default).
+
+  * ``MICD_AngleBracket`` (in configuration: ``AngleBracket``)
+    Main include uses angle brackets: ``#include <foo.hpp>``.
+
+  * ``MICD_Any`` (in configuration: ``Any``)
+    Main include uses either quotes or angle brackets.
+
+
+
 .. _MaxEmptyLinesToKeep:
 
 **MaxEmptyLinesToKeep** (``Unsigned``) :versionbadge:`clang-format 3.7` :ref:`¶ <MaxEmptyLinesToKeep>`

diff  --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 2ca80a7889f8c..415321310c24f 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -4846,6 +4846,7 @@ struct FormatStyle {
                R.IncludeStyle.IncludeIsMainRegex &&
            IncludeStyle.IncludeIsMainSourceRegex ==
                R.IncludeStyle.IncludeIsMainSourceRegex &&
+           IncludeStyle.MainIncludeChar == R.IncludeStyle.MainIncludeChar &&
            IndentAccessModifiers == R.IndentAccessModifiers &&
            IndentCaseBlocks == R.IndentCaseBlocks &&
            IndentCaseLabels == R.IndentCaseLabels &&

diff  --git a/clang/include/clang/Tooling/Inclusions/IncludeStyle.h b/clang/include/clang/Tooling/Inclusions/IncludeStyle.h
index d6b2b0192477d..c91e4a6b0ac54 100644
--- a/clang/include/clang/Tooling/Inclusions/IncludeStyle.h
+++ b/clang/include/clang/Tooling/Inclusions/IncludeStyle.h
@@ -151,6 +151,21 @@ struct IncludeStyle {
   /// before any other include.
   /// \version 10
   std::string IncludeIsMainSourceRegex;
+
+  /// Character to consider in the include directives for the main header.
+  enum MainIncludeCharDiscriminator : int8_t {
+    /// Main include uses quotes: ``#include "foo.hpp"`` (the default).
+    MICD_Quote,
+    /// Main include uses angle brackets: ``#include <foo.hpp>``.
+    MICD_AngleBracket,
+    /// Main include uses either quotes or angle brackets.
+    MICD_Any
+  };
+
+  /// When guessing whether a #include is the "main" include, only the include
+  /// directives that use the specified character are considered.
+  /// \version 18
+  MainIncludeCharDiscriminator MainIncludeChar;
 };
 
 } // namespace tooling
@@ -174,6 +189,14 @@ struct ScalarEnumerationTraits<
   enumeration(IO &IO, clang::tooling::IncludeStyle::IncludeBlocksStyle &Value);
 };
 
+template <>
+struct ScalarEnumerationTraits<
+    clang::tooling::IncludeStyle::MainIncludeCharDiscriminator> {
+  static void enumeration(
+      IO &IO,
+      clang::tooling::IncludeStyle::MainIncludeCharDiscriminator &Value);
+};
+
 } // namespace yaml
 } // namespace llvm
 

diff  --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 01d6e9aca0d29..9c780cd7a5f4e 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -1018,6 +1018,7 @@ template <> struct MappingTraits<FormatStyle> {
     IO.mapOptional("MacroBlockBegin", Style.MacroBlockBegin);
     IO.mapOptional("MacroBlockEnd", Style.MacroBlockEnd);
     IO.mapOptional("Macros", Style.Macros);
+    IO.mapOptional("MainIncludeChar", Style.IncludeStyle.MainIncludeChar);
     IO.mapOptional("MaxEmptyLinesToKeep", Style.MaxEmptyLinesToKeep);
     IO.mapOptional("NamespaceIndentation", Style.NamespaceIndentation);
     IO.mapOptional("NamespaceMacros", Style.NamespaceMacros);
@@ -1496,6 +1497,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
       {".*", 1, 0, false}};
   LLVMStyle.IncludeStyle.IncludeIsMainRegex = "(Test)?$";
   LLVMStyle.IncludeStyle.IncludeBlocks = tooling::IncludeStyle::IBS_Preserve;
+  LLVMStyle.IncludeStyle.MainIncludeChar = tooling::IncludeStyle::MICD_Quote;
   LLVMStyle.IndentAccessModifiers = false;
   LLVMStyle.IndentCaseLabels = false;
   LLVMStyle.IndentCaseBlocks = false;

diff  --git a/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp b/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp
index d275222ac6b58..4313da66efc0b 100644
--- a/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp
+++ b/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp
@@ -234,8 +234,18 @@ int IncludeCategoryManager::getSortIncludePriority(StringRef IncludeName,
   return Ret;
 }
 bool IncludeCategoryManager::isMainHeader(StringRef IncludeName) const {
-  if (!IncludeName.starts_with("\""))
-    return false;
+  switch (Style.MainIncludeChar) {
+  case IncludeStyle::MICD_Quote:
+    if (!IncludeName.starts_with("\""))
+      return false;
+    break;
+  case IncludeStyle::MICD_AngleBracket:
+    if (!IncludeName.starts_with("<"))
+      return false;
+    break;
+  case IncludeStyle::MICD_Any:
+    break;
+  }
 
   IncludeName =
       IncludeName.drop_front(1).drop_back(1); // remove the surrounding "" or <>

diff  --git a/clang/lib/Tooling/Inclusions/IncludeStyle.cpp b/clang/lib/Tooling/Inclusions/IncludeStyle.cpp
index da5bb00d1013a..05dfb50589de0 100644
--- a/clang/lib/Tooling/Inclusions/IncludeStyle.cpp
+++ b/clang/lib/Tooling/Inclusions/IncludeStyle.cpp
@@ -28,5 +28,12 @@ void ScalarEnumerationTraits<IncludeStyle::IncludeBlocksStyle>::enumeration(
   IO.enumCase(Value, "Regroup", IncludeStyle::IBS_Regroup);
 }
 
+void ScalarEnumerationTraits<IncludeStyle::MainIncludeCharDiscriminator>::
+    enumeration(IO &IO, IncludeStyle::MainIncludeCharDiscriminator &Value) {
+  IO.enumCase(Value, "Quote", IncludeStyle::MICD_Quote);
+  IO.enumCase(Value, "AngleBracket", IncludeStyle::MICD_AngleBracket);
+  IO.enumCase(Value, "Any", IncludeStyle::MICD_Any);
+}
+
 } // namespace yaml
 } // namespace llvm

diff  --git a/clang/unittests/Format/SortIncludesTest.cpp b/clang/unittests/Format/SortIncludesTest.cpp
index ec142e03b1285..772eb53806b4b 100644
--- a/clang/unittests/Format/SortIncludesTest.cpp
+++ b/clang/unittests/Format/SortIncludesTest.cpp
@@ -976,6 +976,112 @@ TEST_F(SortIncludesTest,
   EXPECT_EQ(Code, sort(Code, "input.h", 0));
 }
 
+TEST_F(SortIncludesTest, MainIncludeChar) {
+  std::string Code = "#include <a>\n"
+                     "#include \"quote/input.h\"\n"
+                     "#include <angle-bracket/input.h>\n";
+
+  // Default behavior
+  EXPECT_EQ("#include \"quote/input.h\"\n"
+            "#include <a>\n"
+            "#include <angle-bracket/input.h>\n",
+            sort(Code, "input.cc", 1));
+
+  Style.MainIncludeChar = tooling::IncludeStyle::MICD_Quote;
+  EXPECT_EQ("#include \"quote/input.h\"\n"
+            "#include <a>\n"
+            "#include <angle-bracket/input.h>\n",
+            sort(Code, "input.cc", 1));
+
+  Style.MainIncludeChar = tooling::IncludeStyle::MICD_AngleBracket;
+  EXPECT_EQ("#include <angle-bracket/input.h>\n"
+            "#include \"quote/input.h\"\n"
+            "#include <a>\n",
+            sort(Code, "input.cc", 1));
+}
+
+TEST_F(SortIncludesTest, MainIncludeCharAnyPickQuote) {
+  Style.MainIncludeChar = tooling::IncludeStyle::MICD_Any;
+  EXPECT_EQ("#include \"input.h\"\n"
+            "#include <a>\n"
+            "#include <b>\n",
+            sort("#include <a>\n"
+                 "#include \"input.h\"\n"
+                 "#include <b>\n",
+                 "input.cc", 1));
+}
+
+TEST_F(SortIncludesTest, MainIncludeCharAnyPickAngleBracket) {
+  Style.MainIncludeChar = tooling::IncludeStyle::MICD_Any;
+  EXPECT_EQ("#include <input.h>\n"
+            "#include <a>\n"
+            "#include <b>\n",
+            sort("#include <a>\n"
+                 "#include <input.h>\n"
+                 "#include <b>\n",
+                 "input.cc", 1));
+}
+
+TEST_F(SortIncludesTest, MainIncludeCharQuoteAndRegroup) {
+  Style.IncludeCategories = {
+      {"lib-a", 1, 0, false}, {"lib-b", 2, 0, false}, {"lib-c", 3, 0, false}};
+  Style.IncludeBlocks = tooling::IncludeStyle::IBS_Regroup;
+  Style.MainIncludeChar = tooling::IncludeStyle::MICD_Quote;
+
+  EXPECT_EQ("#include \"lib-b/input.h\"\n"
+            "\n"
+            "#include <lib-a/h-1.h>\n"
+            "#include <lib-a/h-3.h>\n"
+            "#include <lib-a/input.h>\n"
+            "\n"
+            "#include <lib-b/h-1.h>\n"
+            "#include <lib-b/h-3.h>\n"
+            "\n"
+            "#include <lib-c/h-1.h>\n"
+            "#include <lib-c/h-2.h>\n"
+            "#include <lib-c/h-3.h>\n",
+            sort("#include <lib-c/h-1.h>\n"
+                 "#include <lib-c/h-2.h>\n"
+                 "#include <lib-c/h-3.h>\n"
+                 "#include <lib-b/h-1.h>\n"
+                 "#include \"lib-b/input.h\"\n"
+                 "#include <lib-b/h-3.h>\n"
+                 "#include <lib-a/h-1.h>\n"
+                 "#include <lib-a/input.h>\n"
+                 "#include <lib-a/h-3.h>\n",
+                 "input.cc"));
+}
+
+TEST_F(SortIncludesTest, MainIncludeCharAngleBracketAndRegroup) {
+  Style.IncludeCategories = {
+      {"lib-a", 1, 0, false}, {"lib-b", 2, 0, false}, {"lib-c", 3, 0, false}};
+  Style.IncludeBlocks = tooling::IncludeStyle::IBS_Regroup;
+  Style.MainIncludeChar = tooling::IncludeStyle::MICD_AngleBracket;
+
+  EXPECT_EQ("#include <lib-a/input.h>\n"
+            "\n"
+            "#include <lib-a/h-1.h>\n"
+            "#include <lib-a/h-3.h>\n"
+            "\n"
+            "#include \"lib-b/input.h\"\n"
+            "#include <lib-b/h-1.h>\n"
+            "#include <lib-b/h-3.h>\n"
+            "\n"
+            "#include <lib-c/h-1.h>\n"
+            "#include <lib-c/h-2.h>\n"
+            "#include <lib-c/h-3.h>\n",
+            sort("#include <lib-c/h-1.h>\n"
+                 "#include <lib-c/h-2.h>\n"
+                 "#include <lib-c/h-3.h>\n"
+                 "#include <lib-b/h-1.h>\n"
+                 "#include \"lib-b/input.h\"\n"
+                 "#include <lib-b/h-3.h>\n"
+                 "#include <lib-a/h-1.h>\n"
+                 "#include <lib-a/input.h>\n"
+                 "#include <lib-a/h-3.h>\n",
+                 "input.cc"));
+}
+
 TEST_F(SortIncludesTest, DoNotRegroupGroupsInGoogleObjCStyle) {
   FmtStyle = getGoogleStyle(FormatStyle::LK_ObjC);
 


        


More information about the cfe-commits mailing list