Skip to content

Commit efb5b98

Browse files
committed
[clang-format] Add BreakBeforeTemplateClose option
1 parent b9ac390 commit efb5b98

File tree

7 files changed

+142
-0
lines changed

7 files changed

+142
-0
lines changed

clang/docs/ClangFormatStyleOptions.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3416,6 +3416,27 @@ the configuration (without a prefix: ``Auto``).
34163416

34173417

34183418

3419+
.. _BreakBeforeTemplateClose:
3420+
3421+
**BreakBeforeTemplateClose** (``Boolean``) :versionbadge:`clang-format 20` :ref:`<BreakBeforeTemplateClose>`
3422+
If ``true``, a line break will be placed before the ``>`` in a multiline
3423+
template declaration.
3424+
3425+
.. code-block:: c++
3426+
3427+
true:
3428+
template <
3429+
typename Foo,
3430+
typename Bar,
3431+
typename Baz
3432+
>
3433+
3434+
false:
3435+
template <
3436+
typename Foo,
3437+
typename Bar,
3438+
typename Baz>
3439+
34193440
.. _BreakBeforeTernaryOperators:
34203441

34213442
**BreakBeforeTernaryOperators** (``Boolean``) :versionbadge:`clang-format 3.7` :ref:`<BreakBeforeTernaryOperators>`

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,7 @@ clang-format
976976
``Never``, and ``true`` to ``Always``.
977977
- Adds ``RemoveEmptyLinesInUnwrappedLines`` option.
978978
- Adds ``KeepFormFeed`` option and set it to ``true`` for ``GNU`` style.
979+
- Adds ``BreakBeforeTemplateClose`` option.
979980

980981
libclang
981982
--------

clang/include/clang/Format/Format.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2248,6 +2248,25 @@ struct FormatStyle {
22482248
/// \version 16
22492249
BreakBeforeInlineASMColonStyle BreakBeforeInlineASMColon;
22502250

2251+
/// If ``true``, a line break will be placed before the ``>`` in a multiline
2252+
/// template declaration.
2253+
/// \code
2254+
/// true:
2255+
/// template <
2256+
/// typename Foo,
2257+
/// typename Bar,
2258+
/// typename Baz
2259+
/// >
2260+
///
2261+
/// false:
2262+
/// template <
2263+
/// typename Foo,
2264+
/// typename Bar,
2265+
/// typename Baz>
2266+
/// \endcode
2267+
/// \version 20
2268+
bool BreakBeforeTemplateClose;
2269+
22512270
/// If ``true``, ternary operators will be placed after line breaks.
22522271
/// \code
22532272
/// true:
@@ -5184,6 +5203,7 @@ struct FormatStyle {
51845203
BreakBeforeBraces == R.BreakBeforeBraces &&
51855204
BreakBeforeConceptDeclarations == R.BreakBeforeConceptDeclarations &&
51865205
BreakBeforeInlineASMColon == R.BreakBeforeInlineASMColon &&
5206+
BreakBeforeTemplateClose == R.BreakBeforeTemplateClose &&
51875207
BreakBeforeTernaryOperators == R.BreakBeforeTernaryOperators &&
51885208
BreakBinaryOperations == R.BreakBinaryOperations &&
51895209
BreakConstructorInitializers == R.BreakConstructorInitializers &&

clang/lib/Format/ContinuationIndenter.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,10 +382,25 @@ bool ContinuationIndenter::canBreak(const LineState &State) {
382382
return !State.NoLineBreak && !CurrentState.NoLineBreak;
383383
}
384384

385+
bool isMatchingBraceOnSameLine(const FormatToken *Token) {
386+
if (!Token->MatchingParen)
387+
return false;
388+
const FormatToken *Matching = Token->MatchingParen;
389+
const FormatToken *Current = Token;
390+
while (Current && Current != Matching) {
391+
if (Current->NewlinesBefore > 0)
392+
return false;
393+
Current = Current->Previous;
394+
}
395+
return true;
396+
}
397+
385398
bool ContinuationIndenter::mustBreak(const LineState &State) {
386399
const FormatToken &Current = *State.NextToken;
387400
const FormatToken &Previous = *Current.Previous;
388401
const auto &CurrentState = State.Stack.back();
402+
if (Current.ClosesTemplateDeclaration && Style.BreakBeforeTemplateClose)
403+
return !isMatchingBraceOnSameLine(State.NextToken);
389404
if (Style.BraceWrapping.BeforeLambdaBody && Current.CanBreakBefore &&
390405
Current.is(TT_LambdaLBrace) && Previous.isNot(TT_LineComment)) {
391406
auto LambdaBodyLength = getLengthToMatchingParen(Current, State.Stack);
@@ -1370,6 +1385,10 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
13701385
State.Stack.size() > 1) {
13711386
return State.Stack[State.Stack.size() - 2].LastSpace;
13721387
}
1388+
if (Current.ClosesTemplateDeclaration && Style.BreakBeforeTemplateClose &&
1389+
State.Stack.size() > 1) {
1390+
return State.Stack[State.Stack.size() - 2].LastSpace;
1391+
}
13731392
if (NextNonComment->is(TT_TemplateString) && NextNonComment->closesScope())
13741393
return State.Stack[State.Stack.size() - 2].LastSpace;
13751394
// Field labels in a nested type should be aligned to the brace. For example

clang/lib/Format/Format.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,7 @@ template <> struct MappingTraits<FormatStyle> {
10001000
IO.mapOptional("BreakBeforeBraces", Style.BreakBeforeBraces);
10011001
IO.mapOptional("BreakBeforeInlineASMColon",
10021002
Style.BreakBeforeInlineASMColon);
1003+
IO.mapOptional("BreakBeforeTemplateClose", Style.BreakBeforeTemplateClose);
10031004
IO.mapOptional("BreakBeforeTernaryOperators",
10041005
Style.BreakBeforeTernaryOperators);
10051006
IO.mapOptional("BreakBinaryOperations", Style.BreakBinaryOperations);
@@ -1514,6 +1515,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
15141515
LLVMStyle.BreakBeforeBraces = FormatStyle::BS_Attach;
15151516
LLVMStyle.BreakBeforeConceptDeclarations = FormatStyle::BBCDS_Always;
15161517
LLVMStyle.BreakBeforeInlineASMColon = FormatStyle::BBIAS_OnlyMultiline;
1518+
LLVMStyle.BreakBeforeTemplateClose = false;
15171519
LLVMStyle.BreakBeforeTernaryOperators = true;
15181520
LLVMStyle.BreakBinaryOperations = FormatStyle::BBO_Never;
15191521
LLVMStyle.BreakConstructorInitializers = FormatStyle::BCIS_BeforeColon;

clang/unittests/Format/ConfigParseTest.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
162162
CHECK_PARSE_BOOL(BinPackArguments);
163163
CHECK_PARSE_BOOL(BreakAdjacentStringLiterals);
164164
CHECK_PARSE_BOOL(BreakAfterJavaFieldAnnotations);
165+
CHECK_PARSE_BOOL(BreakBeforeTemplateClose);
165166
CHECK_PARSE_BOOL(BreakBeforeTernaryOperators);
166167
CHECK_PARSE_BOOL(BreakStringLiterals);
167168
CHECK_PARSE_BOOL(CompactNamespaces);

clang/unittests/Format/FormatTest.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11077,6 +11077,84 @@ TEST_F(FormatTest, WrapsTemplateDeclarationsWithComments) {
1107711077
Style);
1107811078
}
1107911079

11080+
TEST_F(FormatTest, BreakBeforeTemplateClose) {
11081+
FormatStyle Style = getGoogleStyle(FormatStyle::LK_Cpp);
11082+
Style.ColumnLimit = 0;
11083+
verifyNoChange("template <typename Foo>\n"
11084+
"void foo() {}",
11085+
Style);
11086+
verifyNoChange("template <\n"
11087+
" typename Foo,\n"
11088+
" typename Bar>\n"
11089+
"void foo() {}",
11090+
Style);
11091+
// when BreakBeforeTemplateClose is off, this line break is removed:
11092+
verifyFormat("template <\n"
11093+
" typename Foo,\n"
11094+
" typename Bar>\n"
11095+
"void foo() {}",
11096+
"template <\n"
11097+
" typename Foo,\n"
11098+
" typename Bar\n"
11099+
">\n"
11100+
"void foo() {}",
11101+
Style);
11102+
Style.BreakBeforeTemplateClose = true;
11103+
// BreakBeforeTemplateClose should NOT force multiline templates
11104+
verifyNoChange("template <typename Foo>\n"
11105+
"void foo() {}",
11106+
Style);
11107+
verifyNoChange("template <typename Foo, typename Bar>\n"
11108+
"void foo() {}",
11109+
Style);
11110+
// it should allow a line break:
11111+
verifyNoChange("template <\n"
11112+
" typename Foo\n"
11113+
">\n"
11114+
"void foo() {}",
11115+
Style);
11116+
verifyNoChange("template <\n"
11117+
" typename Foo,\n"
11118+
" typename Bar\n"
11119+
">\n"
11120+
"void foo() {}",
11121+
Style);
11122+
// it should add a line break if not already present:
11123+
verifyFormat("template <\n"
11124+
" typename Foo\n"
11125+
">\n"
11126+
"void foo() {}",
11127+
"template <\n"
11128+
" typename Foo>\n"
11129+
"void foo() {}",
11130+
Style);
11131+
verifyFormat("template <\n"
11132+
" typename Foo,\n"
11133+
" typename Bar\n"
11134+
">\n"
11135+
"void foo() {}",
11136+
"template <\n"
11137+
" typename Foo,\n"
11138+
" typename Bar>\n"
11139+
"void foo() {}",
11140+
Style);
11141+
// when within an indent scope, the > should be placed appropriately:
11142+
verifyFormat("struct Baz {\n"
11143+
" template <\n"
11144+
" typename Foo,\n"
11145+
" typename Bar\n"
11146+
" >\n"
11147+
" void foo() {}\n"
11148+
"};",
11149+
"struct Baz {\n"
11150+
" template <\n"
11151+
" typename Foo,\n"
11152+
" typename Bar>\n"
11153+
" void foo() {}\n"
11154+
"};",
11155+
Style);
11156+
}
11157+
1108011158
TEST_F(FormatTest, WrapsTemplateParameters) {
1108111159
FormatStyle Style = getLLVMStyle();
1108211160
Style.AlignAfterOpenBracket = FormatStyle::BAS_DontAlign;

0 commit comments

Comments
 (0)