https://github.com/dmasloff updated https://github.com/llvm/llvm-project/pull/106145
>From 219424f9cd3477d9290c8766eaa857234a1ae387 Mon Sep 17 00:00:00 2001 From: dmasloff <dmaslo...@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 bf76cbe3571a670b37be02eb4dd4634b673fad00 Mon Sep 17 00:00:00 2001 From: dmasloff <dmaslo...@gmail.com> Date: Mon, 2 Sep 2024 16:50:47 +0300 Subject: [PATCH 2/2] FormatTests update --- clang/unittests/Format/FormatTest.cpp | 90 +++++++++++++++++++-------- 1 file changed, 65 insertions(+), 25 deletions(-) diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index b39b86b7e2d3f5..0b833bd240e80c 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -28137,7 +28137,9 @@ 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 +28169,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 +28194,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 +28211,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 +28256,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 +28302,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 +28336,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); } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits