Skip to content

Commit f21e1c6

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 d989c24 commit f21e1c6

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.:
@@ -5127,6 +5199,7 @@ struct FormatStyle {
51275199
bool operator==(const FormatStyle &R) const {
51285200
return AccessModifierOffset == R.AccessModifierOffset &&
51295201
AlignAfterOpenBracket == R.AlignAfterOpenBracket &&
5202+
AlignAfterOpenBracketBreak == R.AlignAfterOpenBracketBreak &&
51305203
AlignArrayOfStructures == R.AlignArrayOfStructures &&
51315204
AlignConsecutiveAssignments == R.AlignConsecutiveAssignments &&
51325205
AlignConsecutiveBitFields == R.AlignConsecutiveBitFields &&

clang/lib/Format/ContinuationIndenter.cpp

+34-10
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,11 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
802802
// parenthesis by disallowing any further line breaks if there is no line
803803
// break after the opening parenthesis. Don't break if it doesn't conserve
804804
// columns.
805+
auto IsOtherConditional = [&](const FormatToken &Tok) {
806+
return Tok.isOneOf(tok::kw_for, tok::kw_while, tok::kw_switch) ||
807+
(Style.isJavaScript() && Tok.is(Keywords.kw_await) && Tok.Previous &&
808+
Tok.Previous->is(tok::kw_for));
809+
};
805810
auto IsOpeningBracket = [&](const FormatToken &Tok) {
806811
auto IsStartOfBracedList = [&]() {
807812
return Tok.is(tok::l_brace) && Tok.isNot(BK_Block) &&
@@ -814,10 +819,11 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
814819
if (!Tok.Previous)
815820
return true;
816821
if (Tok.Previous->isIf())
817-
return Style.AlignAfterOpenBracket == FormatStyle::BAS_AlwaysBreak;
818-
return !Tok.Previous->isOneOf(TT_CastRParen, tok::kw_for, tok::kw_while,
819-
tok::kw_switch) &&
820-
!(Style.isJavaScript() && Tok.Previous->is(Keywords.kw_await));
822+
return Style.AlignAfterOpenBracketBreak.InIfConditionalStatements;
823+
if (IsOtherConditional(*Tok.Previous))
824+
return Style.AlignAfterOpenBracketBreak.InOtherConditionalStatements;
825+
return !Tok.Previous->is(TT_CastRParen) &&
826+
Style.AlignAfterOpenBracketBreak.Other;
821827
};
822828
auto IsFunctionCallParen = [](const FormatToken &Tok) {
823829
return Tok.is(tok::l_paren) && Tok.ParameterCount > 0 && Tok.Previous &&
@@ -852,10 +858,15 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
852858
Tok.isOneOf(tok::ellipsis, Keywords.kw_await))) {
853859
return true;
854860
}
855-
const auto *Previous = Tok.Previous;
856-
if (!Previous || (!Previous->isOneOf(TT_FunctionDeclarationLParen,
857-
TT_LambdaDefinitionLParen) &&
858-
!IsFunctionCallParen(*Previous))) {
861+
const auto *Previous = TokAfterLParen.Previous;
862+
assert(Previous); // IsOpeningBracket(Previous)
863+
if (Previous->Previous && (Previous->Previous->isIf() ||
864+
IsOtherConditional(*Previous->Previous))) {
865+
return false;
866+
}
867+
if (!Previous->isOneOf(TT_FunctionDeclarationLParen,
868+
TT_LambdaDefinitionLParen) &&
869+
!IsFunctionCallParen(*Previous)) {
859870
return true;
860871
}
861872
if (IsOpeningBracket(Tok) || IsInTemplateString(Tok))
@@ -1233,8 +1244,21 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
12331244
}
12341245

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

12401264
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> {
@@ -932,6 +942,8 @@ template <> struct MappingTraits<FormatStyle> {
932942

933943
IO.mapOptional("AccessModifierOffset", Style.AccessModifierOffset);
934944
IO.mapOptional("AlignAfterOpenBracket", Style.AlignAfterOpenBracket);
945+
IO.mapOptional("AlignAfterOpenBracketBreak",
946+
Style.AlignAfterOpenBracketBreak);
935947
IO.mapOptional("AlignArrayOfStructures", Style.AlignArrayOfStructures);
936948
IO.mapOptional("AlignConsecutiveAssignments",
937949
Style.AlignConsecutiveAssignments);
@@ -1168,6 +1180,18 @@ template <> struct MappingTraits<FormatStyle> {
11681180
IO.mapOptional("WhitespaceSensitiveMacros",
11691181
Style.WhitespaceSensitiveMacros);
11701182

1183+
// If AlignAfterOpenBracket was specified but AlignAfterOpenBracketBreak
1184+
// was not, initialize the latter for backwards compatibility.
1185+
if ((Style.AlignAfterOpenBracket == FormatStyle::BAS_AlwaysBreak ||
1186+
Style.AlignAfterOpenBracket == FormatStyle::BAS_BlockIndent) &&
1187+
Style.AlignAfterOpenBracketBreak ==
1188+
FormatStyle::AlignAfterOpenBracketCustom()) {
1189+
if (Style.AlignAfterOpenBracket == FormatStyle::BAS_AlwaysBreak)
1190+
Style.AlignAfterOpenBracketBreak.InIfConditionalStatements = true;
1191+
Style.AlignAfterOpenBracketBreak.InOtherConditionalStatements = false;
1192+
Style.AlignAfterOpenBracketBreak.Other = true;
1193+
}
1194+
11711195
// If AlwaysBreakAfterDefinitionReturnType was specified but
11721196
// BreakAfterReturnType was not, initialize the latter from the former for
11731197
// backwards compatibility.
@@ -1451,6 +1475,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
14511475
FormatStyle LLVMStyle;
14521476
LLVMStyle.AccessModifierOffset = -2;
14531477
LLVMStyle.AlignAfterOpenBracket = FormatStyle::BAS_Align;
1478+
LLVMStyle.AlignAfterOpenBracketBreak = {};
14541479
LLVMStyle.AlignArrayOfStructures = FormatStyle::AIAS_None;
14551480
LLVMStyle.AlignConsecutiveAssignments = {};
14561481
LLVMStyle.AlignConsecutiveAssignments.PadOperators = true;
@@ -1760,6 +1785,7 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) {
17601785
GoogleStyle.SpacesBeforeTrailingComments = 1;
17611786
} else if (Language == FormatStyle::LK_JavaScript) {
17621787
GoogleStyle.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak;
1788+
GoogleStyle.AlignAfterOpenBracketBreak = {true, false, true};
17631789
GoogleStyle.AlignOperands = FormatStyle::OAS_DontAlign;
17641790
GoogleStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Empty;
17651791
// 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
@@ -6279,7 +6279,13 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,
62796279
if (Next && Next->is(tok::l_paren))
62806280
return false;
62816281
const FormatToken *Previous = Right.MatchingParen->Previous;
6282-
return !(Previous && (Previous->is(tok::kw_for) || Previous->isIf()));
6282+
if (!Previous)
6283+
return true;
6284+
if (Previous->isOneOf(tok::kw_for, tok::kw_while, tok::kw_switch))
6285+
return Style.AlignAfterOpenBracketBreak.InOtherConditionalStatements;
6286+
if (Previous->isIf())
6287+
return Style.AlignAfterOpenBracketBreak.InIfConditionalStatements;
6288+
return Style.AlignAfterOpenBracketBreak.Other;
62836289
}
62846290

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

clang/unittests/Format/ConfigParseTest.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,11 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
203203
CHECK_PARSE_BOOL(SpaceBeforeSquareBrackets);
204204
CHECK_PARSE_BOOL(VerilogBreakBetweenInstancePorts);
205205

206+
CHECK_PARSE_NESTED_BOOL(AlignAfterOpenBracketBreak,
207+
InIfConditionalStatements);
208+
CHECK_PARSE_NESTED_BOOL(AlignAfterOpenBracketBreak,
209+
InOtherConditionalStatements);
210+
CHECK_PARSE_NESTED_BOOL(AlignAfterOpenBracketBreak, Other);
206211
CHECK_PARSE_NESTED_BOOL(AlignConsecutiveShortCaseStatements, Enabled);
207212
CHECK_PARSE_NESTED_BOOL(AlignConsecutiveShortCaseStatements,
208213
AcrossEmptyLines);
@@ -515,6 +520,13 @@ TEST(ConfigParseTest, ParsesConfiguration) {
515520
FormatStyle::BAS_DontAlign);
516521
CHECK_PARSE("AlignAfterOpenBracket: true", AlignAfterOpenBracket,
517522
FormatStyle::BAS_Align);
523+
Style.AlignAfterOpenBracket = FormatStyle::BAS_Align;
524+
Style.AlignAfterOpenBracketBreak = {};
525+
CHECK_PARSE("AlignAfterOpenBracket: AlwaysBreak", AlignAfterOpenBracketBreak,
526+
FormatStyle::AlignAfterOpenBracketCustom(true, false, true));
527+
Style.AlignAfterOpenBracketBreak = {};
528+
CHECK_PARSE("AlignAfterOpenBracket: BlockIndent", AlignAfterOpenBracketBreak,
529+
FormatStyle::AlignAfterOpenBracketCustom(false, false, true));
518530

519531
Style.AlignEscapedNewlines = FormatStyle::ENAS_Left;
520532
CHECK_PARSE("AlignEscapedNewlines: DontAlign", AlignEscapedNewlines,

0 commit comments

Comments
 (0)