Skip to content

[clang-format] Add ApplyAlwaysOnePerLineToTemplateArguments option #137544

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
41 changes: 41 additions & 0 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2140,6 +2140,47 @@ the configuration (without a prefix: ``Auto``).
**AlwaysBreakTemplateDeclarations** (``deprecated``) :versionbadge:`clang-format 3.4` :ref:`¶ <AlwaysBreakTemplateDeclarations>`
This option is renamed to ``BreakTemplateDeclarations``.

.. _ApplyAlwaysOnePerLineToTemplateArguments:

**ApplyAlwaysOnePerLineToTemplateArguments** (``Boolean``) :versionbadge:`clang-format 21` :ref:`¶ <ApplyAlwaysOnePerLineToTemplateArguments>`
If ``BinPackParameters`` is set to ``AlwaysOnePerLine``, specifies whether
template argument lists should also be split across multiple lines.

When set to ``true``, each template argument will be placed on its own
line. When set to ``false``, template argument lists remain compact even
when function parameters are broken one per line.


.. code-block:: c++

true:
template <typename T, int N>
struct Foo {
T mData[N];

Foo<T,
N>
operator+(const Foo<T,
N> &other) const {}

Foo<T,
N>
bar(const Foo<T,
N> &other,
float t) const {}
};

false:
template <typename T, int N>
struct Foo {
T mData[N];

Foo<T, N> operator+(const Foo<T, N> &other) const {}

Foo<T, N> bar(const Foo<T, N> &other,
float t) const {}
};

.. _AttributeMacros:

