[clang] BreakBeforeTemplateClose (PR #118046)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Nov 28 19:55:05 PST 2024
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 <leijurv at 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;
More information about the cfe-commits
mailing list