[clang] [clang-format] Add BreakBeforeTemplateClose option (PR #118046)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Nov 30 08:57:20 PST 2024
https://github.com/leijurv updated https://github.com/llvm/llvm-project/pull/118046
>From efb5b98de8817ce02a226353d9f5e36095874b27 Mon Sep 17 00:00:00 2001
From: Leijurv <leijurv at gmail.com>
Date: Fri, 29 Nov 2024 21:54:36 -0600
Subject: [PATCH] [clang-format] Add BreakBeforeTemplateClose option
---
clang/docs/ClangFormatStyleOptions.rst | 21 ++++++
clang/docs/ReleaseNotes.rst | 1 +
clang/include/clang/Format/Format.h | 20 ++++++
clang/lib/Format/ContinuationIndenter.cpp | 19 ++++++
clang/lib/Format/Format.cpp | 2 +
clang/unittests/Format/ConfigParseTest.cpp | 1 +
clang/unittests/Format/FormatTest.cpp | 78 ++++++++++++++++++++++
7 files changed, 142 insertions(+)
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 4be448171699ca..84ab1b0a2eff61 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -3416,6 +3416,27 @@ the configuration (without a prefix: ``Auto``).
+.. _BreakBeforeTemplateClose:
+
+**BreakBeforeTemplateClose** (``Boolean``) :versionbadge:`clang-format 20` :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/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e44aefa90ab386..867d4b5d8c3f18 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -976,6 +976,7 @@ clang-format
``Never``, and ``true`` to ``Always``.
- Adds ``RemoveEmptyLinesInUnwrappedLines`` option.
- Adds ``KeepFormFeed`` option and set it to ``true`` for ``GNU`` style.
+- Adds ``BreakBeforeTemplateClose`` option.
libclang
--------
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 6383934afa2c40..bffd964f6aa8aa 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -2248,6 +2248,25 @@ 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
+ /// \version 20
+ bool BreakBeforeTemplateClose;
+
/// If ``true``, ternary operators will be placed after line breaks.
/// \code
/// true:
@@ -5184,6 +5203,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..f42574e135c91c 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);
@@ -1370,6 +1385,10 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
State.Stack.size() > 1) {
return State.Stack[State.Stack.size() - 2].LastSpace;
}
+ if (Current.ClosesTemplateDeclaration && Style.BreakBeforeTemplateClose &&
+ State.Stack.size() > 1) {
+ return State.Stack[State.Stack.size() - 2].LastSpace;
+ }
if (NextNonComment->is(TT_TemplateString) && NextNonComment->closesScope())
return State.Stack[State.Stack.size() - 2].LastSpace;
// Field labels in a nested type should be aligned to the brace. For example
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index ee52972ce66f4a..d6aa80dd8e3aef 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);
@@ -1514,6 +1515,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.BreakBeforeBraces = FormatStyle::BS_Attach;
LLVMStyle.BreakBeforeConceptDeclarations = FormatStyle::BBCDS_Always;
LLVMStyle.BreakBeforeInlineASMColon = FormatStyle::BBIAS_OnlyMultiline;
+ LLVMStyle.BreakBeforeTemplateClose = false;
LLVMStyle.BreakBeforeTernaryOperators = true;
LLVMStyle.BreakBinaryOperations = FormatStyle::BBO_Never;
LLVMStyle.BreakConstructorInitializers = FormatStyle::BCIS_BeforeColon;
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..53142fc95d0bf8 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -11077,6 +11077,84 @@ TEST_F(FormatTest, WrapsTemplateDeclarationsWithComments) {
Style);
}
+TEST_F(FormatTest, BreakBeforeTemplateClose) {
+ FormatStyle Style = getGoogleStyle(FormatStyle::LK_Cpp);
+ 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