[clang] [clang-format] Add new option: WrapNamespaceBodyWithNewlines (PR #106145)

via cfe-commits cfe-commits at lists.llvm.org
Mon Aug 26 14:33:43 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: None (dmasloff)

<details>
<summary>Changes</summary>

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](https://github.com/ytsaurus/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:
```
namespace N {
int function();
} 
 ```
into that:
```
namespace N {

int function();

} 
 ```
Looking forward to your advices and recommendations

---
Full diff: https://github.com/llvm/llvm-project/pull/106145.diff


5 Files Affected:

- (modified) clang/docs/ClangFormatStyleOptions.rst (+17) 
- (modified) clang/include/clang/Format/Format.h (+17-1) 
- (modified) clang/lib/Format/Format.cpp (+3) 
- (modified) clang/lib/Format/UnwrappedLineFormatter.cpp (+30) 
- (modified) clang/unittests/Format/FormatTest.cpp (+73) 


``````````diff
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

``````````

</details>


https://github.com/llvm/llvm-project/pull/106145


More information about the cfe-commits mailing list