Skip to content

Commit 64000ad

Browse files
committed
Format: add AlignAfterControlStatement
Introduce new style option to allow overriding the breaking after the opening parenthesis for control statements (if/for/while/switch). Fixes #67738. Fixes #79176. Fixes #80123.
1 parent 0c57f89 commit 64000ad

File tree

6 files changed

+390
-22
lines changed

6 files changed

+390
-22
lines changed

clang/include/clang/Format/Format.h

+17
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,22 @@ struct FormatStyle {
6262
/// \version 3.3
6363
int AccessModifierOffset;
6464

65+
/// Different styles for breaking the parenthesis after a control statement
66+
/// (``if/switch/while/for ...``).
67+
/// \version 21
68+
enum BreakAfterControlStatementStyle : int8_t {
69+
/// Use the default behavior.
70+
BACSS_Default,
71+
/// Force break after the left parenthesis of a control statement only
72+
/// when the expression exceeds the column limit, and align on the
73+
/// ``ContinuationIndentWidth``.
74+
BACSS_MultiLine,
75+
/// Do not force a break after the control statment.
76+
BACSS_No,
77+
};
78+
79+
BreakAfterControlStatementStyle AlignAfterControlStatement;
80+
6581
/// Different styles for aligning after open brackets.
6682
enum BracketAlignmentStyle : int8_t {
6783
/// Align parameters on the open bracket, e.g.:
@@ -5283,6 +5299,7 @@ struct FormatStyle {
52835299
bool operator==(const FormatStyle &R) const {
52845300
return AccessModifierOffset == R.AccessModifierOffset &&
52855301
AlignAfterOpenBracket == R.AlignAfterOpenBracket &&
5302+
AlignAfterControlStatement == R.AlignAfterControlStatement &&
52865303
AlignArrayOfStructures == R.AlignArrayOfStructures &&
52875304
AlignConsecutiveAssignments == R.AlignConsecutiveAssignments &&
52885305
AlignConsecutiveBitFields == R.AlignConsecutiveBitFields &&

clang/lib/Format/ContinuationIndenter.cpp

+49-21
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,11 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
814814
// parenthesis by disallowing any further line breaks if there is no line
815815
// break after the opening parenthesis. Don't break if it doesn't conserve
816816
// columns.
817+
auto IsOtherConditional = [&](const FormatToken &Tok) {
818+
return Tok.isOneOf(tok::kw_for, tok::kw_while, tok::kw_switch) ||
819+
(Style.isJavaScript() && Tok.is(Keywords.kw_await) && Tok.Previous &&
820+
Tok.Previous->is(tok::kw_for));
821+
};
817822
auto IsOpeningBracket = [&](const FormatToken &Tok) {
818823
auto IsStartOfBracedList = [&]() {
819824
return Tok.is(tok::l_brace) && Tok.isNot(BK_Block) &&
@@ -825,26 +830,37 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
825830
}
826831
if (!Tok.Previous)
827832
return true;
828-
if (Tok.Previous->isIf())
829-
return Style.AlignAfterOpenBracket == FormatStyle::BAS_AlwaysBreak;
830-
return !Tok.Previous->isOneOf(TT_CastRParen, tok::kw_for, tok::kw_while,
831-
tok::kw_switch) &&
832-
!(Style.isJavaScript() && Tok.Previous->is(Keywords.kw_await));
833+
if (Tok.Previous->isIf()) {
834+
/* For backward compatibility, use AlignAfterOpenBracket
835+
* in case AlignAfterControlStatement is not initialized */
836+
return Style.AlignAfterControlStatement == FormatStyle::BACSS_MultiLine ||
837+
(Style.AlignAfterControlStatement == FormatStyle::BACSS_Default &&
838+
Style.AlignAfterOpenBracket == FormatStyle::BAS_AlwaysBreak);
839+
}
840+
if (IsOtherConditional(*Tok.Previous)) {
841+
return Style.AlignAfterControlStatement == FormatStyle::BACSS_MultiLine;
842+
}
843+
if (Style.AlignAfterOpenBracket == FormatStyle::BAS_AlwaysBreak ||
844+
Style.AlignAfterOpenBracket == FormatStyle::BAS_BlockIndent) {
845+
return !Tok.Previous->is(TT_CastRParen) &&
846+
!(Style.isJavaScript() && Tok.is(Keywords.kw_await));
847+
}
848+
return false;
833849
};
834850
auto IsFunctionCallParen = [](const FormatToken &Tok) {
835851
return Tok.is(tok::l_paren) && Tok.ParameterCount > 0 && Tok.Previous &&
836852
Tok.Previous->is(tok::identifier);
837853
};
838-
auto IsInTemplateString = [this](const FormatToken &Tok) {
854+
auto IsInTemplateString = [this](const FormatToken &Tok, bool NestBlocks) {
839855
if (!Style.isJavaScript())
840856
return false;
841857
for (const auto *Prev = &Tok; Prev; Prev = Prev->Previous) {
842858
if (Prev->is(TT_TemplateString) && Prev->opensScope())
843859
return true;
844-
if (Prev->opensScope() ||
845-
(Prev->is(TT_TemplateString) && Prev->closesScope())) {
846-
break;
847-
}
860+
if (Prev->opensScope() && !NestBlocks)
861+
return false;
862+
if (Prev->is(TT_TemplateString) && Prev->closesScope())
863+
return false;
848864
}
849865
return false;
850866
};
@@ -866,21 +882,24 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
866882
Tok.isOneOf(tok::ellipsis, Keywords.kw_await))) {
867883
return true;
868884
}
869-
const auto *Previous = Tok.Previous;
870-
if (!Previous || (!Previous->isOneOf(TT_FunctionDeclarationLParen,
871-
TT_LambdaDefinitionLParen) &&
872-
!IsFunctionCallParen(*Previous))) {
885+
const auto *Previous = TokAfterLParen.Previous;
886+
assert(Previous); // IsOpeningBracket(Previous)
887+
if (Previous->Previous && (Previous->Previous->isIf() ||
888+
IsOtherConditional(*Previous->Previous))) {
889+
return false;
890+
}
891+
if (!Previous->isOneOf(TT_FunctionDeclarationLParen,
892+
TT_LambdaDefinitionLParen) &&
893+
!IsFunctionCallParen(*Previous)) {
873894
return true;
874895
}
875-
if (IsOpeningBracket(Tok) || IsInTemplateString(Tok))
896+
if (IsOpeningBracket(Tok) || IsInTemplateString(Tok, true))
876897
return true;
877898
const auto *Next = Tok.Next;
878899
return !Next || Next->isMemberAccess() ||
879900
Next->is(TT_FunctionDeclarationLParen) || IsFunctionCallParen(*Next);
880901
};
881-
if ((Style.AlignAfterOpenBracket == FormatStyle::BAS_AlwaysBreak ||
882-
Style.AlignAfterOpenBracket == FormatStyle::BAS_BlockIndent) &&
883-
IsOpeningBracket(Previous) && State.Column > getNewLineColumn(State) &&
902+
if (IsOpeningBracket(Previous) && State.Column > getNewLineColumn(State) &&
884903
// Don't do this for simple (no expressions) one-argument function calls
885904
// as that feels like needlessly wasting whitespace, e.g.:
886905
//
@@ -910,7 +929,7 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
910929
!(Current.MacroParent && Previous.MacroParent) &&
911930
(Current.isNot(TT_LineComment) ||
912931
Previous.isOneOf(BK_BracedInit, TT_VerilogMultiLineListLParen)) &&
913-
!IsInTemplateString(Current)) {
932+
!IsInTemplateString(Current, false)) {
914933
CurrentState.Indent = State.Column + Spaces;
915934
CurrentState.IsAligned = true;
916935
}
@@ -1247,8 +1266,17 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
12471266
}
12481267

12491268
if (PreviousNonComment && PreviousNonComment->is(tok::l_paren)) {
1250-
CurrentState.BreakBeforeClosingParen =
1251-
Style.AlignAfterOpenBracket == FormatStyle::BAS_BlockIndent;
1269+
auto Previous = PreviousNonComment->Previous;
1270+
if (Previous &&
1271+
(Previous->isIf() ||
1272+
Previous->isOneOf(tok::kw_for, tok::kw_while, tok::kw_switch))) {
1273+
CurrentState.BreakBeforeClosingParen =
1274+
Style.AlignAfterControlStatement == FormatStyle::BACSS_MultiLine &&
1275+
Style.AlignAfterOpenBracket == FormatStyle::BAS_BlockIndent;
1276+
} else {
1277+
CurrentState.BreakBeforeClosingParen =
1278+
Style.AlignAfterOpenBracket == FormatStyle::BAS_BlockIndent;
1279+
}
12521280
}
12531281

12541282
if (PreviousNonComment && PreviousNonComment->is(TT_TemplateOpener))

clang/lib/Format/Format.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,14 @@ template <> struct MappingTraits<FormatStyle::BraceWrappingFlags> {
202202
}
203203
};
204204

205+
template <> struct ScalarEnumerationTraits<FormatStyle::BreakAfterControlStatementStyle> {
206+
static void enumeration(IO &IO, FormatStyle::BreakAfterControlStatementStyle &Value) {
207+
IO.enumCase(Value, "Default", FormatStyle::BACSS_Default);
208+
IO.enumCase(Value, "MultiLine", FormatStyle::BACSS_MultiLine);
209+
IO.enumCase(Value, "No", FormatStyle::BACSS_No);
210+
}
211+
};
212+
205213
template <> struct ScalarEnumerationTraits<FormatStyle::BracketAlignmentStyle> {
206214
static void enumeration(IO &IO, FormatStyle::BracketAlignmentStyle &Value) {
207215
IO.enumCase(Value, "Align", FormatStyle::BAS_Align);
@@ -954,6 +962,8 @@ template <> struct MappingTraits<FormatStyle> {
954962

955963
IO.mapOptional("AccessModifierOffset", Style.AccessModifierOffset);
956964
IO.mapOptional("AlignAfterOpenBracket", Style.AlignAfterOpenBracket);
965+
IO.mapOptional("AlignAfterControlStatement",
966+
Style.AlignAfterControlStatement);
957967
IO.mapOptional("AlignArrayOfStructures", Style.AlignArrayOfStructures);
958968
IO.mapOptional("AlignConsecutiveAssignments",
959969
Style.AlignConsecutiveAssignments);
@@ -1485,6 +1495,7 @@ static void expandPresetsSpacesInParens(FormatStyle &Expanded) {
14851495
FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
14861496
FormatStyle LLVMStyle;
14871497
LLVMStyle.AccessModifierOffset = -2;
1498+
LLVMStyle.AlignAfterControlStatement = FormatStyle::BACSS_Default;
14881499
LLVMStyle.AlignAfterOpenBracket = FormatStyle::BAS_Align;
14891500
LLVMStyle.AlignArrayOfStructures = FormatStyle::AIAS_None;
14901501
LLVMStyle.AlignConsecutiveAssignments = {};

clang/lib/Format/TokenAnnotator.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -6219,7 +6219,12 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,
62196219
if (Next && Next->is(tok::l_paren))
62206220
return false;
62216221
const FormatToken *Previous = Right.MatchingParen->Previous;
6222-
return !(Previous && (Previous->is(tok::kw_for) || Previous->isIf()));
6222+
if (!Previous)
6223+
return true;
6224+
if (Previous->isIf() || Previous->isOneOf(tok::kw_for, tok::kw_while, tok::kw_switch)) {
6225+
return Style.AlignAfterControlStatement == FormatStyle::BACSS_MultiLine;
6226+
}
6227+
return true;
62236228
}
62246229

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

clang/unittests/Format/ConfigParseTest.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,14 @@ TEST(ConfigParseTest, ParsesConfiguration) {
528528
CHECK_PARSE("EnumTrailingComma: Remove", EnumTrailingComma,
529529
FormatStyle::ETC_Remove);
530530

531+
Style.AlignAfterControlStatement = FormatStyle::BACSS_Default;
532+
CHECK_PARSE("AlignAfterControlStatement: MultiLine",
533+
AlignAfterControlStatement, FormatStyle::BACSS_MultiLine);
534+
CHECK_PARSE("AlignAfterControlStatement: No", AlignAfterControlStatement,
535+
FormatStyle::BACSS_No);
536+
CHECK_PARSE("AlignAfterControlStatement: Default", AlignAfterControlStatement,
537+
FormatStyle::BACSS_Default);
538+
531539
Style.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak;
532540
CHECK_PARSE("AlignAfterOpenBracket: Align", AlignAfterOpenBracket,
533541
FormatStyle::BAS_Align);

0 commit comments

Comments
 (0)