**AttributeMacros** (``List of Strings``) :versionbadge:`clang-format 12` :ref:`¶ <AttributeMacros>`
Expand Down
41 changes: 41 additions & 0 deletions clang/include/clang/Format/Format.h
Original file line number Diff line number Diff line change
Expand Up @@ -1259,6 +1259,45 @@ struct FormatStyle {
/// \version 3.7
BinPackParametersStyle BinPackParameters;

/// If ``BinPackParameters`` is set to ``AlwaysOnePerLine``, specifies whether
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a reference on BinPackParameters to this one.

/// template argument lists should also be split across multiple lines.
///
/// When set to ``true``, each template argument will be placed on its own
/// line. When set to ``false``, template argument lists remain compact even
/// when function parameters are broken one per line.
///
/// \code
/// true:
/// template <typename T, int N>
/// struct Foo {
/// T mData[N];
///
/// Foo<T,
/// N>
/// operator+(const Foo<T,
/// N> &other) const {}
///
/// Foo<T,
/// N>
/// bar(const Foo<T,
/// N> &other,
/// float t) const {}
/// };
///
/// false:
/// template <typename T, int N>
/// struct Foo {
/// T mData[N];
///
/// Foo<T, N> operator+(const Foo<T, N> &other) const {}
///
/// Foo<T, N> bar(const Foo<T, N> &other,
/// float t) const {}
/// };
/// \endcode
/// \version 21
bool ApplyAlwaysOnePerLineToTemplateArguments;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sort alphabetically.


/// Styles for adding spacing around ``:`` in bitfield definitions.
enum BitFieldColonSpacingStyle : int8_t {
/// Add one space on each side of the ``:``
Expand Down Expand Up @@ -5326,6 +5365,8 @@ struct FormatStyle {
BinPackArguments == R.BinPackArguments &&
BinPackLongBracedList == R.BinPackLongBracedList &&
BinPackParameters == R.BinPackParameters &&
ApplyAlwaysOnePerLineToTemplateArguments ==
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also sorting.

R.ApplyAlwaysOnePerLineToTemplateArguments &&
BitFieldColonSpacing == R.BitFieldColonSpacing &&
BracedInitializerIndentWidth == R.BracedInitializerIndentWidth &&
BreakAdjacentStringLiterals == R.BreakAdjacentStringLiterals &&
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,8 @@ template <> struct MappingTraits<FormatStyle> {
IO.mapOptional("BinPackArguments", Style.BinPackArguments);
IO.mapOptional("BinPackLongBracedList", Style.BinPackLongBracedList);
IO.mapOptional("BinPackParameters", Style.BinPackParameters);
IO.mapOptional("ApplyAlwaysOnePerLineToTemplateArguments",
Style.ApplyAlwaysOnePerLineToTemplateArguments);
IO.mapOptional("BitFieldColonSpacing", Style.BitFieldColonSpacing);
IO.mapOptional("BracedInitializerIndentWidth",
Style.BracedInitializerIndentWidth);
Expand Down Expand Up @@ -1521,6 +1523,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.BinPackArguments = true;
LLVMStyle.BinPackLongBracedList = true;
LLVMStyle.BinPackParameters = FormatStyle::BPPS_BinPack;
LLVMStyle.ApplyAlwaysOnePerLineToTemplateArguments = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the current behavior? is this the correct default?

LLVMStyle.BitFieldColonSpacing = FormatStyle::BFCS_Both;
LLVMStyle.BracedInitializerIndentWidth = -1;
LLVMStyle.BraceWrapping = {/*AfterCaseLabel=*/false,
Expand Down
13 changes: 8 additions & 5 deletions clang/lib/Format/FormatToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -309,11 +309,11 @@ struct FormatToken {
IsUnterminatedLiteral(false), CanBreakBefore(false),
ClosesTemplateDeclaration(false), StartsBinaryExpression(false),
EndsBinaryExpression(false), PartOfMultiVariableDeclStmt(false),
ContinuesLineCommentSection(false), Finalized(false),
ClosesRequiresClause(false), EndsCppAttributeGroup(false),
BlockKind(BK_Unknown), Decision(FD_Unformatted),
PackingKind(PPK_Inconclusive), TypeIsFinalized(false),
Type(TT_Unknown) {}
InTemplateArgumentList(false), ContinuesLineCommentSection(false),
Finalized(false), ClosesRequiresClause(false),
EndsCppAttributeGroup(false), BlockKind(BK_Unknown),
Decision(FD_Unformatted), PackingKind(PPK_Inconclusive),
TypeIsFinalized(false), Type(TT_Unknown) {}

/// The \c Token.
Token Tok;
Expand Down Expand Up @@ -373,6 +373,9 @@ struct FormatToken {
/// Only set if \c Type == \c TT_StartOfName.
unsigned PartOfMultiVariableDeclStmt : 1;

/// \c true if this token is part of a template argument list.
unsigned InTemplateArgumentList : 1;

/// Does this line comment continue a line comment section?
///
/// Only set to true if \c Type == \c TT_LineComment.
Expand Down
16 changes: 16 additions & 0 deletions clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1977,6 +1977,19 @@ class AnnotatingParser {
return Type;
}

void markTokenAsTemplateArgumentInLine() {
int TemplateDepth = 0;
for (FormatToken *Tok = Line.First; Tok; Tok = Tok->Next) {
if (Tok->is(TT_TemplateCloser))
--TemplateDepth;

Tok->InTemplateArgumentList = (TemplateDepth > 0);

if (Tok->is(TT_TemplateOpener))
++TemplateDepth;
}
}

public:
LineType parseLine() {
if (!CurrentToken)
Expand Down Expand Up @@ -2074,6 +2087,7 @@ class AnnotatingParser {
if (ctx.ContextType == Context::StructArrayInitializer)
return LT_ArrayOfStructInitializer;

markTokenAsTemplateArgumentInLine();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you want this before the preceding loop. And can you get it into the first pass of the line? Just going through all tokens again doesn't seem nice.

return LT_Other;
}

Expand Down Expand Up @@ -5619,6 +5633,8 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
// BreakFunctionDefinitionParameters or AlignAfterOpenBracket.
if (Style.BinPackParameters == FormatStyle::BPPS_AlwaysOnePerLine &&
Line.MightBeFunctionDecl && !Left.opensScope() &&
(Style.ApplyAlwaysOnePerLineToTemplateArguments ||
!Left.InTemplateArgumentList) &&
startsNextParameter(Right, Style)) {
return true;
}
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Format/TokenAnnotator.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ class AnnotatedLine {
bool MightBeFunctionDecl;
bool IsMultiVariableDeclStmt;

/// \c True if this token is part o a template declaration.
bool InTemplateDecl = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A leftover?


/// \c True if this line contains a macro call for which an expansion exists.
bool ContainsMacroCall = false;

Expand Down
6 changes: 6 additions & 0 deletions clang/unittests/Format/ConfigParseTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,12 @@ TEST(ConfigParseTest, ParsesConfiguration) {
CHECK_PARSE("BinPackParameters: false", BinPackParameters,
FormatStyle::BPPS_OnePerLine);

Style.ApplyAlwaysOnePerLineToTemplateArguments = false;
CHECK_PARSE("ApplyAlwaysOnePerLineToTemplateArguments: true",
ApplyAlwaysOnePerLineToTemplateArguments, true);
CHECK_PARSE("ApplyAlwaysOnePerLineToTemplateArguments: false",
ApplyAlwaysOnePerLineToTemplateArguments, false);

Style.PackConstructorInitializers = FormatStyle::PCIS_BinPack;
CHECK_PARSE("PackConstructorInitializers: Never", PackConstructorInitializers,
FormatStyle::PCIS_Never);
Expand Down
49 changes: 49 additions & 0 deletions clang/unittests/Format/FormatTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9024,6 +9024,55 @@ TEST_F(FormatTest, FormatsDeclarationBreakAlways) {
BreakAlways);
}

TEST_F(FormatTest, ApplyAlwaysOnePerLineToTemplateArguments) {
FormatStyle Style = getGoogleStyle();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why google style?

Style.BinPackParameters = FormatStyle::BPPS_AlwaysOnePerLine;

// Case 1: Template arguments split by AlwaysOnePerLine
Style.ApplyAlwaysOnePerLineToTemplateArguments = true;
verifyFormat("template <typename T, int N>\n"
"struct Foo {\n"
" T mData[N];\n"
" Foo<T,\n"
" N>\n"
" operator+(const Foo<T,\n"
" N> &other) const {}\n"
" Foo<T,\n"
" N>\n"
" bar(const Foo<T,\n"
" N> &other,\n"
" float t) const {}\n"
"};\n",
Style);

// Case 2: Template arguments not split by The
// ApplyAlwaysOnePerLineToTemplateArguments
Style.ApplyAlwaysOnePerLineToTemplateArguments = false;
verifyFormat("template <typename T, int N>\n"
"struct Foo {\n"
" T mData[N];\n"
" Foo<T, N> operator+(const Foo<T, N> &other) const {}\n"
" Foo<T, N> bar(const Foo<T, N> &other,\n"
" float t) const {}\n"
"};\n",
Style);

// Case 3: Template arguments not split by the
// ApplyAlwaysOnePerLineToTemplateArguments but using the
// BreakFunctionDefinitionParameters flag
Style.BreakFunctionDefinitionParameters = true;
verifyFormat("template <typename T, int N>\n"
"struct Foo {\n"
" T mData[N];\n"
" Foo<T, N> operator+(\n"
" const Foo<T, N> &other) const {}\n"
" Foo<T, N> bar(\n"
" const Foo<T, N> &other,\n"
" float t) const {}\n"
"};\n",
Style);
}

TEST_F(FormatTest, FormatsDefinitionBreakAlways) {
FormatStyle BreakAlways = getGoogleStyle();
BreakAlways.BinPackParameters = FormatStyle::BPPS_AlwaysOnePerLine;
Expand Down
60 changes: 60 additions & 0 deletions clang/unittests/Format/FormatTestComments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,66 @@ TEST_F(FormatTestComments, UnderstandsBlockComments) {
" int jjj; /*b*/");
}

TEST_F(FormatTestComments,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's your reason for this test? You didn't change anything comment related, right?

AlwaysOnePerLineRespectsTemplateArgumentsFlagWithComments) {
FormatStyle Style = getGoogleStyle();
Style.BinPackParameters = FormatStyle::BPPS_AlwaysOnePerLine;

// Case 1: Template arguments split by AlwaysOnePerLine
Style.ApplyAlwaysOnePerLineToTemplateArguments = true;
verifyFormat("template <typename T, // comment\n"
" int N> // comment\n"
"struct Foo {\n"
" T mData[N];\n"
" Foo<T,\n"
" N>\n"
" operator+(const Foo<T,\n"
" N> &other) const { // comment\n"
" }\n"
" Foo<T,\n"
" N>\n"
" bar(const Foo<T,\n"
" N> &other, // comment\n"
" float t) const { // comment\n"
" }\n"
"};\n",
Style);

// Case 2: Template arguments not split by The
// ApplyAlwaysOnePerLineToTemplateArguments
Style.ApplyAlwaysOnePerLineToTemplateArguments = false;
verifyFormat(
"template <typename T, // comment\n"
" int N> // comment\n"
"struct Foo {\n"
" T mData[N];\n"
" Foo<T, N> operator+(const Foo<T, N> &other) const { // comment\n"
" }\n"
" Foo<T, N> bar(const Foo<T, N> &other, // comment\n"
" float t) const { // comment\n"
" }\n"
"};\n",
Style);

// Case 3: Template arguments not split by the
// ApplyAlwaysOnePerLineToTemplateArguments but using the
// BreakFunctionDefinitionParameters flag
Style.BreakFunctionDefinitionParameters = true;
verifyFormat("template <typename T, // comment\n"
" int N> // comment\n"
"struct Foo {\n"
" T mData[N];\n"
" Foo<T, N> operator+(\n"
" const Foo<T, N> &other) const { // comment\n"
" }\n"
" Foo<T, N> bar(\n"
" const Foo<T, N> &other, // comment\n"
" float t) const { // comment\n"
" }\n"
"};\n",
Style);
}

TEST_F(FormatTestComments, AlignsBlockComments) {
EXPECT_EQ("/*\n"
" * Really multi-line\n"
Expand Down
Loading