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

In clang-format, multiline templates have the `>` on the same line as the last 
parameter:

```c++
template<
    typename Foo,
    typename Bar,
    typename Baz>
void foo() {
```

I would like to add an option to put the `>` on the next line, like this:

```c++
template<
    typename Foo,
    typename Bar,
    typename Baz
>
void foo() {
```

My reasoning is that it rubs me the wrong way and reminds me of this style of 
braces:

```c++
if (foo()) {
    bar();}
```

Most people agree this is better:

```c++
if (foo()) {
    bar();
}
```

>From b9886635c250fe765bf35c6b0e785f55b0749d52 Mon Sep 17 00:00:00 2001
From: Leijurv <leij...@gmail.com>
Date: Thu, 28 Nov 2024 21:44:50 -0600
Subject: [PATCH] BreakBeforeTemplateClose

---
 clang/docs/ClangFormatStyleOptions.rst     | 23 ++++++++++++++++++++++
 clang/include/clang/Format/Format.h        |  4 ++++
 clang/lib/Format/ContinuationIndenter.cpp  | 22 +++++++++++++++++++++
 clang/lib/Format/Format.cpp                |  2 ++
 clang/unittests/Format/ConfigParseTest.cpp |  1 +
 clang/unittests/Format/FormatTest.cpp      | 21 ++++++++++++++++++++
 6 files changed, 73 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..a8478060828072 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -2248,6 +2248,9 @@ struct FormatStyle {
   /// \version 16
   BreakBeforeInlineASMColonStyle BreakBeforeInlineASMColon;
 
+  /// If ``true``, a line break will be placed before the ``>`` in a multiline 
template declaration.
+  bool BreakBeforeTemplateClose;
+
   /// If ``true``, ternary operators will be placed after line breaks.
   /// \code
   ///    true:
@@ -5184,6 +5187,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..5887e9cef9011d 100644
--- a/clang/lib/Format/ContinuationIndenter.cpp
+++ b/clang/lib/Format/ContinuationIndenter.cpp
@@ -382,10 +382,28 @@ 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 +1297,10 @@ unsigned ContinuationIndenter::getNewLineColumn(const 
LineState &State) {
   FormatToken &Current = *State.NextToken;
   const auto &CurrentState = State.Stack.back();
 
+  if (Current.ClosesTemplateDeclaration && Style.BreakBeforeTemplateClose) {
+    return 0;
+  }
+
   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..a957129bbcf440 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -1000,6 +1000,8 @@ 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..3cd2e5d292e0f5 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -11077,6 +11077,27 @@ TEST_F(FormatTest, 
WrapsTemplateDeclarationsWithComments) {
       Style);
 }
 
+TEST_F(FormatTest, BreakBeforeTemplateClose) {
+  FromatStyle Style = getLLVMStyle();
+  verifyFormat("template <typename Foo>\nvoid foo() {}", Style);
+  verifyFormat("template <\n    typename Foo>\nvoid foo() {}", Style);
+  // when BreakBeforeTemplateClose is off, this line break is removed:
+  verifyFormat("template <\n    typename Foo\n>\nvoid foo() {}",
+    "template <\n    typename Foo>\nvoid foo() {}", Style);
+  Style.BreakBeforeTemplateClose = true;
+  // BreakBeforeTemplateClose should NOT force multiline templates
+  verifyFormat("template <typename Foo>\nvoid foo() {}", Style);
+  verifyFormat("template <typename Foo, typename Bar>\nvoid foo() {}", Style);
+  // it should allow a line break:
+  verifyFormat("template <\n    typename Foo\n>\nvoid foo() {}", Style);
+  verifyFormat("template <\n    typename Foo,\n    typename Bar\n>\nvoid foo() 
{}", Style);
+  // it should add a line break if not already present:
+  verifyFormat("template <\n    typename Foo>\nvoid foo() {}",
+    "template <\n    typename Foo\n>\nvoid foo() {}", Style);
+  verifyFormat("template <\n    typename Foo,\n    typename Bar>\nvoid foo() 
{}",
+    "template <\n    typename Foo,\n    typename Bar\n>\nvoid foo() {}", 
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