https://github.com/leijurv updated 
https://github.com/llvm/llvm-project/pull/118046

>From de78cfbdefd90ce2427b552c941c613f93c9fd9a Mon Sep 17 00:00:00 2001
From: Leijurv <leij...@gmail.com>
Date: Fri, 29 Nov 2024 21:35:06 -0600
Subject: [PATCH] BreakBeforeTemplateClose

---
 clang/docs/ClangFormatStyleOptions.rst     | 23 +++++++
 clang/include/clang/Format/Format.h        | 19 ++++++
 clang/lib/Format/ContinuationIndenter.cpp  | 18 +++++
 clang/lib/Format/Format.cpp                |  1 +
 clang/unittests/Format/ConfigParseTest.cpp |  1 +
 clang/unittests/Format/FormatTest.cpp      | 79 ++++++++++++++++++++++
 6 files changed, 141 insertions(+)

diff --git a/clang/docs/ClangFormatStyleOptions.rst 
b/clang/docs/ClangFormatStyleOptions.rst
index dc34094b5053a9..b40507b289049d 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -3416,6 +3416,29 @@ the configuration (without a prefix: ``Auto``).
 
 
 
+
+.. _BreakBeforeTemplateClose:
+
+**BreakBeforeTemplateClose** (``Boolean``) :ref:`¶ <BreakBeforeTemplateClose>`
+  If ``true``, a line break will be placed before the ``>`` in a multiline 
template declaration.
+
+  .. code-block:: c++
+
+     true:
+     template <
+         typename Foo,
+         typename Bar,
+         typename Baz
+     >
+
+     false:
+     template <
+         typename Foo,
+         typename Bar,
+         typename Baz>
+
+
+
 .. _BreakBeforeTernaryOperators:
 
 **BreakBeforeTernaryOperators** (``Boolean``) :versionbadge:`clang-format 3.7` 
