-
Notifications
You must be signed in to change notification settings - Fork 12.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang-format] Add an option to control indentation of export { ... }
#110381
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-format Author: None (Sirraide) Changes
I based this on the Full diff: https://github.com/llvm/llvm-project/pull/110381.diff 7 Files Affected:
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index a427d7cd40fcdd..7bdb1d3383e056 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -3828,6 +3828,21 @@ the configuration (without a prefix: ``Auto``).
This is an experimental flag, that might go away or be renamed. Do
not use this in config files, etc. Use at your own risk.
+.. _ExportBlockIndentation:
+
+**ExportBlockIndentation** (``Boolean``) :versionbadge:`clang-format 20` :ref:`¶ <ExportBlockIndentation>`
+ If ``true``, clang-format will indent the body of an ``export { ... }``
+ block. This doesn't affect the formatting of anything else related to
+ exported declarations.
+
+ .. code-block:: c++
+
+ true: false:
+ export { vs. export {
+ void foo(); void foo();
+ void bar(); void bar();
+ } }
+
.. _FixNamespaceComments:
**FixNamespaceComments** (``Boolean``) :versionbadge:`clang-format 5` :ref:`¶ <FixNamespaceComments>`
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 14907e7db18de3..2588523e326d13 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -554,6 +554,7 @@ clang-format
------------
- Adds ``BreakBinaryOperations`` option.
+- Adds the ``ExportBlockIndentation`` option.
libclang
--------
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index d8b62c7652a0f6..238a082e0f3670 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -2655,6 +2655,19 @@ struct FormatStyle {
/// \version 3.7
bool ExperimentalAutoDetectBinPacking;
+ /// If ``true``, clang-format will indent the body of an ``export { ... }``
+ /// block. This doesn't affect the formatting of anything else related to
+ /// exported declarations.
+ /// \code
+ /// true: false:
+ /// export { vs. export {
+ /// void foo(); void foo();
+ /// void bar(); void bar();
+ /// } }
+ /// \endcode
+ /// \version 20
+ bool ExportBlockIndentation;
+
/// If ``true``, clang-format adds missing namespace end comments for
/// namespaces and fixes invalid existing ones. This doesn't affect short
/// namespaces, which are controlled by ``ShortNamespaceLines``.
@@ -5131,6 +5144,7 @@ struct FormatStyle {
EmptyLineBeforeAccessModifier == R.EmptyLineBeforeAccessModifier &&
ExperimentalAutoDetectBinPacking ==
R.ExperimentalAutoDetectBinPacking &&
+ ExportBlockIndentation == R.ExportBlockIndentation &&
FixNamespaceComments == R.FixNamespaceComments &&
ForEachMacros == R.ForEachMacros &&
IncludeStyle.IncludeBlocks == R.IncludeStyle.IncludeBlocks &&
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index d2463b892fbb96..ee584b1ad0d1e5 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -1016,6 +1016,7 @@ template <> struct MappingTraits<FormatStyle> {
Style.EmptyLineBeforeAccessModifier);
IO.mapOptional("ExperimentalAutoDetectBinPacking",
Style.ExperimentalAutoDetectBinPacking);
+ IO.mapOptional("ExportBlockIndentation", Style.ExportBlockIndentation);
IO.mapOptional("FixNamespaceComments", Style.FixNamespaceComments);
IO.mapOptional("ForEachMacros", Style.ForEachMacros);
IO.mapOptional("IfMacros", Style.IfMacros);
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index 40f77266fabdca..306747290c0a7a 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -1616,6 +1616,10 @@ void UnwrappedLineParser::parseStructuralElement(
parseNamespace();
return;
}
+ if (FormatTok->is(tok::l_brace)) {
+ parseCXXExportBlock();
+ return;
+ }
if (FormatTok->is(Keywords.kw_import) && parseModuleImport())
return;
}
@@ -3075,6 +3079,26 @@ void UnwrappedLineParser::parseTryCatch() {
addUnwrappedLine();
}
+void UnwrappedLineParser::parseNamespaceOrExportBlock(unsigned AddLevels) {
+ bool ManageWhitesmithsBraces =
+ AddLevels == 0u && Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths;
+
+ // If we're in Whitesmiths mode, indent the brace if we're not indenting
+ // the whole block.
+ if (ManageWhitesmithsBraces)
+ ++Line->Level;
+
+ // Munch the semicolon after a namespace. This is more common than one would
+ // think. Putting the semicolon into its own line is very ugly.
+ parseBlock(/*MustBeDeclaration=*/true, AddLevels, /*MunchSemi=*/true,
+ /*KeepBraces=*/true, /*IfKind=*/nullptr, ManageWhitesmithsBraces);
+
+ addUnwrappedLine(AddLevels > 0 ? LineLevel::Remove : LineLevel::Keep);
+
+ if (ManageWhitesmithsBraces)
+ --Line->Level;
+}
+
void UnwrappedLineParser::parseNamespace() {
assert(FormatTok->isOneOf(tok::kw_namespace, TT_NamespaceMacro) &&
"'namespace' expected");
@@ -3107,29 +3131,15 @@ void UnwrappedLineParser::parseNamespace() {
DeclarationScopeStack.size() > 1)
? 1u
: 0u;
- bool ManageWhitesmithsBraces =
- AddLevels == 0u &&
- Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths;
-
- // If we're in Whitesmiths mode, indent the brace if we're not indenting
- // the whole block.
- if (ManageWhitesmithsBraces)
- ++Line->Level;
-
- // Munch the semicolon after a namespace. This is more common than one would
- // think. Putting the semicolon into its own line is very ugly.
- parseBlock(/*MustBeDeclaration=*/true, AddLevels, /*MunchSemi=*/true,
- /*KeepBraces=*/true, /*IfKind=*/nullptr,
- ManageWhitesmithsBraces);
-
- addUnwrappedLine(AddLevels > 0 ? LineLevel::Remove : LineLevel::Keep);
-
- if (ManageWhitesmithsBraces)
- --Line->Level;
+ parseNamespaceOrExportBlock(AddLevels);
}
// FIXME: Add error handling.
}
+void UnwrappedLineParser::parseCXXExportBlock() {
+ parseNamespaceOrExportBlock(Style.ExportBlockIndentation ? 1 : 0);
+}
+
void UnwrappedLineParser::parseNew() {
assert(FormatTok->is(tok::kw_new) && "'new' expected");
nextToken();
diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h
index b7daf8d9f44012..e5cfe4f6810791 100644
--- a/clang/lib/Format/UnwrappedLineParser.h
+++ b/clang/lib/Format/UnwrappedLineParser.h
@@ -171,6 +171,8 @@ class UnwrappedLineParser {
void parseRequiresClause(FormatToken *RequiresToken);
void parseRequiresExpression(FormatToken *RequiresToken);
void parseConstraintExpression();
+ void parseCXXExportBlock();
+ void parseNamespaceOrExportBlock(unsigned AddLevels);
void parseJavaEnumBody();
// Parses a record (aka class) as a top level element. If ParseAsExpr is true,
// parses the record as a child block, i.e. if the class declaration is an
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 5d386c1bbdbcd9..add52cd7460a48 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -9040,6 +9040,31 @@ TEST_F(FormatTest, AdaptiveOnePerLineFormatting) {
Style);
}
+TEST_F(FormatTest, ExportBlockIndentation) {
+ FormatStyle Style = getLLVMStyleWithColumns(80);
+ Style.ExportBlockIndentation = true;
+ verifyFormat("export {\n"
+ " int x;\n"
+ " int y;\n"
+ "}\n",
+ "export {\n"
+ " int x;\n"
+ " int y;\n"
+ "}\n",
+ Style);
+
+ Style.ExportBlockIndentation = false;
+ verifyFormat("export {\n"
+ " int x;\n"
+ " int y;\n"
+ "}\n",
+ "export {\n"
+ "int x;\n"
+ "int y;\n"
+ "}\n",
+ Style);
+}
+
TEST_F(FormatTest, FormatsBuilderPattern) {
verifyFormat("return llvm::StringSwitch<Reference::Kind>(name)\n"
" .StartsWith(\".eh_frame_hdr\", ORDER_EH_FRAMEHDR)\n"
|
" int foo;\n" | ||
"};", | ||
Style); | ||
verifyFormat("export { int foo; };", Style); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's going on here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can't fix the tests by changing them... here you make the decision that people want a collapsed export {} what makes you say thats what everyone wants?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, makes sense. As far as I understand it, were were previously just parsing this as a compound statement, which afaik isn’t formatted on a single line by default, but maybe namespaces are, but I’m candidly not quite sure what’s causing this to be formatted on one line... I think this has something to do with the fact that I used parseBlock
for this, but I’ll have to look into it a bit more.
Do we want a separate option for this (e.g. something like AllowShortExportBlocksOnASingleLine
) or should that just fall under AllowShortBlocksOnASingleLine
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be an option to en/disable it, if it can be shared with other things, or be exclusively for export is to be discussed. I have no strong opinion, but would lean to an extra option, because someone will ask for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense; I’ll hopefully have time to look into this next week because I’ve been a bit busy recently.
} | ||
// FIXME: Add error handling. | ||
} | ||
|
||
void UnwrappedLineParser::parseCXXExportBlock() { | ||
parseNamespaceOrExportBlock(Style.ExportBlockIndentation ? 1 : 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parseNamespaceOrExportBlock(Style.ExportBlockIndentation ? 1 : 0); | |
parseNamespaceOrExportBlock(/*AddLevels=*/Style.ExportBlockIndentation ? 1 : 0); |
" int foo;\n" | ||
"};", | ||
Style); | ||
verifyFormat("export { int foo; };", Style); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be an option to en/disable it, if it can be shared with other things, or be exclusively for export is to be discussed. I have no strong opinion, but would lean to an extra option, because someone will ask for it.
@Sirraide hi are you still working on this? We observed similar issue recently. |
Closes #121723.
export { ... }
blocks can get a bit long, so I thought it would make sense to have an option that makes it so their contents are not indented (basically the same argument as for namespaces). This is my first contribution to clang-format (I normally contribute to Clang), so I’m not entirely sure if what I did is the best approach.I based this on the
NamespaceIndentation
option, except that I didn’t include an option to control the behaviour of export blocks when nested because nesting them doesn’t really make sense (now that I think about it, iirc they could be nested inside something else though—e.g. a namespace—do we care about that case?).