Skip to content

[clang-format] Add BreakBeforeTemplateCloser option #118046

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 34 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1caf823
[clang-format] Add BreakBeforeTemplateClose option
leijurv Nov 30, 2024
212e444
add test case
leijurv Dec 7, 2024
3927d41
more tests
leijurv Dec 9, 2024
2c3a64c
more tests
leijurv Dec 22, 2024
abedc01
Merge branch 'main' into BreakBeforeTemplateClose
leijurv Dec 30, 2024
29742cd
Merge branch 'main' into BreakBeforeTemplateClose
leijurv Jan 2, 2025
f96441d
Merge branch 'main' into BreakBeforeTemplateClose
leijurv Jan 6, 2025
19bc40e
address comments
leijurv Jan 11, 2025
f75268a
address comments
leijurv Jan 12, 2025
1304288
address comments
leijurv Jan 12, 2025
9277a68
switch from bool to enum
leijurv Jan 12, 2025
0952882
switch from multiline to blockindent
leijurv Jan 13, 2025
ed0289f
one last test
leijurv Jan 13, 2025
53766dd
rerun dump format style
leijurv Jan 13, 2025
605ade3
Merge branch 'main' into BreakBeforeTemplateClose
leijurv Jan 14, 2025
deb01fc
Merge branch 'main' into BreakBeforeTemplateClose
leijurv Jan 16, 2025
b1a5445
Merge branch 'main' into BreakBeforeTemplateClose
leijurv Jan 20, 2025
fa15aca
address comments
leijurv Jan 20, 2025
c0090c8
address comments
leijurv Jan 28, 2025
2973647
Merge branch 'main' into BreakBeforeTemplateClose
leijurv Jan 28, 2025
9c684c8
Merge branch 'main' into BreakBeforeTemplateClose
leijurv Jan 28, 2025
dc65a0d
Merge branch 'main' into BreakBeforeTemplateClose
leijurv Jan 31, 2025
9381a00
bump version to 21
leijurv Jan 31, 2025
a437f23
also need to bump version here
leijurv Jan 31, 2025
a768adb
switch back to bool
leijurv Feb 2, 2025
e1eaba9
address comments
leijurv Feb 4, 2025
7d456ca
empty commit to kick github
leijurv Feb 5, 2025
b6219e2
address comment
leijurv Feb 5, 2025
2cc5a69
address comments
leijurv Feb 6, 2025
4faf28a
address comments
leijurv Feb 6, 2025
9cf6bcc
Update clang/include/clang/Format/Format.h
leijurv Feb 6, 2025
b5a5e96
regenerate docs
leijurv Feb 6, 2025
51f3b9a
NFC: minor cleanup
owenca Feb 6, 2025
00bad0e
Remove trailing whitespace in ContinuationIndenter.cpp
owenca Feb 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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>`
Expand Down
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
--------
Expand Down
20 changes: 20 additions & 0 deletions clang/include/clang/Format/Format.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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 &&
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/Format/ContinuationIndenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,10 @@ bool ContinuationIndenter::mustBreak(const LineState &State) {
}
if (CurrentState.BreakBeforeClosingParen && Current.is(tok::r_paren))
return true;
if (CurrentState.BreakBeforeClosingAngle && Current.is(TT_TemplateCloser) &&
Style.BreakBeforeTemplateClose) {
return true;
}
if (Style.Language == FormatStyle::LK_ObjC &&
Style.ObjCBreakBeforeNestedBlockParam &&
Current.ObjCSelectorNameParts > 1 &&
Expand Down Expand Up @@ -1234,6 +1238,9 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
Style.AlignAfterOpenBracket == FormatStyle::BAS_BlockIndent;
}

if (PreviousNonComment && PreviousNonComment->is(tok::less))
CurrentState.BreakBeforeClosingAngle = true;

if (CurrentState.AvoidBinPacking) {
// If we are breaking after '(', '{', '<', or this is the break after a ':'
// to start a member initializer list in a constructor, this should not
Expand Down Expand Up @@ -1370,6 +1377,10 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
State.Stack.size() > 1) {
return State.Stack[State.Stack.size() - 2].LastSpace;
}
if (Current.is(TT_TemplateCloser) && 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
Expand Down
26 changes: 18 additions & 8 deletions clang/lib/Format/ContinuationIndenter.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,14 +200,15 @@ struct ParenState {
: Tok(Tok), Indent(Indent), LastSpace(LastSpace),
NestedBlockIndent(Indent), IsAligned(false),
BreakBeforeClosingBrace(false), BreakBeforeClosingParen(false),
AvoidBinPacking(AvoidBinPacking), BreakBeforeParameter(false),
NoLineBreak(NoLineBreak), NoLineBreakInOperand(false),
LastOperatorWrapped(true), ContainsLineBreak(false),
ContainsUnwrappedBuilder(false), AlignColons(true),
ObjCSelectorNameFound(false), HasMultipleNestedBlocks(false),
NestedBlockInlined(false), IsInsideObjCArrayLiteral(false),
IsCSharpGenericTypeConstraint(false), IsChainedConditional(false),
IsWrappedConditional(false), UnindentOperator(false) {}
BreakBeforeClosingAngle(false), AvoidBinPacking(AvoidBinPacking),
BreakBeforeParameter(false), NoLineBreak(NoLineBreak),
NoLineBreakInOperand(false), LastOperatorWrapped(true),
ContainsLineBreak(false), ContainsUnwrappedBuilder(false),
AlignColons(true), ObjCSelectorNameFound(false),
HasMultipleNestedBlocks(false), NestedBlockInlined(false),
IsInsideObjCArrayLiteral(false), IsCSharpGenericTypeConstraint(false),
IsChainedConditional(false), IsWrappedConditional(false),
UnindentOperator(false) {}

/// \brief The token opening this parenthesis level, or nullptr if this level
/// is opened by fake parenthesis.
Expand Down Expand Up @@ -280,6 +281,13 @@ struct ParenState {
/// was a newline after the beginning left paren.
bool BreakBeforeClosingParen : 1;

/// Whether a newline needs to be inserted before the block's closing
/// angle < >.
///
/// We only want to insert a newline before the closing angle if there also
/// was a newline after the beginning left angle.
bool BreakBeforeClosingAngle : 1;

/// Avoid bin packing, i.e. multiple parameters/elements on multiple
/// lines, in this context.
bool AvoidBinPacking : 1;
Expand Down Expand Up @@ -367,6 +375,8 @@ struct ParenState {
return BreakBeforeClosingBrace;
if (BreakBeforeClosingParen != Other.BreakBeforeClosingParen)
return BreakBeforeClosingParen;
if (BreakBeforeClosingAngle != Other.BreakBeforeClosingAngle)
return BreakBeforeClosingAngle;
if (QuestionColumn != Other.QuestionColumn)
return QuestionColumn < Other.QuestionColumn;
if (AvoidBinPacking != Other.AvoidBinPacking)
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6254,7 +6254,7 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,
return false;

if (Right.is(TT_TemplateCloser))
return false;
return Style.BreakBeforeTemplateClose;
if (Right.is(tok::r_square) && Right.MatchingParen &&
Right.MatchingParen->is(TT_LambdaLSquare)) {
return false;
Expand Down
1 change: 1 addition & 0 deletions clang/unittests/Format/ConfigParseTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
151 changes: 151 additions & 0 deletions clang/unittests/Format/FormatTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11077,6 +11077,157 @@ 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 from issue #80049
verifyFormat(
"void foo() {\n"
" using type = std::remove_cv_t<\n"
" add_common_cv_reference<\n"
" std::common_type_t<std::decay_t<T0>, std::decay_t<T1>>,\n"
" T0,\n"
" T1\n"
" >\n"
" >;\n"
"}\n",
"void foo() {\n"
" using type = std::remove_cv_t<\n"
" add_common_cv_reference<\n"
" std::common_type_t<std::decay_t<T0>, std::decay_t<T1>>,\n"
" T0,\n"
" T1>>;\n"
"}\n",
Style);

// now test that it handles the cases when the column limit forces wrapping
Style.ColumnLimit = 40;
// when the column limit allows it, the template should be combined back into
// one line:
verifyFormat("template <typename Foo, typename Bar>\n"
"void foo() {}",
"template <\n"
" typename Foo,\n"
" typename Bar\n"
">\n"
"void foo() {}",
Style);
// but not when the name is looong
verifyFormat("template <\n"
" typename Foo,\n"
" typename Barrrrrrrrrrrrrrrrrrrrrrrrrr\n"
">\n"
"void foo() {}",
Style);
verifyFormat("template <\n"
" typename Fooooooooooooooooooooooooooo,\n"
" typename Bar\n"
">\n"
"void foo() {}",
Style);
// additionally, long names should be split in one step:
verifyFormat(
"template <\n"
" typename Foo,\n"
" typename Barrrrrrrrrrrrrrrrrrrrrrrrrr\n"
">\n"
"void foo() {}",
"template <typename Foo, typename Barrrrrrrrrrrrrrrrrrrrrrrrrr>\n"
"void foo() {}",
Style);
verifyFormat(
"template <\n"
" typename Fooooooooooooooooooooooooooo,\n"
" typename Bar\n"
">\n"
"void foo() {}",
"template <typename Fooooooooooooooooooooooooooo, typename Bar>\n"
"void foo() {}",
Style);
// even when there is only one long name:
verifyFormat("template <\n"
" typename Fooooooooooooooooooooooooooo\n"
">\n"
"void foo() {}",
"template <typename Fooooooooooooooooooooooooooo>\n"
"void foo() {}",
Style);
}

TEST_F(FormatTest, WrapsTemplateParameters) {
FormatStyle Style = getLLVMStyle();
Style.AlignAfterOpenBracket = FormatStyle::BAS_DontAlign;
Expand Down
Loading