-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang-format] Add new option: WrapNamespaceBodyWithNewlines #106145
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 new option: WrapNamespaceBodyWithNewlines #106145
Conversation
Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be notified. If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers. If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. |
@llvm/pr-subscribers-clang-format @llvm/pr-subscribers-clang Author: None (dmasloff) ChangesI would like to suggest a new clang-format option for llvm-project - WrapNamespaceBodyWithNewlines. I think it can be added to upstream since it is used by many popular public repositories, for example, ytsaurus. You can look through their style guide at this page: https://github.com/ytsaurus/ytsaurus/blob/main/yt/styleguide/cpp.md#namespaces As you can see from the name of the option it wraps the body of namespace with additional newlines turning this code:
into that:
Looking forward to your advices and recommendations Full diff: https://github.com/llvm/llvm-project/pull/106145.diff 5 Files Affected:
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index a427d7cd40fcdd..140787ad9776d9 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -6652,6 +6652,23 @@ the configuration (without a prefix: ``Auto``).
For example: BOOST_PP_STRINGIZE
+.. _WrapNamespaceBodyWithNewlines:
+
+**WrapNamespaceBodyWithNewlines** (``Boolean``) :versionbadge:`clang-format 19` :ref:`¶ <WrapNamespaceBodyWithNewlines>`
+ Insert a newline at the begging and at the end of namespace definition
+
+ .. code-block:: c++
+
+ false: vs. true:
+
+ namespace a { namespace a {
+ namespace b { namespace b {
+ function();
+ } function();
+ }
+ }
+ }
+
.. END_FORMAT_STYLE_OPTIONS
Adding additional style options
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index d8b62c7652a0f6..eb8f198cc8a919 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -5057,6 +5057,21 @@ struct FormatStyle {
/// \version 11
std::vector<std::string> WhitespaceSensitiveMacros;
+ /// Insert a newline at the begging and at the end of namespace definition
+ /// \code
+ /// false: vs. true:
+ ///
+ /// namespace a { namespace a {
+ /// namespace b { namespace b {
+ /// function();
+ /// } function();
+ /// }
+ /// }
+ /// }
+ /// \endcode
+ /// \version 19
+ bool WrapNamespaceBodyWithNewlines;
+
bool operator==(const FormatStyle &R) const {
return AccessModifierOffset == R.AccessModifierOffset &&
AlignAfterOpenBracket == R.AlignAfterOpenBracket &&
@@ -5234,7 +5249,8 @@ struct FormatStyle {
TypenameMacros == R.TypenameMacros && UseTab == R.UseTab &&
VerilogBreakBetweenInstancePorts ==
R.VerilogBreakBetweenInstancePorts &&
- WhitespaceSensitiveMacros == R.WhitespaceSensitiveMacros;
+ WhitespaceSensitiveMacros == R.WhitespaceSensitiveMacros &&
+ WrapNamespaceBodyWithNewlines == R.WrapNamespaceBodyWithNewlines;
}
std::optional<FormatStyle> GetLanguageStyle(LanguageKind Language) const;
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index d2463b892fbb96..65d7737b397212 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -1154,6 +1154,8 @@ template <> struct MappingTraits<FormatStyle> {
Style.VerilogBreakBetweenInstancePorts);
IO.mapOptional("WhitespaceSensitiveMacros",
Style.WhitespaceSensitiveMacros);
+ IO.mapOptional("WrapNamespaceBodyWithNewlines",
+ Style.WrapNamespaceBodyWithNewlines);
// If AlwaysBreakAfterDefinitionReturnType was specified but
// BreakAfterReturnType was not, initialize the latter from the former for
@@ -1623,6 +1625,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.WhitespaceSensitiveMacros.push_back("NS_SWIFT_NAME");
LLVMStyle.WhitespaceSensitiveMacros.push_back("PP_STRINGIZE");
LLVMStyle.WhitespaceSensitiveMacros.push_back("STRINGIZE");
+ LLVMStyle.WrapNamespaceBodyWithNewlines = false;
LLVMStyle.PenaltyBreakAssignment = prec::Assignment;
LLVMStyle.PenaltyBreakBeforeFirstCallParameter = 19;
diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp
index 1804c1437fd41d..f3f76ac227df50 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.cpp
+++ b/clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -32,6 +32,24 @@ bool isRecordLBrace(const FormatToken &Tok) {
TT_StructLBrace, TT_UnionLBrace);
}
+bool LineStartsNamespaceScope(const AnnotatedLine* Line,
+ const AnnotatedLine* PreviousLine,
+ const AnnotatedLine* PrevPrevLine) {
+ return PreviousLine && PreviousLine->Last->is(tok::l_brace) &&
+ (PreviousLine->startsWithNamespace() ||
+ (PrevPrevLine && PrevPrevLine->startsWithNamespace() &&
+ PreviousLine->startsWith(tok::l_brace))) && !Line->startsWithNamespace();
+}
+
+bool LineEndsNamespaceScope(const AnnotatedLine *Line,
+ const SmallVectorImpl<AnnotatedLine*> &Lines) {
+ const FormatToken* tok = Line->First;
+ if (!tok || tok->isNot(tok::r_brace)) {
+ return false;
+ }
+ return getNamespaceToken(Line, Lines) != nullptr;
+}
+
/// Tracks the indent level of \c AnnotatedLines across levels.
///
/// \c nextLine must be called for each \c AnnotatedLine, after which \c
@@ -1493,6 +1511,18 @@ static auto computeNewlines(const AnnotatedLine &Line,
Newlines = 1;
}
+ // Insert empty line after "{" that opens namespace scope
+ if (Style.WrapNamespaceBodyWithNewlines &&
+ LineStartsNamespaceScope(&Line, PreviousLine, PrevPrevLine)) {
+ Newlines = 2;
+ }
+
+ // Insert empty line before "}" that closes namespace scope
+ if (Style.WrapNamespaceBodyWithNewlines &&
+ LineEndsNamespaceScope(&Line, Lines) && !LineEndsNamespaceScope(PreviousLine, Lines)) {
+ Newlines = std::max(Newlines, 2u);
+ }
+
// Insert or remove empty line before access specifiers.
if (PreviousLine && RootToken.isAccessSpecifier()) {
switch (Style.EmptyLineBeforeAccessModifier) {
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index a383a624434b1f..94d8d652f80046 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -28084,6 +28084,79 @@ TEST_F(FormatTest, BreakBinaryOperations) {
Style);
}
+TEST_F(FormatTest, WrappingNamespaceBodyWithNewlines) {
+ FormatStyle Style = getLLVMStyle();
+ Style.FixNamespaceComments = false;
+ Style.ShortNamespaceLines = 0;
+ Style.WrapNamespaceBodyWithNewlines = true;
+
+ // Empty namespace
+ verifyFormat("namespace N {};", Style);
+
+ // Single namespace
+ verifyFormat("namespace N {\n\n"
+ "int f1(int a) { return 2 * a; }\n\n"
+ "};", Style);
+
+ // Nested namespace
+ verifyFormat("namespace N1 {\n"
+ "namespace N2 {\n"
+ "namespace N3 {\n\n"
+ "int f1() {\n"
+ " int a = 1;\n"
+ " return a;\n"
+ "}\n\n"
+ "}\n"
+ "}\n"
+ "}", Style);
+
+ Style.WrapNamespaceBodyWithNewlines = false;
+
+ // Empty namespace
+ verifyFormat("namespace N {};", Style);
+
+ // Single namespace
+ verifyFormat("namespace N {\n"
+ "int f1(int a) { return 2 * a; }\n"
+ "};", Style);
+
+ // Nested namespace
+ verifyFormat("namespace N1 {\n"
+ "namespace N2 {\n"
+ "namespace N3 {\n"
+ "int f1() {\n"
+ " int a = 1;\n"
+ " return a;\n"
+ "}\n"
+ "}\n"
+ "}\n"
+ "}", Style);
+}
+
+TEST_F(FormatTest, WrappingNamespaceBodyWithNewlinesWithCompactNamespaces) {
+ FormatStyle Style = getLLVMStyle();
+ Style.FixNamespaceComments = false;
+ Style.CompactNamespaces = true;
+ Style.WrapNamespaceBodyWithNewlines = true;
+
+
+ verifyFormat("namespace N1 { namespace N2 { namespace N3 {\n\n"
+ "int f1() {\n"
+ " int a = 1;\n"
+ " return a;\n"
+ "}\n\n"
+ "}}}", Style);
+
+ Style.WrapNamespaceBodyWithNewlines = false;
+
+ verifyFormat("namespace N1 { namespace N2 { namespace N3 {\n"
+ "int f1() {\n"
+ " int a = 1;\n"
+ " return a;\n"
+ "}\n"
+ "}}}", Style);
+}
+
} // namespace
} // namespace test
} // namespace format
|
@mydeveloperday sorry for tagging, I hope it could save some time for out team as in this issue you seemed positive about new contributors |
✅ With the latest revision this PR passed the C/C++ code formatter. |
84ba993
to
568c99f
Compare
Please run |
568c99f
to
219424f
Compare
Maybe it's only my problem, but i get this message running
|
8c34acb
to
bf76cbe
Compare
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.
Running FormatTests
failed:
[ RUN ] FormatTest.WrapNamespaceBodyWithEmptyLinesNever
Assertion failed: (TheLine->MatchingClosingBlockLineIndex > 0), function tryFitMultipleLinesInOne, file UnwrappedLineFormatter.cpp, line 390.
@owenca I looked through this error, fortunately it is not connected with code changes introduced in my pr, but with additional test, which I wrote. Formatting this test |
@owenca @mydeveloperday @HazardyKnusperkeks maybe somebody could continue the review? |
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.
Please update the release notes.
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.
Please enable "Allowing edits by maintainers".
af6ef19
to
94509ed
Compare
Resolved merge conflict |
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.
Also, please address #106145 (review).
Add missing dependency that sometimes makes a build fails with ninja.
ebe57d3
to
1edc890
Compare
@dmasloff Congratulations on having your first Pull Request (PR) merged into the LLVM Project! Your changes will be combined with recent changes from other authors, then tested by our build bots. If there is a problem with a build, you may receive a report in an email or a comment on this PR. Please check whether problems have been caused by your change specifically, as the builds can include changes from many authors. It is not uncommon for your change to be included in a build that fails due to someone else's changes, or infrastructure issues. How to do this, and the rest of the post-merge process, is covered in detail here. If your change does cause a problem, it may be reverted, or you can revert it yourself. This is a normal part of LLVM development. You can fix your changes and open a new PR to merge them again. If you don't get any reports, no action is required from you. Your changes are working as expected, well done! |
I would like to suggest a new clang-format option for llvm-project - WrapNamespaceBodyWithNewlines. I think it can be added to upstream since it is used by many popular public repositories, for example, ytsaurus. You can look through their style guide at this page: https://github.com/ytsaurus/ytsaurus/blob/main/yt/styleguide/cpp.md#namespaces
As you can see from the name of the option it wraps the body of namespace with additional newlines turning this code:
into that:
Looking forward to your advices and recommendations