:ref:`¶ <BreakBeforeTernaryOperators>`
diff --git a/clang/include/clang/Format/Format.h 
b/clang/include/clang/Format/Format.h
index 056fad2cc0ff8c..7a8b8ca72e494f 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -2248,6 +2248,24 @@ struct FormatStyle {
   /// \version 16
   BreakBeforeInlineASMColonStyle BreakBeforeInlineASMColon;
 
+  /// If ``true``, a line break will be placed before the ``>`` in a multiline
+  /// template declaration.
+  /// \code
+  ///    true:
+  ///    template <
+  ///        typename Foo,
+  ///        typename Bar,
+  ///        typename Baz
+  ///    >
+  ///
+  ///    false:
+  ///    template <
+  ///        typename Foo,
+  ///        typename Bar,
+  ///        typename Baz>
+  /// \endcode
+  bool BreakBeforeTemplateClose{};
+
   /// If ``true``, ternary operators will be placed after line breaks.
   /// \code
   ///    true:
@@ -5184,6 +5202,7 @@ struct FormatStyle {
            BreakBeforeBraces == R.BreakBeforeBraces &&
            BreakBeforeConceptDeclarations == R.BreakBeforeConceptDeclarations 
&&
            BreakBeforeInlineASMColon == R.BreakBeforeInlineASMColon &&
+           BreakBeforeTemplateClose == R.BreakBeforeTemplateClose &&
            BreakBeforeTernaryOperators == R.BreakBeforeTernaryOperators &&
            BreakBinaryOperations == R.BreakBinaryOperations &&
            BreakConstructorInitializers == R.BreakConstructorInitializers &&
diff --git a/clang/lib/Format/ContinuationIndenter.cpp 
b/clang/lib/Format/ContinuationIndenter.cpp
index aed86c1fb99551..0c77c18b3e7f48 100644
--- a/clang/lib/Format/ContinuationIndenter.cpp
+++ b/clang/lib/Format/ContinuationIndenter.cpp
@@ -382,10 +382,25 @@ bool ContinuationIndenter::canBreak(const LineState 
&State) {
   return !State.NoLineBreak && !CurrentState.NoLineBreak;
 }
 
+bool isMatchingBraceOnSameLine(const FormatToken *Token) {
+  if (!Token->MatchingParen)
+    return false;
+  const FormatToken *Matching = Token->MatchingParen;
+  const FormatToken *Current = Token;
+  while (Current && Current != Matching) {
+    if (Current->NewlinesBefore > 0)
+      return false;
+    Current = Current->Previous;
+  }
+  return true;
+}
+
 bool ContinuationIndenter::mustBreak(const LineState &State) {
   const FormatToken &Current = *State.NextToken;
   const FormatToken &Previous = *Current.Previous;
   const auto &CurrentState = State.Stack.back();
+  if (Current.ClosesTemplateDeclaration && Style.BreakBeforeTemplateClose)
+    return !isMatchingBraceOnSameLine(State.NextToken);
   if (Style.BraceWrapping.BeforeLambdaBody && Current.CanBreakBefore &&
       Current.is(TT_LambdaLBrace) && Previous.isNot(TT_LineComment)) {
     auto LambdaBodyLength = getLengthToMatchingParen(Current, State.Stack);
@@ -1279,6 +1294,9 @@ unsigned ContinuationIndenter::getNewLineColumn(const 
LineState &State) {
   FormatToken &Current = *State.NextToken;
   const auto &CurrentState = State.Stack.back();
 
+  if (Current.ClosesTemplateDeclaration && Style.BreakBeforeTemplateClose)
+    return CurrentState.Indent - Style.ContinuationIndentWidth;
+
   if (CurrentState.IsCSharpGenericTypeConstraint &&
       Current.isNot(TT_CSharpGenericTypeConstraint)) {
     return CurrentState.ColonPos + 2;
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index ee52972ce66f4a..3523f843d86b50 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -1000,6 +1000,7 @@ template <> struct MappingTraits<FormatStyle> {
     IO.mapOptional("BreakBeforeBraces", Style.BreakBeforeBraces);
     IO.mapOptional("BreakBeforeInlineASMColon",
                    Style.BreakBeforeInlineASMColon);
+    IO.mapOptional("BreakBeforeTemplateClose", Style.BreakBeforeTemplateClose);
     IO.mapOptional("BreakBeforeTernaryOperators",
                    Style.BreakBeforeTernaryOperators);
     IO.mapOptional("BreakBinaryOperations", Style.BreakBinaryOperations);
diff --git a/clang/unittests/Format/ConfigParseTest.cpp 
b/clang/unittests/Format/ConfigParseTest.cpp
index 7fc7492271668b..39f4ea49719600 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -162,6 +162,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
   CHECK_PARSE_BOOL(BinPackArguments);
   CHECK_PARSE_BOOL(BreakAdjacentStringLiterals);
   CHECK_PARSE_BOOL(BreakAfterJavaFieldAnnotations);
+  CHECK_PARSE_BOOL(BreakBeforeTemplateClose);
   CHECK_PARSE_BOOL(BreakBeforeTernaryOperators);
   CHECK_PARSE_BOOL(BreakStringLiterals);
   CHECK_PARSE_BOOL(CompactNamespaces);
diff --git a/clang/unittests/Format/FormatTest.cpp 
b/clang/unittests/Format/FormatTest.cpp
index 250e51b5421664..1dffce7925a055 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -11077,6 +11077,85 @@ TEST_F(FormatTest, 
WrapsTemplateDeclarationsWithComments) {
       Style);
 }
 
+TEST_F(FormatTest, BreakBeforeTemplateClose) {
+  FormatStyle Style = getGoogleStyle(FormatStyle::LK_Cpp);
+  Style.BreakTemplateDeclarations = FormatStyle::BTDS_Yes;
+  Style.ColumnLimit = 0;
+  verifyNoChange("template <typename Foo>\n"
+                 "void foo() {}",
+                 Style);
+  verifyNoChange("template <\n"
+                 "    typename Foo,\n"
+                 "    typename Bar>\n"
+                 "void foo() {}",
+                 Style);
+  // when BreakBeforeTemplateClose is off, this line break is removed:
+  verifyFormat("template <\n"
+               "    typename Foo,\n"
+               "    typename Bar>\n"
+               "void foo() {}",
+               "template <\n"
+               "    typename Foo,\n"
+               "    typename Bar\n"
+               ">\n"
+               "void foo() {}",
+               Style);
+  Style.BreakBeforeTemplateClose = true;
+  // BreakBeforeTemplateClose should NOT force multiline templates
+  verifyNoChange("template <typename Foo>\n"
+                 "void foo() {}",
+                 Style);
+  verifyNoChange("template <typename Foo, typename Bar>\n"
+                 "void foo() {}",
+                 Style);
+  // it should allow a line break:
+  verifyNoChange("template <\n"
+                 "    typename Foo\n"
+                 ">\n"
+                 "void foo() {}",
+                 Style);
+  verifyNoChange("template <\n"
+                 "    typename Foo,\n"
+                 "    typename Bar\n"
+                 ">\n"
+                 "void foo() {}",
+                 Style);
+  // it should add a line break if not already present:
+  verifyFormat("template <\n"
+               "    typename Foo\n"
+               ">\n"
+               "void foo() {}",
+               "template <\n"
+               "    typename Foo>\n"
+               "void foo() {}",
+               Style);
+  verifyFormat("template <\n"
+               "    typename Foo,\n"
+               "    typename Bar\n"
+               ">\n"
+               "void foo() {}",
+               "template <\n"
+               "    typename Foo,\n"
+               "    typename Bar>\n"
+               "void foo() {}",
+               Style);
+  // when within an indent scope, the > should be placed appropriately:
+  verifyFormat("struct Baz {\n"
+               "  template <\n"
+               "      typename Foo,\n"
+               "      typename Bar\n"
+               "  >\n"
+               "  void foo() {}\n"
+               "};",
+               "struct Baz {\n"
+               "  template <\n"
+               "      typename Foo,\n"
+               "      typename Bar>\n"
+               "  void foo() {}\n"
+               "};",
+               Style);
+}
+
 TEST_F(FormatTest, WrapsTemplateParameters) {
   FormatStyle Style = getLLVMStyle();
   Style.AlignAfterOpenBracket = FormatStyle::BAS_DontAlign;

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to