[clang] [clang-format] Add new option: WrapNamespaceBodyWithNewlines (PR #106145)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 2 06:52:42 PDT 2024
https://github.com/dmasloff updated https://github.com/llvm/llvm-project/pull/106145
>From 219424f9cd3477d9290c8766eaa857234a1ae387 Mon Sep 17 00:00:00 2001
From: dmasloff <dmaslov24 at gmail.com>
Date: Mon, 26 Aug 2024 22:11:05 +0300
Subject: [PATCH 1/2] [clang-format] Add new option:
WrapNamespaceBodyWithNewlines
---
clang/docs/ClangFormatStyleOptions.rst | 42 ++++
clang/include/clang/Format/Format.h | 40 +++-
clang/lib/Format/Format.cpp | 15 ++
clang/lib/Format/UnwrappedLineFormatter.cpp | 42 ++++
clang/unittests/Format/FormatTest.cpp | 205 ++++++++++++++++++++
5 files changed, 343 insertions(+), 1 deletion(-)
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index a427d7cd40fcdd..06ac88fc337983 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -6652,6 +6652,48 @@ the configuration (without a prefix: ``Auto``).
For example: BOOST_PP_STRINGIZE
+.. _WrapNamespaceBodyWithEmptyLines:
+
+**WrapNamespaceBodyWithEmptyLines** (``WrapNamespaceBodyWithEmptyLinesStyle``) :versionbadge:`clang-format 19` :ref:`ΒΆ <WrapNamespaceBodyWithEmptyLines>`
+ Controls number of empty lines at the begging and at the end of
+ namespace definition.
+
+ Possible values:
+
+ * ``WNBWELS_Never`` (in configuration: ``Never``)
+ Removes all empty lines at the beginning and at the end of
+ namespace definition.
+
+ .. code-block:: c++
+
+ namespace N1 {
+ namespace N2
+ function();
+ }
+ }
+
+ * ``WNBWELS_Always`` (in configuration: ``Always``)
+ Always adds an empty line at the beginning and at the end of
+ namespace definition. MaxEmptyLinesToKeep is also applied, but
+ empty lines between consecutive namespace declarations are
+ always removed.
+
+ .. code-block:: c++
+
+ namespace N1 {
+ namespace N2 {
+
+ function();
+
+ }
+ }
+
+ * ``WNBWELS_Leave`` (in configuration: ``Leave``)
+ Keeps existing newlines at the beginning and at the end of
+ namespace definition using MaxEmptyLinesToKeep for formatting.
+
+
+
.. 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..963c7cbe1f8809 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -5057,6 +5057,43 @@ struct FormatStyle {
/// \version 11
std::vector<std::string> WhitespaceSensitiveMacros;
+ /// Different styles for modify number of empty lines in
+ /// the beginning and at the of end of namespaces.
+ enum WrapNamespaceBodyWithEmptyLinesStyle : int8_t {
+ /// Removes all empty lines at the beginning and at the end of
+ /// namespace definition.
+ /// \code
+ /// namespace N1 {
+ /// namespace N2
+ /// function();
+ /// }
+ /// }
+ /// \endcode
+ WNBWELS_Never,
+ /// Always adds an empty line at the beginning and at the end of
+ /// namespace definition. MaxEmptyLinesToKeep is also applied, but
+ /// empty lines between consecutive namespace declarations are
+ /// always removed.
+ /// \code
+ /// namespace N1 {
+ /// namespace N2 {
+ ///
+ /// function();
+ ///
+ /// }
+ /// }
+ /// \endcode
+ WNBWELS_Always,
+ /// Keeps existing newlines at the beginning and at the end of
+ /// namespace definition using MaxEmptyLinesToKeep for formatting.
+ WNBWELS_Leave
+ };
+
+ /// Controls number of empty lines at the begging and at the end of
+ /// namespace definition.
+ /// \version 19
+ WrapNamespaceBodyWithEmptyLinesStyle WrapNamespaceBodyWithEmptyLines;
+
bool operator==(const FormatStyle &R) const {
return AccessModifierOffset == R.AccessModifierOffset &&
AlignAfterOpenBracket == R.AlignAfterOpenBracket &&
@@ -5234,7 +5271,8 @@ struct FormatStyle {
TypenameMacros == R.TypenameMacros && UseTab == R.UseTab &&
VerilogBreakBetweenInstancePorts ==
R.VerilogBreakBetweenInstancePorts &&
- WhitespaceSensitiveMacros == R.WhitespaceSensitiveMacros;
+ WhitespaceSensitiveMacros == R.WhitespaceSensitiveMacros &&
+ WrapNamespaceBodyWithEmptyLines == R.WrapNamespaceBodyWithEmptyLines;
}
std::optional<FormatStyle> GetLanguageStyle(LanguageKind Language) const;
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index d2463b892fbb96..b0d2836e9c69c2 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -829,6 +829,18 @@ template <> struct ScalarEnumerationTraits<FormatStyle::UseTabStyle> {
}
};
+template <>
+struct ScalarEnumerationTraits<
+ FormatStyle::WrapNamespaceBodyWithEmptyLinesStyle> {
+ static void
+ enumeration(IO &IO,
+ FormatStyle::WrapNamespaceBodyWithEmptyLinesStyle &Value) {
+ IO.enumCase(Value, "Never", FormatStyle::WNBWELS_Never);
+ IO.enumCase(Value, "Always", FormatStyle::WNBWELS_Always);
+ IO.enumCase(Value, "Leave", FormatStyle::WNBWELS_Leave);
+ }
+};
+
template <> struct MappingTraits<FormatStyle> {
static void mapping(IO &IO, FormatStyle &Style) {
// When reading, read the language first, we need it for getPredefinedStyle.
@@ -1154,6 +1166,8 @@ template <> struct MappingTraits<FormatStyle> {
Style.VerilogBreakBetweenInstancePorts);
IO.mapOptional("WhitespaceSensitiveMacros",
Style.WhitespaceSensitiveMacros);
+ IO.mapOptional("WrapNamespaceBodyWithEmptyLines",
+ Style.WrapNamespaceBodyWithEmptyLines);
// If AlwaysBreakAfterDefinitionReturnType was specified but
// BreakAfterReturnType was not, initialize the latter from the former for
@@ -1623,6 +1637,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.WrapNamespaceBodyWithEmptyLines = FormatStyle::WNBWELS_Leave;
LLVMStyle.PenaltyBreakAssignment = prec::Assignment;
LLVMStyle.PenaltyBreakBeforeFirstCallParameter = 19;
diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp
index 1804c1437fd41d..e50f62b49acbcc 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.cpp
+++ b/clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -32,6 +32,26 @@ 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)));
+}
+
+bool LineEndsNamespaceScope(const AnnotatedLine *Line,
+ const SmallVectorImpl<AnnotatedLine *> &Lines) {
+ if (!Line)
+ return false;
+ 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 +1513,28 @@ static auto computeNewlines(const AnnotatedLine &Line,
Newlines = 1;
}
+ // Modify empty lines after "{" that opens namespace scope.
+ if (Style.WrapNamespaceBodyWithEmptyLines != FormatStyle::WNBWELS_Leave &&
+ LineStartsNamespaceScope(&Line, PreviousLine, PrevPrevLine)) {
+ if (Style.WrapNamespaceBodyWithEmptyLines == FormatStyle::WNBWELS_Never)
+ Newlines = std::min(Newlines, 1u);
+ else if (!Line.startsWithNamespace())
+ Newlines = std::max(Newlines, 2u);
+ else
+ Newlines = std::min(Newlines, 1u);
+ }
+
+ // Modify empty lines before "}" that closes namespace scope.
+ if (Style.WrapNamespaceBodyWithEmptyLines != FormatStyle::WNBWELS_Leave &&
+ LineEndsNamespaceScope(&Line, Lines)) {
+ if (Style.WrapNamespaceBodyWithEmptyLines == FormatStyle::WNBWELS_Never)
+ Newlines = std::min(Newlines, 1u);
+ else if (!LineEndsNamespaceScope(PreviousLine, Lines))
+ Newlines = std::max(Newlines, 2u);
+ else
+ Newlines = std::min(Newlines, 1u);
+ }
+
// 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..afb56add0c32ba 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -28084,6 +28084,211 @@ TEST_F(FormatTest, BreakBinaryOperations) {
Style);
}
+TEST_F(FormatTest, WrapNamespaceBodyWithEmptyLinesNever) {
+ FormatStyle Style = getLLVMStyle();
+ Style.FixNamespaceComments = false;
+ Style.ShortNamespaceLines = 0;
+ Style.MaxEmptyLinesToKeep = 2;
+ Style.WrapNamespaceBodyWithEmptyLines = FormatStyle::WNBWELS_Never;
+ Style.CompactNamespaces = false;
+
+ // Empty namespace
+ verifyNoChange("namespace N {};", Style);
+
+ // Single namespace
+ verifyNoChange("namespace N {\n"
+ "int f1(int a) { return 2 * a; }\n"
+ "};",
+ Style);
+
+ // Nested namespace
+ verifyNoChange("namespace N1 {\n"
+ "namespace N2 {\n"
+ "namespace N3 {\n"
+ "int f1() {\n"
+ " int a = 1;\n"
+ " return a;\n"
+ "}\n"
+ "}\n"
+ "}\n"
+ "}",
+ Style);
+
+ Style.CompactNamespaces = true;
+
+ // Empty namespace
+ verifyNoChange("namespace N {\n};", Style);
+
+ // Single namespace
+ verifyNoChange("namespace N {\n"
+ "int f1(int a) { return 2 * a; }\n"
+ "};",
+ Style);
+
+ // Nested namespace
+ verifyNoChange("namespace N1 { namespace N2 { namespace N3 {\n"
+ "int f1() {\n"
+ " int a = 1;\n"
+ " return a;\n"
+ "}\n"
+ "}}}",
+ Style);
+}
+
+TEST_F(FormatTest, WrapNamespaceBodyWithEmptyLinesAlways) {
+ FormatStyle Style = getLLVMStyle();
+ Style.FixNamespaceComments = false;
+ Style.ShortNamespaceLines = 0;
+ Style.MaxEmptyLinesToKeep = 0;
+ Style.WrapNamespaceBodyWithEmptyLines = FormatStyle::WNBWELS_Always;
+ Style.CompactNamespaces = false;
+
+ // Empty namespace
+ verifyNoChange("namespace N {};", Style);
+
+ // Single namespace
+ verifyNoChange("namespace N {\n\n"
+ "int f1(int a) { return 2 * a; }\n\n"
+ "};",
+ Style);
+
+ // Nested namespace
+ verifyNoChange("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.CompactNamespaces = true;
+
+ // Nested namespace
+ verifyNoChange("namespace N1 { namespace N2 { namespace N3 {\n\n"
+ "int f1() {\n"
+ " int a = 1;\n"
+ " return a;\n"
+ "}\n\n"
+ "}}}",
+ Style);
+
+ Style.MaxEmptyLinesToKeep = 2;
+ Style.CompactNamespaces = false;
+
+ // Empty namespace
+ verifyNoChange("namespace N {};", Style);
+
+ // Single namespace
+ verifyNoChange("namespace N {\n\n\n"
+ "void function()\n\n\n"
+ "};",
+ Style);
+
+ // Nested namespace
+ verifyFormat("namespace N1 {\n"
+ "namespace N2 {\n"
+ "namespace N3 {\n\n\n"
+ "int f1() {\n"
+ " int a = 1;\n"
+ " return a;\n"
+ "}\n\n\n"
+ "}\n"
+ "}\n"
+ "}",
+ "namespace N1 {\n"
+ "namespace N2 {\n\n"
+ "namespace N3 {\n\n\n"
+ "int f1() {\n"
+ " int a = 1;\n"
+ " return a;\n"
+ "}\n\n\n"
+ "}\n\n"
+ "}\n"
+ "}",
+ Style);
+
+ Style.CompactNamespaces = true;
+
+ // Nested namespace
+ verifyNoChange("namespace N1 { namespace N2 { namespace N3 {\n\n\n"
+ "int f1() {\n"
+ " int a = 1;\n"
+ " return a;\n"
+ "}\n\n\n"
+ "}}}",
+ Style);
+}
+
+TEST_F(FormatTest, WrapNamespaceBodyWithEmptyLinesLeave) {
+ FormatStyle Style = getLLVMStyle();
+ Style.FixNamespaceComments = false;
+ Style.ShortNamespaceLines = 0;
+ Style.MaxEmptyLinesToKeep = 0;
+ Style.WrapNamespaceBodyWithEmptyLines = FormatStyle::WNBWELS_Leave;
+ Style.CompactNamespaces = false;
+
+ // Empty namespace
+ verifyNoChange("namespace N {};", Style);
+
+ // Single namespace
+ verifyNoChange("namespace N {\n"
+ "int f1(int a) { return 2 * a; }\n"
+ "};",
+ Style);
+
+ // Nested namespace
+ verifyNoChange("namespace N1 {\n"
+ "namespace N2 {\n"
+ "namespace N3 {\n"
+ "int f1() {\n"
+ " int a = 1;\n"
+ " return a;\n"
+ "}\n"
+ "}\n"
+ "}\n"
+ "}",
+ Style);
+
+ Style.MaxEmptyLinesToKeep = 2;
+
+ // Single namespace
+ verifyNoChange("namespace N {\n\n\n"
+ "int f1(int a) { return 2 * a; }\n\n\n"
+ "};",
+ Style);
+
+ // Nested namespace
+ verifyNoChange("namespace N1 {\n"
+ "namespace N2 {\n\n"
+ "namespace N3 {\n\n\n"
+ "int f1() {\n"
+ " int a = 1;\n"
+ " return a;\n"
+ "}\n\n\n"
+ "}\n\n"
+ "}\n"
+ "}",
+ Style);
+
+ Style.CompactNamespaces = true;
+
+ // Empty namespace
+ verifyNoChange("namespace N {\n};", Style);
+
+ // Nested namespace
+ verifyNoChange("namespace N1 { namespace N2 { namespace N3 {\n\n\n"
+ "int f1() {\n"
+ " int a = 1;\n"
+ " return a;\n"
+ "}\n\n\n"
+ "}}}",
+ Style);
+}
+
} // namespace
} // namespace test
} // namespace format
>From 8c34acba55003b439e235ca5ec9913168c0a4ff0 Mon Sep 17 00:00:00 2001
From: dmasloff <dmaslov24 at gmail.com>
Date: Mon, 2 Sep 2024 16:50:47 +0300
Subject: [PATCH 2/2] FormatTests update
---
clang/unittests/Format/FormatTest.cpp | 89 +++++++++++++++++++--------
1 file changed, 64 insertions(+), 25 deletions(-)
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index b39b86b7e2d3f5..991a8b1c351108 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -28137,7 +28137,8 @@ TEST_F(FormatTest, WrapNamespaceBodyWithEmptyLinesNever) {
Style.CompactNamespaces = true;
// Empty namespace
- verifyNoChange("namespace N {\n};", Style);
+ verifyNoChange("namespace N {\n"
+ "};", Style);
// Single namespace
verifyNoChange("namespace N {\n"
@@ -28167,19 +28168,23 @@ TEST_F(FormatTest, WrapNamespaceBodyWithEmptyLinesAlways) {
verifyNoChange("namespace N {};", Style);
// Single namespace
- verifyNoChange("namespace N {\n\n"
- "int f1(int a) { return 2 * a; }\n\n"
+ verifyNoChange("namespace N {\n"
+ "\n"
+ "int f1(int a) { return 2 * a; }\n"
+ "\n"
"};",
Style);
// Nested namespace
verifyNoChange("namespace N1 {\n"
"namespace N2 {\n"
- "namespace N3 {\n\n"
+ "namespace N3 {\n"
+ "\n"
"int f1() {\n"
" int a = 1;\n"
" return a;\n"
- "}\n\n"
+ "}\n"
+ "\n"
"}\n"
"}\n"
"}",
@@ -28188,11 +28193,13 @@ TEST_F(FormatTest, WrapNamespaceBodyWithEmptyLinesAlways) {
Style.CompactNamespaces = true;
// Nested namespace
- verifyNoChange("namespace N1 { namespace N2 { namespace N3 {\n\n"
+ verifyNoChange("namespace N1 { namespace N2 { namespace N3 {\n"
+ "\n"
"int f1() {\n"
" int a = 1;\n"
" return a;\n"
- "}\n\n"
+ "}\n"
+ "\n"
"}}}",
Style);
@@ -28203,30 +28210,44 @@ TEST_F(FormatTest, WrapNamespaceBodyWithEmptyLinesAlways) {
verifyNoChange("namespace N {};", Style);
// Single namespace
- verifyNoChange("namespace N {\n\n\n"
- "void function()\n\n\n"
+ verifyNoChange("namespace N {\n"
+ "\n"
+ "\n"
+ "void function()\n"
+ "\n"
+ "\n"
"};",
Style);
// Nested namespace
verifyFormat("namespace N1 {\n"
"namespace N2 {\n"
- "namespace N3 {\n\n\n"
+ "namespace N3 {\n"
+ "\n"
+ "\n"
"int f1() {\n"
" int a = 1;\n"
" return a;\n"
- "}\n\n\n"
+ "}\n"
+ "\n"
+ "\n"
"}\n"
"}\n"
"}",
"namespace N1 {\n"
- "namespace N2 {\n\n"
- "namespace N3 {\n\n\n"
+ "namespace N2 {\n"
+ "\n"
+ "namespace N3 {\n"
+ "\n"
+ "\n"
"int f1() {\n"
" int a = 1;\n"
" return a;\n"
- "}\n\n\n"
- "}\n\n"
+ "}\n"
+ "\n"
+ "\n"
+ "}\n"
+ "\n"
"}\n"
"}",
Style);
@@ -28234,11 +28255,15 @@ TEST_F(FormatTest, WrapNamespaceBodyWithEmptyLinesAlways) {
Style.CompactNamespaces = true;
// Nested namespace
- verifyNoChange("namespace N1 { namespace N2 { namespace N3 {\n\n\n"
+ verifyNoChange("namespace N1 { namespace N2 { namespace N3 {\n"
+ "\n"
+ "\n"
"int f1() {\n"
" int a = 1;\n"
" return a;\n"
- "}\n\n\n"
+ "}\n"
+ "\n"
+ "\n"
"}}}",
Style);
}
@@ -28276,20 +28301,30 @@ TEST_F(FormatTest, WrapNamespaceBodyWithEmptyLinesLeave) {
Style.MaxEmptyLinesToKeep = 2;
// Single namespace
- verifyNoChange("namespace N {\n\n\n"
- "int f1(int a) { return 2 * a; }\n\n\n"
+ verifyNoChange("namespace N {\n"
+ "\n"
+ "\n"
+ "int f1(int a) { return 2 * a; }\n"
+ "\n"
+ "\n"
"};",
Style);
// Nested namespace
verifyNoChange("namespace N1 {\n"
- "namespace N2 {\n\n"
- "namespace N3 {\n\n\n"
+ "namespace N2 {\n"
+ "\n"
+ "namespace N3 {\n"
+ "\n"
+ "\n"
"int f1() {\n"
" int a = 1;\n"
" return a;\n"
- "}\n\n\n"
- "}\n\n"
+ "}\n"
+ "\n"
+ "\n"
+ "}\n"
+ "\n"
"}\n"
"}",
Style);
@@ -28300,11 +28335,15 @@ TEST_F(FormatTest, WrapNamespaceBodyWithEmptyLinesLeave) {
verifyNoChange("namespace N {\n};", Style);
// Nested namespace
- verifyNoChange("namespace N1 { namespace N2 { namespace N3 {\n\n\n"
+ verifyNoChange("namespace N1 { namespace N2 { namespace N3 {\n"
+ "\n"
+ "\n"
"int f1() {\n"
" int a = 1;\n"
" return a;\n"
- "}\n\n\n"
+ "}\n"
+ "\n"
+ "\n"
"}}}",
Style);
}
More information about the cfe-commits
mailing list