Skip to content

Commit 984dd15

Browse files
authored
[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.
1 parent 933247d commit 984dd15

File tree

7 files changed

+170
-2
lines changed

7 files changed

+170
-2
lines changed

clang/docs/ClangFormatStyleOptions.rst

+19
Original file line numberDiff line numberDiff line change
@@ -4142,6 +4142,25 @@ the configuration (without a prefix: ``Auto``).
41424142
A(z); -> z;
41434143
A(a, b); // will not be expanded.
41444144

4145+
.. _MainIncludeChar:
4146+
4147+
**MainIncludeChar** (``MainIncludeCharDiscriminator``) :versionbadge:`clang-format 18` :ref:`<MainIncludeChar>`
4148+
When guessing whether a #include is the "main" include, only the include
4149+
directives that use the specified character are considered.
4150+
4151+
Possible values:
4152+
4153+
* ``MICD_Quote`` (in configuration: ``Quote``)
4154+
Main include uses quotes: ``#include "foo.hpp"`` (the default).
4155+
4156+
* ``MICD_AngleBracket`` (in configuration: ``AngleBracket``)
4157+
Main include uses angle brackets: ``#include <foo.hpp>``.
4158+
4159+
* ``MICD_Any`` (in configuration: ``Any``)
4160+
Main include uses either quotes or angle brackets.
4161+
4162+
4163+
41454164
.. _MaxEmptyLinesToKeep:
41464165

41474166
**MaxEmptyLinesToKeep** (``Unsigned``) :versionbadge:`clang-format 3.7` :ref:`<MaxEmptyLinesToKeep>`

clang/include/clang/Format/Format.h

+1
Original file line numberDiff line numberDiff line change
@@ -4846,6 +4846,7 @@ struct FormatStyle {
48464846
R.IncludeStyle.IncludeIsMainRegex &&
48474847
IncludeStyle.IncludeIsMainSourceRegex ==
48484848
R.IncludeStyle.IncludeIsMainSourceRegex &&
4849+
IncludeStyle.MainIncludeChar == R.IncludeStyle.MainIncludeChar &&
48494850
IndentAccessModifiers == R.IndentAccessModifiers &&
48504851
IndentCaseBlocks == R.IndentCaseBlocks &&
48514852
IndentCaseLabels == R.IndentCaseLabels &&

clang/include/clang/Tooling/Inclusions/IncludeStyle.h

+23
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,21 @@ struct IncludeStyle {
151151
/// before any other include.
152152
/// \version 10
153153
std::string IncludeIsMainSourceRegex;
154+
155+
/// Character to consider in the include directives for the main header.
156+
enum MainIncludeCharDiscriminator : int8_t {
157+
/// Main include uses quotes: ``#include "foo.hpp"`` (the default).
158+
MICD_Quote,
159+
/// Main include uses angle brackets: ``#include <foo.hpp>``.
160+
MICD_AngleBracket,
161+
/// Main include uses either quotes or angle brackets.
162+
MICD_Any
163+
};
164+
165+
/// When guessing whether a #include is the "main" include, only the include
166+
/// directives that use the specified character are considered.
167+
/// \version 18
168+
MainIncludeCharDiscriminator MainIncludeChar;
154169
};
155170

156171
} // namespace tooling
@@ -174,6 +189,14 @@ struct ScalarEnumerationTraits<
174189
enumeration(IO &IO, clang::tooling::IncludeStyle::IncludeBlocksStyle &Value);
175190
};
176191

192+
template <>
193+
struct ScalarEnumerationTraits<
194+
clang::tooling::IncludeStyle::MainIncludeCharDiscriminator> {
195+
static void enumeration(
196+
IO &IO,
197+
clang::tooling::IncludeStyle::MainIncludeCharDiscriminator &Value);
198+
};
199+
177200
} // namespace yaml
178201
} // namespace llvm
179202

