[clang] 1c997fe - [clang-format] Add option WrapNamespaceBodyWithNewlines (#106145)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 2 21:52:06 PST 2025
Author: dmasloff
Date: 2025-01-02T21:52:01-08:00
New Revision: 1c997feff16860ab6b21c5c03dc7ca65f300967f
URL: https://github.com/llvm/llvm-project/commit/1c997feff16860ab6b21c5c03dc7ca65f300967f
DIFF: https://github.com/llvm/llvm-project/commit/1c997feff16860ab6b21c5c03dc7ca65f300967f.diff
LOG: [clang-format] Add option WrapNamespaceBodyWithNewlines (#106145)
It wraps the body of namespace with additional newlines, turning this code:
```
namespace N {
int function();
}
```
into the following:
```
namespace N {
int function();
}
```
---------
Co-authored-by: Owen Pan <owenpiano at gmail.com>
Added:
Modified:
clang/docs/ClangFormatStyleOptions.rst
clang/docs/ReleaseNotes.rst
clang/include/clang/Format/Format.h
clang/lib/Format/Format.cpp
clang/lib/Format/UnwrappedLineFormatter.cpp
clang/unittests/Format/ConfigParseTest.cpp
clang/unittests/Format/FormatTest.cpp
Removed:
################################################################################
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index d9b3f666df03c0..7bfaee4e2d35b9 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -6843,6 +6843,45 @@ the configuration (without a prefix: ``Auto``).
For example: BOOST_PP_STRINGIZE
+.. _WrapNamespaceBodyWithEmptyLines:
+
+**WrapNamespaceBodyWithEmptyLines** (``WrapNamespaceBodyWithEmptyLinesStyle``) :versionbadge:`clang-format 20` :ref:`ΒΆ <WrapNamespaceBodyWithEmptyLines>`
+ Wrap namespace body with empty lines.
+
+ Possible values:
+
+ * ``WNBWELS_Never`` (in configuration: ``Never``)
+ Remove all empty lines at the beginning and the end of namespace body.
+
+ .. code-block:: c++
+
+ namespace N1 {
+ namespace N2
+ function();
+ }
+ }
+
+ * ``WNBWELS_Always`` (in configuration: ``Always``)
+ Always have at least one empty line at the beginning and the end of
+ namespace body except that the number of empty lines between consecutive
+ nested namespace definitions is not increased.
+
+ .. code-block:: c++
+
+ namespace N1 {
+ namespace N2 {
+
+ function();
+
+ }
+ }
+
+ * ``WNBWELS_Leave`` (in configuration: ``Leave``)
+ Keep existing newlines at the beginning and the end of namespace body.
+ ``MaxEmptyLinesToKeep`` still applies.
+
+
+
.. END_FORMAT_STYLE_OPTIONS
Adding additional style options
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index aca07e2ba9cf2d..2789a24ebf273d 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -1127,6 +1127,7 @@ clang-format
- Adds ``AllowShortNamespacesOnASingleLine`` option.
- Adds ``VariableTemplates`` option.
- Adds support for bash globstar in ``.clang-format-ignore``.
+- Adds ``WrapNamespaceBodyWithEmptyLines`` option.
libclang
--------
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index bb34f2d33ac15e..9b7a633e0a1461 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -5143,6 +5143,39 @@ struct FormatStyle {
/// \version 11
std::vector<std::string> WhitespaceSensitiveMacros;
+ /// Different styles for wrapping namespace body with empty lines.
+ enum WrapNamespaceBodyWithEmptyLinesStyle : int8_t {
+ /// Remove all empty lines at the beginning and the end of namespace body.
+ /// \code
+ /// namespace N1 {
+ /// namespace N2
+ /// function();
+ /// }
+ /// }
+ /// \endcode
+ WNBWELS_Never,
+ /// Always have at least one empty line at the beginning and the end of
+ /// namespace body except that the number of empty lines between consecutive
+ /// nested namespace definitions is not increased.
+ /// \code
+ /// namespace N1 {
+ /// namespace N2 {
+ ///
+ /// function();
+ ///
+ /// }
+ /// }
+ /// \endcode
+ WNBWELS_Always,
+ /// Keep existing newlines at the beginning and the end of namespace body.
+ /// ``MaxEmptyLinesToKeep`` still applies.
+ WNBWELS_Leave
+ };
+
+ /// Wrap namespace body with empty lines.
+ /// \version 20
+ WrapNamespaceBodyWithEmptyLinesStyle WrapNamespaceBodyWithEmptyLines;
+
bool operator==(const FormatStyle &R) const {
return AccessModifierOffset == R.AccessModifierOffset &&
AlignAfterOpenBracket == R.AlignAfterOpenBracket &&
@@ -5326,7 +5359,8 @@ struct FormatStyle {
UseTab == R.UseTab && VariableTemplates == R.VariableTemplates &&
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 a5657f2d910f68..e51d7ac2e5b6c4 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -839,6 +839,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.
@@ -1171,6 +1183,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
@@ -1639,6 +1653,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 803c600cec44db..bc6766a47f5c70 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.cpp
+++ b/clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -1584,6 +1584,23 @@ static auto computeNewlines(const AnnotatedLine &Line,
Newlines = 1;
}
+ if (Style.WrapNamespaceBodyWithEmptyLines != FormatStyle::WNBWELS_Leave) {
+ // Modify empty lines after TT_NamespaceLBrace.
+ if (PreviousLine && PreviousLine->endsWith(TT_NamespaceLBrace)) {
+ if (Style.WrapNamespaceBodyWithEmptyLines == FormatStyle::WNBWELS_Never)
+ Newlines = 1;
+ else if (!Line.startsWithNamespace())
+ Newlines = std::max(Newlines, 2u);
+ }
+ // Modify empty lines before TT_NamespaceRBrace.
+ if (Line.startsWith(TT_NamespaceRBrace)) {
+ if (Style.WrapNamespaceBodyWithEmptyLines == FormatStyle::WNBWELS_Never)
+ Newlines = 1;
+ else if (!PreviousLine->startsWith(TT_NamespaceRBrace))
+ 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/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp
index b249bf073aa453..9c38dbbc51f0a1 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -865,6 +865,13 @@ TEST(ConfigParseTest, ParsesConfiguration) {
CHECK_PARSE("SortUsingDeclarations: true", SortUsingDeclarations,
FormatStyle::SUD_LexicographicNumeric);
+ CHECK_PARSE("WrapNamespaceBodyWithEmptyLines: Never",
+ WrapNamespaceBodyWithEmptyLines, FormatStyle::WNBWELS_Never);
+ CHECK_PARSE("WrapNamespaceBodyWithEmptyLines: Always",
+ WrapNamespaceBodyWithEmptyLines, FormatStyle::WNBWELS_Always);
+ CHECK_PARSE("WrapNamespaceBodyWithEmptyLines: Leave",
+ WrapNamespaceBodyWithEmptyLines, FormatStyle::WNBWELS_Leave);
+
// FIXME: This is required because parsing a configuration simply overwrites
// the first N elements of the list instead of resetting it.
Style.ForEachMacros.clear();
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 22b6f7e1b62e2e..44b9dba2498900 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -28427,6 +28427,136 @@ TEST_F(FormatTest, ShortNamespacesOption) {
Style);
}
+TEST_F(FormatTest, WrapNamespaceBodyWithEmptyLinesNever) {
+ auto Style = getLLVMStyle();
+ Style.FixNamespaceComments = false;
+ Style.MaxEmptyLinesToKeep = 2;
+ Style.WrapNamespaceBodyWithEmptyLines = FormatStyle::WNBWELS_Never;
+
+ // Empty namespace.
+ verifyFormat("namespace N {}", Style);
+
+ // Single namespace.
+ verifyFormat("namespace N {\n"
+ "int f1(int a) { return 2 * a; }\n"
+ "}",
+ "namespace N {\n"
+ "\n"
+ "\n"
+ "int f1(int a) { return 2 * a; }\n"
+ "\n"
+ "\n"
+ "}",
+ Style);
+
+ // Nested namespace.
+ verifyFormat("namespace N1 {\n"
+ "namespace N2 {\n"
+ "int a = 1;\n"
+ "}\n"
+ "}",
+ "namespace N1 {\n"
+ "\n"
+ "\n"
+ "namespace N2 {\n"
+ "\n"
+ "int a = 1;\n"
+ "\n"
+ "}\n"
+ "\n"
+ "\n"
+ "}",
+ Style);
+
+ Style.CompactNamespaces = true;
+
+ verifyFormat("namespace N1 { namespace N2 {\n"
+ "int a = 1;\n"
+ "}}",
+ "namespace N1 { namespace N2 {\n"
+ "\n"
+ "\n"
+ "int a = 1;\n"
+ "\n"
+ "\n"
+ "}}",
+ Style);
+}
+
+TEST_F(FormatTest, WrapNamespaceBodyWithEmptyLinesAlways) {
+ auto Style = getLLVMStyle();
+ Style.FixNamespaceComments = false;
+ Style.MaxEmptyLinesToKeep = 2;
+ Style.WrapNamespaceBodyWithEmptyLines = FormatStyle::WNBWELS_Always;
+
+ // Empty namespace.
+ verifyFormat("namespace N {}", Style);
+
+ // Single namespace.
+ verifyFormat("namespace N {\n"
+ "\n"
+ "int f1(int a) { return 2 * a; }\n"
+ "\n"
+ "}",
+ "namespace N {\n"
+ "int f1(int a) { return 2 * a; }\n"
+ "}",
+ Style);
+
+ // Nested namespace.
+ verifyFormat("namespace N1 {\n"
+ "namespace N2 {\n"
+ "\n"
+ "int a = 1;\n"
+ "\n"
+ "}\n"
+ "}",
+ "namespace N1 {\n"
+ "namespace N2 {\n"
+ "int a = 1;\n"
+ "}\n"
+ "}",
+ Style);
+
+ verifyFormat("namespace N1 {\n"
+ "\n"
+ "namespace N2 {\n"
+ "\n"
+ "\n"
+ "int a = 1;\n"
+ "\n"
+ "\n"
+ "}\n"
+ "\n"
+ "}",
+ "namespace N1 {\n"
+ "\n"
+ "namespace N2 {\n"
+ "\n"
+ "\n"
+ "\n"
+ "int a = 1;\n"
+ "\n"
+ "\n"
+ "\n"
+ "}\n"
+ "\n"
+ "}",
+ Style);
+
+ Style.CompactNamespaces = true;
+
+ verifyFormat("namespace N1 { namespace N2 {\n"
+ "\n"
+ "int a = 1;\n"
+ "\n"
+ "}}",
+ "namespace N1 { namespace N2 {\n"
+ "int a = 1;\n"
+ "}}",
+ Style);
+}
+
} // namespace
} // namespace test
} // namespace format
More information about the cfe-commits
mailing list