[clang] BreakBeforeTemplateClose (PR #118046)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Nov 29 19:35:39 PST 2024
https://github.com/leijurv updated https://github.com/llvm/llvm-project/pull/118046
>From de78cfbdefd90ce2427b552c941c613f93c9fd9a Mon Sep 17 00:00:00 2001
From: Leijurv <leijurv at 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;
More information about the cfe-commits
mailing list