clang/lib/Format/Format.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,7 @@ template <> struct MappingTraits<FormatStyle> {
10181018
IO.mapOptional("MacroBlockBegin", Style.MacroBlockBegin);
10191019
IO.mapOptional("MacroBlockEnd", Style.MacroBlockEnd);
10201020
IO.mapOptional("Macros", Style.Macros);
1021+
IO.mapOptional("MainIncludeChar", Style.IncludeStyle.MainIncludeChar);
10211022
IO.mapOptional("MaxEmptyLinesToKeep", Style.MaxEmptyLinesToKeep);
10221023
IO.mapOptional("NamespaceIndentation", Style.NamespaceIndentation);
10231024
IO.mapOptional("NamespaceMacros", Style.NamespaceMacros);
@@ -1496,6 +1497,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
14961497
{".*", 1, 0, false}};
14971498
LLVMStyle.IncludeStyle.IncludeIsMainRegex = "(Test)?$";
14981499
LLVMStyle.IncludeStyle.IncludeBlocks = tooling::IncludeStyle::IBS_Preserve;
1500+
LLVMStyle.IncludeStyle.MainIncludeChar = tooling::IncludeStyle::MICD_Quote;
14991501
LLVMStyle.IndentAccessModifiers = false;
15001502
LLVMStyle.IndentCaseLabels = false;
15011503
LLVMStyle.IndentCaseBlocks = false;

clang/lib/Tooling/Inclusions/HeaderIncludes.cpp

+12-2
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,18 @@ int IncludeCategoryManager::getSortIncludePriority(StringRef IncludeName,
234234
return Ret;
235235
}
236236
bool IncludeCategoryManager::isMainHeader(StringRef IncludeName) const {
237-
if (!IncludeName.starts_with("\""))
238-
return false;
237+
switch (Style.MainIncludeChar) {
238+
case IncludeStyle::MICD_Quote:
239+
if (!IncludeName.starts_with("\""))
240+
return false;
241+
break;
242+
case IncludeStyle::MICD_AngleBracket:
243+
if (!IncludeName.starts_with("<"))
244+
return false;
245+
break;
246+
case IncludeStyle::MICD_Any:
247+
break;
248+
}
239249

240250
IncludeName =
241251
IncludeName.drop_front(1).drop_back(1); // remove the surrounding "" or <>

clang/lib/Tooling/Inclusions/IncludeStyle.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,12 @@ void ScalarEnumerationTraits<IncludeStyle::IncludeBlocksStyle>::enumeration(
2828
IO.enumCase(Value, "Regroup", IncludeStyle::IBS_Regroup);
2929
}
3030

31+
void ScalarEnumerationTraits<IncludeStyle::MainIncludeCharDiscriminator>::
32+
enumeration(IO &IO, IncludeStyle::MainIncludeCharDiscriminator &Value) {
33+
IO.enumCase(Value, "Quote", IncludeStyle::MICD_Quote);
34+
IO.enumCase(Value, "AngleBracket", IncludeStyle::MICD_AngleBracket);
35+
IO.enumCase(Value, "Any", IncludeStyle::MICD_Any);
36+
}
37+
3138
} // namespace yaml
3239
} // namespace llvm

clang/unittests/Format/SortIncludesTest.cpp

+106
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,112 @@ TEST_F(SortIncludesTest,
976976
EXPECT_EQ(Code, sort(Code, "input.h", 0));
977977
}
978978

