Skip to content

Commit d7f9196

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 5e80fc8 commit d7f9196

File tree

7 files changed

+353
-10
lines changed

7 files changed

+353
-10
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.:
@@ -5060,6 +5132,7 @@ struct FormatStyle {
50605132
bool operator==(const FormatStyle &R) const {
50615133
return AccessModifierOffset == R.AccessModifierOffset &&
50625134
AlignAfterOpenBracket == R.AlignAfterOpenBracket &&
5135+
AlignAfterOpenBracketBreak == R.AlignAfterOpenBracketBreak &&
50635136
AlignArrayOfStructures == R.AlignArrayOfStructures &&
50645137
AlignConsecutiveAssignments == R.AlignConsecutiveAssignments &&
50655138
AlignConsecutiveBitFields == R.AlignConsecutiveBitFields &&

clang/lib/Format/ContinuationIndenter.cpp

+32-9
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,9 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
795795
// parenthesis by disallowing any further line breaks if there is no line
796796
// break after the opening parenthesis. Don't break if it doesn't conserve
797797
// columns.
798+
auto IsOtherConditional = [](const FormatToken &Tok) {
799+
return Tok.isOneOf(tok::kw_for, tok::kw_while, tok::kw_switch);
800+
};
798801
auto IsOpeningBracket = [&](const FormatToken &Tok) {
799802
auto IsStartOfBracedList = [&]() {
800803
return Tok.is(tok::l_brace) && Tok.isNot(BK_Block) &&
@@ -807,9 +810,11 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
807810
if (!Tok.Previous)
808811
return true;
809812
if (Tok.Previous->isIf())
810-
return Style.AlignAfterOpenBracket == FormatStyle::BAS_AlwaysBreak;
811-
return !Tok.Previous->isOneOf(TT_CastRParen, tok::kw_for, tok::kw_while,
812-
tok::kw_switch);
813+
return Style.AlignAfterOpenBracketBreak.InIfConditionalStatements;
814+
if (IsOtherConditional(*Tok.Previous))
815+
return Style.AlignAfterOpenBracketBreak.InOtherConditionalStatements;
816+
return !Tok.Previous->is(TT_CastRParen) &&
817+
Style.AlignAfterOpenBracketBreak.Other;
813818
};
814819
auto IsFunctionCallParen = [](const FormatToken &Tok) {
815820
return Tok.is(tok::l_paren) && Tok.ParameterCount > 0 && Tok.Previous &&
@@ -844,10 +849,15 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
844849
Tok.isOneOf(tok::ellipsis, Keywords.kw_await))) {
845850
return true;
846851
}
847-
const auto *Previous = Tok.Previous;
848-
if (!Previous || (!Previous->isOneOf(TT_FunctionDeclarationLParen,
849-
TT_LambdaDefinitionLParen) &&
850-
!IsFunctionCallParen(*Previous))) {
852+
const auto *Previous = TokAfterLParen.Previous;
853+
assert(Previous); // IsOpeningBracket(Previous)
854+
if (Previous->Previous && (Previous->Previous->isIf() ||
855+
IsOtherConditional(*Previous->Previous))) {
856+
return false;
857+
}
858+
if (!Previous->isOneOf(TT_FunctionDeclarationLParen,
859+
TT_LambdaDefinitionLParen) &&
860+
!IsFunctionCallParen(*Previous)) {
851861
return true;
852862
}
853863
if (IsOpeningBracket(Tok) || IsInTemplateString(Tok))
@@ -1225,8 +1235,21 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
12251235
}
12261236

12271237
if (PreviousNonComment && PreviousNonComment->is(tok::l_paren)) {
1228-
CurrentState.BreakBeforeClosingParen =
1229-
Style.AlignAfterOpenBracket == FormatStyle::BAS_BlockIndent;
1238+
CurrentState.BreakBeforeClosingParen = false;
1239+
if (Style.AlignAfterOpenBracket == FormatStyle::BAS_BlockIndent) {
1240+
auto Previous = PreviousNonComment->Previous;
1241+
if (Previous && Previous->isIf()) {
1242+
CurrentState.BreakBeforeClosingParen =
1243+
Style.AlignAfterOpenBracketBreak.InIfConditionalStatements;
1244+
} else if (Previous && Previous->isOneOf(tok::kw_for, tok::kw_while,
1245+
tok::kw_switch)) {
1246+
CurrentState.BreakBeforeClosingParen =
1247+
Style.AlignAfterOpenBracketBreak.InOtherConditionalStatements;
1248+
} else {
1249+
CurrentState.BreakBeforeClosingParen =
1250+
Style.AlignAfterOpenBracketBreak.Other;
1251+
}
1252+
}
12301253
}
12311254

12321255
if (CurrentState.AvoidBinPacking) {

clang/lib/Format/Format.cpp

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

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

923933
IO.mapOptional("AccessModifierOffset", Style.AccessModifierOffset);
924934
IO.mapOptional("AlignAfterOpenBracket", Style.AlignAfterOpenBracket);
935+
IO.mapOptional("AlignAfterOpenBracketBreak",
936+
Style.AlignAfterOpenBracketBreak);
925937
IO.mapOptional("AlignArrayOfStructures", Style.AlignArrayOfStructures);
926938
IO.mapOptional("AlignConsecutiveAssignments",
927939
Style.AlignConsecutiveAssignments);
@@ -1155,6 +1167,18 @@ template <> struct MappingTraits<FormatStyle> {
11551167
IO.mapOptional("WhitespaceSensitiveMacros",
11561168
Style.WhitespaceSensitiveMacros);
11571169

1170+
// If AlignAfterOpenBracket was specified but AlignAfterOpenBracketBreak
1171+
// was not, initialize the latter for backwards compatibility.
1172+
if ((Style.AlignAfterOpenBracket == FormatStyle::BAS_AlwaysBreak ||
1173+
Style.AlignAfterOpenBracket == FormatStyle::BAS_BlockIndent) &&
1174+
Style.AlignAfterOpenBracketBreak ==
1175+
FormatStyle::AlignAfterOpenBracketCustom()) {
1176+
if (Style.AlignAfterOpenBracket == FormatStyle::BAS_AlwaysBreak)
1177+
Style.AlignAfterOpenBracketBreak.InIfConditionalStatements = true;
1178+
Style.AlignAfterOpenBracketBreak.InOtherConditionalStatements = false;
1179+
Style.AlignAfterOpenBracketBreak.Other = true;
1180+
}
1181+
11581182
// If AlwaysBreakAfterDefinitionReturnType was specified but
11591183
// BreakAfterReturnType was not, initialize the latter from the former for
11601184
// backwards compatibility.
@@ -1438,6 +1462,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
14381462
FormatStyle LLVMStyle;
14391463
LLVMStyle.AccessModifierOffset = -2;
14401464
LLVMStyle.AlignAfterOpenBracket = FormatStyle::BAS_Align;
1465+
LLVMStyle.AlignAfterOpenBracketBreak = {};
14411466
LLVMStyle.AlignArrayOfStructures = FormatStyle::AIAS_None;
14421467
LLVMStyle.AlignConsecutiveAssignments = {};
14431468
LLVMStyle.AlignConsecutiveAssignments.AcrossComments = false;
@@ -1750,6 +1775,7 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) {
17501775
GoogleStyle.SpacesBeforeTrailingComments = 1;
17511776
} else if (Language == FormatStyle::LK_JavaScript) {
17521777
GoogleStyle.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak;
1778+
GoogleStyle.AlignAfterOpenBracketBreak = {true, false, true};
17531779
GoogleStyle.AlignOperands = FormatStyle::OAS_DontAlign;
17541780
GoogleStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Empty;
17551781
// 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
@@ -6259,7 +6259,13 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,
62596259
if (Next && Next->is(tok::l_paren))
62606260
return false;
62616261
const FormatToken *Previous = Right.MatchingParen->Previous;
6262-
return !(Previous && (Previous->is(tok::kw_for) || Previous->isIf()));
6262+
if (!Previous)
6263+
return true;
6264+
if (Previous->isOneOf(tok::kw_for, tok::kw_while, tok::kw_switch))
6265+
return Style.AlignAfterOpenBracketBreak.InOtherConditionalStatements;
6266+
if (Previous->isIf())
6267+
return Style.AlignAfterOpenBracketBreak.InIfConditionalStatements;
6268+
return Style.AlignAfterOpenBracketBreak.Other;
62636269
}
62646270

62656271
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);
@@ -500,6 +505,13 @@ TEST(ConfigParseTest, ParsesConfiguration) {
500505
FormatStyle::BAS_DontAlign);
501506
CHECK_PARSE("AlignAfterOpenBracket: true", AlignAfterOpenBracket,
502507
FormatStyle::BAS_Align);
508+
Style.AlignAfterOpenBracket = FormatStyle::BAS_Align;
509+
Style.AlignAfterOpenBracketBreak = {};
510+
CHECK_PARSE("AlignAfterOpenBracket: AlwaysBreak", AlignAfterOpenBracketBreak,
511+
FormatStyle::AlignAfterOpenBracketCustom(true, false, true));
512+
Style.AlignAfterOpenBracketBreak = {};
513+
CHECK_PARSE("AlignAfterOpenBracket: BlockIndent", AlignAfterOpenBracketBreak,
514+
FormatStyle::AlignAfterOpenBracketCustom(false, false, true));
503515

504516
Style.AlignEscapedNewlines = FormatStyle::ENAS_Left;
505517
CHECK_PARSE("AlignEscapedNewlines: DontAlign", AlignEscapedNewlines,

0 commit comments

Comments
 (0)