Skip to content

Commit c7b34d1

Browse files
committed
Format: add AlignAfterOpenBracketOptions
Introduce new options to allow for control of AlwaysBreak and BlockIndent selectively for If conditional statements (as currently supported), other conditional statements (for/while/switch), and other statements. Fixes #67738. Fixes #79176. Fixes #80123.
1 parent 00f692b commit c7b34d1

File tree

7 files changed

+355
-11
lines changed

7 files changed

+355
-11
lines changed

clang/docs/ClangFormatStyleOptions.rst

+60
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,66 @@ the configuration (without a prefix: ``Auto``).
246246

247247

248248

249+
.. _AlignAfterOpenBracketBreak:
250+
251+
**AlignAfterOpenBracketBreak** (``AlignAfterOpenBracketCustom``) :versionbadge:`clang-format 20` :ref:`<AlignAfterOpenBracketBreak>`
252+
Control of when ``AlignAfterOpenBracket`` breaks an opening bracket.
253+
254+
If ``AlignAfterOpenBracket`` is set to ``AlwaysBreak`` or ``BlockIndent``,
255+
use this to specify how different cases of breaking the opening brackets
256+
should be handled. Otherwise, this is ignored. Setting any of these to
257+
``false`` will cause them to not break. At least one of these must be set
258+
to ``true``, otherwise a default (backward compatible) breaking behavior
259+
is used. This is ignored for ``Align`` and ``DontAlign``.
260+
261+
.. code-block:: c++
262+
263+
# Example of usage:
264+
AlignAfterOpenBracket: AlwaysBreak
265+
AlignAfterOpenBracketBreak:
266+
InIfConditionalStatements: true
267+
InOtherConditionalStatements: false
268+
Other: true
269+
270+
Nested configuration flags:
271+
272+
Precise control over breaking the opening bracket of
273+
``AlignAfterOpenBracket``.
274+
275+
.. code-block:: c++
276+
277+
# Should be declared this way:
278+
AlignAfterOpenBracketBreak:
279+
InIfConditionalStatements: true
280+
InOtherConditionalStatements: false
281+
Other: true
282+
283+
* ``bool InIfConditionalStatements`` Break inside if/else if statements.
284+
285+
.. code-block:: c++
286+
287+
true: false:
288+
if constexpr ( vs. if constexpr (a ||
289+
a || b) b)
290+
291+
* ``bool InOtherConditionalStatements`` Break inside conditional statements not covered by preceding options.
292+
(``for/while/switch...``).
293+
294+
.. code-block:: c++
295+
296+
true: false:
297+
while ( vs. while (a &&
298+
a && b ) { b) {
299+
300+
* ``bool Other`` Break inside brackets not covered by preceding options.
301+
302+
.. code-block:: c++
303+
304+
true: false:
305+
someLongFunction( vs. someLongFunction(argument1,
306+
argument1, argument2); argument2);
307+
308+
249309
.. _AlignArrayOfStructures:
250310

251311
**AlignArrayOfStructures** (``ArrayInitializerAlignmentStyle``) :versionbadge:`clang-format 13` :ref:`<AlignArrayOfStructures>`

clang/include/clang/Format/Format.h

+73
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,78 @@ struct FormatStyle {
106106
/// \version 3.8
107107
BracketAlignmentStyle AlignAfterOpenBracket;
108108

109+
/// Precise control over breaking the opening bracket of
110+
/// ``AlignAfterOpenBracket``.
111+
/// \code
112+
/// # Should be declared this way:
113+
/// AlignAfterOpenBracketBreak:
114+
/// InIfConditionalStatements: true
115+
/// InOtherConditionalStatements: false
116+
/// Other: true
117+
/// \endcode
118+
struct AlignAfterOpenBracketCustom {
119+
/// Break inside if/else if statements.
120+
/// \code
121+
/// true: false:
122+
/// if constexpr ( vs. if constexpr (a ||
123+
/// a || b) b)
124+
/// \endcode
125+
bool InIfConditionalStatements;
126+
/// Break inside conditional statements not covered by preceding options.
127+
/// (``for/while/switch...``).
128+
/// \code
129+
/// true: false:
130+
/// while ( vs. while (a &&
131+
/// a && b ) { b) {
132+
/// \endcode
133+
bool InOtherConditionalStatements;
134+
/// Break inside brackets not covered by preceding options.
135+
/// \code
136+
/// true: false:
137+
/// someLongFunction( vs. someLongFunction(argument1,
138+
/// argument1, argument2); argument2);
139+
/// \endcode
140+
bool Other;
141+
142+
AlignAfterOpenBracketCustom()
143+
: InIfConditionalStatements(false), InOtherConditionalStatements(false),
144+
Other(false) {}
145+
146+
AlignAfterOpenBracketCustom(bool InIfConditionalStatements,
147+
bool InOtherConditionalStatements, bool Other)
148+
: InIfConditionalStatements(InIfConditionalStatements),
149+
InOtherConditionalStatements(InOtherConditionalStatements),
150+
Other(Other) {}
151+
152+
bool operator==(const AlignAfterOpenBracketCustom &R) const {
153+
return InIfConditionalStatements == R.InIfConditionalStatements &&
154+
InOtherConditionalStatements == R.InOtherConditionalStatements &&
155+
Other == R.Other;
156+
}
157+
bool operator!=(const AlignAfterOpenBracketCustom &R) const {
158+
return !(*this == R);
159+
}
160+
};
161+
162+
/// Control of when ``AlignAfterOpenBracket`` breaks an opening bracket.
163+
///
164+
/// If ``AlignAfterOpenBracket`` is set to ``AlwaysBreak`` or ``BlockIndent``,
165+
/// use this to specify how different cases of breaking the opening brackets
166+
/// should be handled. Otherwise, this is ignored. Setting any of these to
167+
/// ``false`` will cause them to not break. At least one of these must be set
168+
/// to ``true``, otherwise a default (backward compatible) breaking behavior
169+
/// is used. This is ignored for ``Align`` and ``DontAlign``.
170+
/// \code
171+
/// # Example of usage:
172+
/// AlignAfterOpenBracket: AlwaysBreak
173+
/// AlignAfterOpenBracketBreak:
174+
/// InIfConditionalStatements: true
175+
/// InOtherConditionalStatements: false
176+
/// Other: true
177+
/// \endcode
178+
/// \version 20
179+
AlignAfterOpenBracketCustom AlignAfterOpenBracketBreak;
180+
109181
/// Different style for aligning array initializers.
110182
enum ArrayInitializerAlignmentStyle : int8_t {
111183
/// Align array column and left justify the columns e.g.:
@@ -5198,6 +5270,7 @@ struct FormatStyle {
51985270
bool operator==(const FormatStyle &R) const {
51995271
return AccessModifierOffset == R.AccessModifierOffset &&
52005272
AlignAfterOpenBracket == R.AlignAfterOpenBracket &&
5273+
AlignAfterOpenBracketBreak == R.AlignAfterOpenBracketBreak &&
52015274
AlignArrayOfStructures == R.AlignArrayOfStructures &&
52025275
AlignConsecutiveAssignments == R.AlignConsecutiveAssignments &&
52035276
AlignConsecutiveBitFields == R.AlignConsecutiveBitFields &&

clang/lib/Format/ContinuationIndenter.cpp

+34-10
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,11 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
799799
// parenthesis by disallowing any further line breaks if there is no line
800800
// break after the opening parenthesis. Don't break if it doesn't conserve
801801
// columns.
802+
auto IsOtherConditional = [&](const FormatToken &Tok) {
803+
return Tok.isOneOf(tok::kw_for, tok::kw_while, tok::kw_switch) ||
804+
(Style.isJavaScript() && Tok.is(Keywords.kw_await) && Tok.Previous &&
805+
Tok.Previous->is(tok::kw_for));
806+
};
802807
auto IsOpeningBracket = [&](const FormatToken &Tok) {
803808
auto IsStartOfBracedList = [&]() {
804809
return Tok.is(tok::l_brace) && Tok.isNot(BK_Block) &&
@@ -811,10 +816,11 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
811816
if (!Tok.Previous)
812817
return true;
813818
if (Tok.Previous->isIf())
814-
return Style.AlignAfterOpenBracket == FormatStyle::BAS_AlwaysBreak;
815-
return !Tok.Previous->isOneOf(TT_CastRParen, tok::kw_for, tok::kw_while,
816-
tok::kw_switch) &&
817-
!(Style.isJavaScript() && Tok.Previous->is(Keywords.kw_await));
819+
return Style.AlignAfterOpenBracketBreak.InIfConditionalStatements;
820+
if (IsOtherConditional(*Tok.Previous))
821+
return Style.AlignAfterOpenBracketBreak.InOtherConditionalStatements;
822+
return !Tok.Previous->is(TT_CastRParen) &&
823+
Style.AlignAfterOpenBracketBreak.Other;
818824
};
819825
auto IsFunctionCallParen = [](const FormatToken &Tok) {
820826
return Tok.is(tok::l_paren) && Tok.ParameterCount > 0 && Tok.Previous &&
@@ -851,10 +857,15 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
851857
Tok.isOneOf(tok::ellipsis, Keywords.kw_await))) {
852858
return true;
853859
}
854-
const auto *Previous = Tok.Previous;
855-
if (!Previous || (!Previous->isOneOf(TT_FunctionDeclarationLParen,
856-
TT_LambdaDefinitionLParen) &&
857-
!IsFunctionCallParen(*Previous))) {
860+
const auto *Previous = TokAfterLParen.Previous;
861+
assert(Previous); // IsOpeningBracket(Previous)
862+
if (Previous->Previous && (Previous->Previous->isIf() ||
863+
IsOtherConditional(*Previous->Previous))) {
864+
return false;
865+
}
866+
if (!Previous->isOneOf(TT_FunctionDeclarationLParen,
867+
TT_LambdaDefinitionLParen) &&
868+
!IsFunctionCallParen(*Previous)) {
858869
return true;
859870
}
860871
if (IsOpeningBracket(Tok) || IsInTemplateString(Tok))
@@ -1232,8 +1243,21 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
12321243
}
12331244

12341245
if (PreviousNonComment && PreviousNonComment->is(tok::l_paren)) {
1235-
CurrentState.BreakBeforeClosingParen =
1236-
Style.AlignAfterOpenBracket == FormatStyle::BAS_BlockIndent;
1246+
CurrentState.BreakBeforeClosingParen = false;
1247+
if (Style.AlignAfterOpenBracket == FormatStyle::BAS_BlockIndent) {
1248+
auto Previous = PreviousNonComment->Previous;
1249+
if (Previous && Previous->isIf()) {
1250+
CurrentState.BreakBeforeClosingParen =
1251+
Style.AlignAfterOpenBracketBreak.InIfConditionalStatements;
1252+
} else if (Previous && Previous->isOneOf(tok::kw_for, tok::kw_while,
1253+
tok::kw_switch)) {
1254+
CurrentState.BreakBeforeClosingParen =
1255+
Style.AlignAfterOpenBracketBreak.InOtherConditionalStatements;
1256+
} else {
1257+
CurrentState.BreakBeforeClosingParen =
1258+
Style.AlignAfterOpenBracketBreak.Other;
1259+
}
1260+
}
12371261
}
12381262

12391263
if (CurrentState.AvoidBinPacking) {

clang/lib/Format/Format.cpp

+26
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,16 @@ template <> struct ScalarEnumerationTraits<FormatStyle::BracketAlignmentStyle> {
215215
}
216216
};
217217

218+
template <> struct MappingTraits<FormatStyle::AlignAfterOpenBracketCustom> {
219+
static void mapping(IO &IO, FormatStyle::AlignAfterOpenBracketCustom &Value) {
220+
IO.mapOptional("InIfConditionalStatements",
221+
Value.InIfConditionalStatements);
222+
IO.mapOptional("InOtherConditionalStatements",
223+
Value.InOtherConditionalStatements);
224+
IO.mapOptional("Other", Value.Other);
225+
}
226+
};
227+
218228
template <>
219229
struct ScalarEnumerationTraits<
220230
FormatStyle::BraceWrappingAfterControlStatementStyle> {
@@ -944,6 +954,8 @@ template <> struct MappingTraits<FormatStyle> {
944954

945955
IO.mapOptional("AccessModifierOffset", Style.AccessModifierOffset);
946956
IO.mapOptional("AlignAfterOpenBracket", Style.AlignAfterOpenBracket);
957+
IO.mapOptional("AlignAfterOpenBracketBreak",
958+
Style.AlignAfterOpenBracketBreak);
947959
IO.mapOptional("AlignArrayOfStructures", Style.AlignArrayOfStructures);
948960
IO.mapOptional("AlignConsecutiveAssignments",
949961
Style.AlignConsecutiveAssignments);
@@ -1189,6 +1201,18 @@ template <> struct MappingTraits<FormatStyle> {
11891201
IO.mapOptional("WrapNamespaceBodyWithEmptyLines",
11901202
Style.WrapNamespaceBodyWithEmptyLines);
11911203

1204+
// If AlignAfterOpenBracket was specified but AlignAfterOpenBracketBreak
1205+
// was not, initialize the latter for backwards compatibility.
1206+
if ((Style.AlignAfterOpenBracket == FormatStyle::BAS_AlwaysBreak ||
1207+
Style.AlignAfterOpenBracket == FormatStyle::BAS_BlockIndent) &&
1208+
Style.AlignAfterOpenBracketBreak ==
1209+
FormatStyle::AlignAfterOpenBracketCustom()) {
1210+
if (Style.AlignAfterOpenBracket == FormatStyle::BAS_AlwaysBreak)
1211+
Style.AlignAfterOpenBracketBreak.InIfConditionalStatements = true;
1212+
Style.AlignAfterOpenBracketBreak.InOtherConditionalStatements = false;
1213+
Style.AlignAfterOpenBracketBreak.Other = true;
1214+
}
1215+
11921216
// If AlwaysBreakAfterDefinitionReturnType was specified but
11931217
// BreakAfterReturnType was not, initialize the latter from the former for
11941218
// backwards compatibility.
@@ -1472,6 +1496,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
14721496
FormatStyle LLVMStyle;
14731497
LLVMStyle.AccessModifierOffset = -2;
14741498
LLVMStyle.AlignAfterOpenBracket = FormatStyle::BAS_Align;
1499+
LLVMStyle.AlignAfterOpenBracketBreak = {};
14751500
LLVMStyle.AlignArrayOfStructures = FormatStyle::AIAS_None;
14761501
LLVMStyle.AlignConsecutiveAssignments = {};
14771502
LLVMStyle.AlignConsecutiveAssignments.PadOperators = true;
@@ -1786,6 +1811,7 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) {
17861811
GoogleStyle.SpacesBeforeTrailingComments = 1;
17871812
} else if (Language == FormatStyle::LK_JavaScript) {
17881813
GoogleStyle.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak;
1814+
GoogleStyle.AlignAfterOpenBracketBreak = {true, false, true};
17891815
GoogleStyle.AlignOperands = FormatStyle::OAS_DontAlign;
17901816
GoogleStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Empty;
17911817
// TODO: still under discussion whether to switch to SLS_All.

clang/lib/Format/TokenAnnotator.cpp

+7-1
Original file line numberDiff line numberDiff line change
@@ -6165,7 +6165,13 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,
61656165
if (Next && Next->is(tok::l_paren))
61666166
return false;
61676167
const FormatToken *Previous = Right.MatchingParen->Previous;
6168-
return !(Previous && (Previous->is(tok::kw_for) || Previous->isIf()));
6168+
if (!Previous)
6169+
return true;
6170+
if (Previous->isOneOf(tok::kw_for, tok::kw_while, tok::kw_switch))
6171+
return Style.AlignAfterOpenBracketBreak.InOtherConditionalStatements;
6172+
if (Previous->isIf())
6173+
return Style.AlignAfterOpenBracketBreak.InIfConditionalStatements;
6174+
return Style.AlignAfterOpenBracketBreak.Other;
61696175
}
61706176

61716177
if (Left.isOneOf(tok::r_paren, TT_TrailingAnnotation) &&

clang/unittests/Format/ConfigParseTest.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,11 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
209209
CHECK_PARSE_BOOL(SpaceBeforeSquareBrackets);
210210
CHECK_PARSE_BOOL(VerilogBreakBetweenInstancePorts);
211211

212+
CHECK_PARSE_NESTED_BOOL(AlignAfterOpenBracketBreak,
213+
InIfConditionalStatements);
214+
CHECK_PARSE_NESTED_BOOL(AlignAfterOpenBracketBreak,
215+
InOtherConditionalStatements);
216+
CHECK_PARSE_NESTED_BOOL(AlignAfterOpenBracketBreak, Other);
212217
CHECK_PARSE_NESTED_BOOL(AlignConsecutiveShortCaseStatements, Enabled);
213218
CHECK_PARSE_NESTED_BOOL(AlignConsecutiveShortCaseStatements,
214219
AcrossEmptyLines);
@@ -523,6 +528,13 @@ TEST(ConfigParseTest, ParsesConfiguration) {
523528
FormatStyle::BAS_DontAlign);
524529
CHECK_PARSE("AlignAfterOpenBracket: true", AlignAfterOpenBracket,
525530
FormatStyle::BAS_Align);
531+
Style.AlignAfterOpenBracket = FormatStyle::BAS_Align;
532+
Style.AlignAfterOpenBracketBreak = {};
533+
CHECK_PARSE("AlignAfterOpenBracket: AlwaysBreak", AlignAfterOpenBracketBreak,
534+
FormatStyle::AlignAfterOpenBracketCustom(true, false, true));
535+
Style.AlignAfterOpenBracketBreak = {};
536+
CHECK_PARSE("AlignAfterOpenBracket: BlockIndent", AlignAfterOpenBracketBreak,
537+
FormatStyle::AlignAfterOpenBracketCustom(false, false, true));
526538

527539
Style.AlignEscapedNewlines = FormatStyle::ENAS_Left;
528540
CHECK_PARSE("AlignEscapedNewlines: DontAlign", AlignEscapedNewlines,

0 commit comments

Comments
 (0)