979+
TEST_F(SortIncludesTest, MainIncludeChar) {
980+
std::string Code = "#include <a>\n"
981+
"#include \"quote/input.h\"\n"
982+
"#include <angle-bracket/input.h>\n";
983+
984+
// Default behavior
985+
EXPECT_EQ("#include \"quote/input.h\"\n"
986+
"#include <a>\n"
987+
"#include <angle-bracket/input.h>\n",
988+
sort(Code, "input.cc", 1));
989+
990+
Style.MainIncludeChar = tooling::IncludeStyle::MICD_Quote;
991+
EXPECT_EQ("#include \"quote/input.h\"\n"
992+
"#include <a>\n"
993+
"#include <angle-bracket/input.h>\n",
994+
sort(Code, "input.cc", 1));
995+
996+
Style.MainIncludeChar = tooling::IncludeStyle::MICD_AngleBracket;
997+
EXPECT_EQ("#include <angle-bracket/input.h>\n"
998+
"#include \"quote/input.h\"\n"
999+
"#include <a>\n",
1000+
sort(Code, "input.cc", 1));
1001+
}
1002+
1003+
TEST_F(SortIncludesTest, MainIncludeCharAnyPickQuote) {
1004+
Style.MainIncludeChar = tooling::IncludeStyle::MICD_Any;
1005+
EXPECT_EQ("#include \"input.h\"\n"
1006+
"#include <a>\n"
1007+
"#include <b>\n",
1008+
sort("#include <a>\n"
1009+
"#include \"input.h\"\n"
1010+
"#include <b>\n",
1011+
"input.cc", 1));
1012+
}
1013+
1014+
TEST_F(SortIncludesTest, MainIncludeCharAnyPickAngleBracket) {
1015+
Style.MainIncludeChar = tooling::IncludeStyle::MICD_Any;
1016+
EXPECT_EQ("#include <input.h>\n"
1017+
"#include <a>\n"
1018+
"#include <b>\n",
1019+
sort("#include <a>\n"
1020+
"#include <input.h>\n"
1021+
"#include <b>\n",
1022+
"input.cc", 1));
1023+
}
1024+
1025+
TEST_F(SortIncludesTest, MainIncludeCharQuoteAndRegroup) {
1026+
Style.IncludeCategories = {
1027+
{"lib-a", 1, 0, false}, {"lib-b", 2, 0, false}, {"lib-c", 3, 0, false}};
1028+
Style.IncludeBlocks = tooling::IncludeStyle::IBS_Regroup;
1029+
Style.MainIncludeChar = tooling::IncludeStyle::MICD_Quote;
1030+
1031+
EXPECT_EQ("#include \"lib-b/input.h\"\n"
1032+
"\n"
1033+
"#include <lib-a/h-1.h>\n"
1034+
"#include <lib-a/h-3.h>\n"
1035+
"#include <lib-a/input.h>\n"
1036+
"\n"
1037+
"#include <lib-b/h-1.h>\n"
1038+
"#include <lib-b/h-3.h>\n"
1039+
"\n"
1040+
"#include <lib-c/h-1.h>\n"
1041+
"#include <lib-c/h-2.h>\n"
1042+
"#include <lib-c/h-3.h>\n",
1043+
sort("#include <lib-c/h-1.h>\n"
1044+
"#include <lib-c/h-2.h>\n"
1045+
"#include <lib-c/h-3.h>\n"
1046+
"#include <lib-b/h-1.h>\n"
1047+
"#include \"lib-b/input.h\"\n"
1048+
"#include <lib-b/h-3.h>\n"
1049+
"#include <lib-a/h-1.h>\n"
1050+
"#include <lib-a/input.h>\n"
1051+
"#include <lib-a/h-3.h>\n",
1052+
"input.cc"));
1053+
}
1054+
1055+
TEST_F(SortIncludesTest, MainIncludeCharAngleBracketAndRegroup) {
1056+
Style.IncludeCategories = {
1057+
{"lib-a", 1, 0, false}, {"lib-b", 2, 0, false}, {"lib-c", 3, 0, false}};
1058+
Style.IncludeBlocks = tooling::IncludeStyle::IBS_Regroup;
1059+
Style.MainIncludeChar = tooling::IncludeStyle::MICD_AngleBracket;
1060+
1061+
EXPECT_EQ("#include <lib-a/input.h>\n"
1062+
"\n"
1063+
"#include <lib-a/h-1.h>\n"
1064+
"#include <lib-a/h-3.h>\n"
1065+
"\n"
1066+
"#include \"lib-b/input.h\"\n"
1067+
"#include <lib-b/h-1.h>\n"
1068+
"#include <lib-b/h-3.h>\n"
1069+
"\n"
1070+
"#include <lib-c/h-1.h>\n"
1071+
"#include <lib-c/h-2.h>\n"
1072+
"#include <lib-c/h-3.h>\n",
1073+
sort("#include <lib-c/h-1.h>\n"
1074+
"#include <lib-c/h-2.h>\n"
1075+
"#include <lib-c/h-3.h>\n"
1076+
"#include <lib-b/h-1.h>\n"
1077+
"#include \"lib-b/input.h\"\n"
1078+
"#include <lib-b/h-3.h>\n"
1079+
"#include <lib-a/h-1.h>\n"
1080+
"#include <lib-a/input.h>\n"
1081+
"#include <lib-a/h-3.h>\n",
1082+
"input.cc"));
1083+
}
1084+
9791085
TEST_F(SortIncludesTest, DoNotRegroupGroupsInGoogleObjCStyle) {
9801086
FmtStyle = getGoogleStyle(FormatStyle::LK_ObjC);
9811087

0 commit comments

Comments
 (0)