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); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits