Skip to content

Commit b2cbc77

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 c2c0ef5 commit b2cbc77

File tree

6 files changed

+391
-22
lines changed

6 files changed

+391
-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.:
@@ -5303,6 +5319,7 @@ struct FormatStyle {
53035319

53045320
bool operator==(const FormatStyle &R) const {
53055321
return AccessModifierOffset == R.AccessModifierOffset &&
5322+
AlignAfterControlStatement == R.AlignAfterControlStatement &&
53065323
AlignAfterOpenBracket == R.AlignAfterOpenBracket &&
53075324
AlignArrayOfStructures == R.AlignArrayOfStructures &&
53085325
AlignConsecutiveAssignments == R.AlignConsecutiveAssignments &&

clang/lib/Format/ContinuationIndenter.cpp

+48-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,36 @@ 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+
if (Style.AlignAfterOpenBracket == FormatStyle::BAS_AlwaysBreak ||
843+
Style.AlignAfterOpenBracket == FormatStyle::BAS_BlockIndent) {
844+
return !Tok.Previous->is(TT_CastRParen) &&
845+
!(Style.isJavaScript() && Tok.is(Keywords.kw_await));
846+
}
847+
return false;
833848
};
834849
auto IsFunctionCallParen = [](const FormatToken &Tok) {
835850
return Tok.is(tok::l_paren) && Tok.ParameterCount > 0 && Tok.Previous &&
836851
Tok.Previous->is(tok::identifier);
837852
};
838-
auto IsInTemplateString = [this](const FormatToken &Tok) {
853+
auto IsInTemplateString = [this](const FormatToken &Tok, bool NestBlocks) {
839854
if (!Style.isJavaScript())
840855
return false;
841856
for (const auto *Prev = &Tok; Prev; Prev = Prev->Previous) {
842857
if (Prev->is(TT_TemplateString) && Prev->opensScope())
843858
return true;
844-
if (Prev->opensScope() ||
845-
(Prev->is(TT_TemplateString) && Prev->closesScope())) {
846-
break;
847-
}
859+
if (Prev->opensScope() && !NestBlocks)
860+
return false;
861+
if (Prev->is(TT_TemplateString) && Prev->closesScope())
862+
return false;
848863
}
849864
return false;
850865
};
@@ -866,21 +881,24 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
866881
Tok.isOneOf(tok::ellipsis, Keywords.kw_await))) {
867882
return true;
868883
}
869-
const auto *Previous = Tok.Previous;
870-
if (!Previous || (!Previous->isOneOf(TT_FunctionDeclarationLParen,
871-
TT_LambdaDefinitionLParen) &&
872-
!IsFunctionCallParen(*Previous))) {
884+
const auto *Previous = TokAfterLParen.Previous;
885+
assert(Previous); // IsOpeningBracket(Previous)
886+
if (Previous->Previous && (Previous->Previous->isIf() ||
887+
IsOtherConditional(*Previous->Previous))) {
888+
return false;
889+
}
890+
if (!Previous->isOneOf(TT_FunctionDeclarationLParen,
891+
TT_LambdaDefinitionLParen) &&
892+
!IsFunctionCallParen(*Previous)) {
873893
return true;
874894
}
875-
if (IsOpeningBracket(Tok) || IsInTemplateString(Tok))
895+
if (IsOpeningBracket(Tok) || IsInTemplateString(Tok, true))
876896
return true;
877897
const auto *Next = Tok.Next;
878898
return !Next || Next->isMemberAccess() ||
879899
Next->is(TT_FunctionDeclarationLParen) || IsFunctionCallParen(*Next);
880900
};
881-
if ((Style.AlignAfterOpenBracket == FormatStyle::BAS_AlwaysBreak ||
882-
Style.AlignAfterOpenBracket == FormatStyle::BAS_BlockIndent) &&
883-
IsOpeningBracket(Previous) && State.Column > getNewLineColumn(State) &&
901+
if (IsOpeningBracket(Previous) && State.Column > getNewLineColumn(State) &&
884902
// Don't do this for simple (no expressions) one-argument function calls
885903
// as that feels like needlessly wasting whitespace, e.g.:
886904
//
@@ -910,7 +928,7 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
910928
!(Current.MacroParent && Previous.MacroParent) &&
911929
(Current.isNot(TT_LineComment) ||
912930
Previous.isOneOf(BK_BracedInit, TT_VerilogMultiLineListLParen)) &&
913-
!IsInTemplateString(Current)) {
931+
!IsInTemplateString(Current, false)) {
914932
CurrentState.Indent = State.Column + Spaces;
915933
CurrentState.IsAligned = true;
916934
}
@@ -1247,8 +1265,17 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
12471265
}
12481266

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

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

clang/lib/Format/Format.cpp

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

205+
template <>
206+
struct ScalarEnumerationTraits<FormatStyle::BreakAfterControlStatementStyle> {
207+
static void enumeration(IO &IO,
208+
FormatStyle::BreakAfterControlStatementStyle &Value) {
209+
IO.enumCase(Value, "Default", FormatStyle::BACSS_Default);
210+
IO.enumCase(Value, "MultiLine", FormatStyle::BACSS_MultiLine);
211+
IO.enumCase(Value, "No", FormatStyle::BACSS_No);
212+
}
213+
};
214+
205215
template <> struct ScalarEnumerationTraits<FormatStyle::BracketAlignmentStyle> {
206216
static void enumeration(IO &IO, FormatStyle::BracketAlignmentStyle &Value) {
207217
IO.enumCase(Value, "Align", FormatStyle::BAS_Align);
@@ -954,6 +964,8 @@ template <> struct MappingTraits<FormatStyle> {
954964

955965
IO.mapOptional("AccessModifierOffset", Style.AccessModifierOffset);
956966
IO.mapOptional("AlignAfterOpenBracket", Style.AlignAfterOpenBracket);
967+
IO.mapOptional("AlignAfterControlStatement",
968+
Style.AlignAfterControlStatement);
957969
IO.mapOptional("AlignArrayOfStructures", Style.AlignArrayOfStructures);
958970
IO.mapOptional("AlignConsecutiveAssignments",
959971
Style.AlignConsecutiveAssignments);
@@ -1486,6 +1498,7 @@ static void expandPresetsSpacesInParens(FormatStyle &Expanded) {
14861498
FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
14871499
FormatStyle LLVMStyle;
14881500
LLVMStyle.AccessModifierOffset = -2;
1501+
LLVMStyle.AlignAfterControlStatement = FormatStyle::BACSS_Default;
14891502
LLVMStyle.AlignAfterOpenBracket = FormatStyle::BAS_Align;
14901503
LLVMStyle.AlignArrayOfStructures = FormatStyle::AIAS_None;
14911504
LLVMStyle.AlignConsecutiveAssignments = {};

clang/lib/Format/TokenAnnotator.cpp

+7-1
Original file line numberDiff line numberDiff line change
@@ -6224,7 +6224,13 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,
62246224
if (Next && Next->is(tok::l_paren))
62256225
return false;
62266226
const FormatToken *Previous = Right.MatchingParen->Previous;
6227-
return !(Previous && (Previous->is(tok::kw_for) || Previous->isIf()));
6227+
if (!Previous)
6228+
return true;
6229+
if (Previous->isIf() ||
6230+
Previous->isOneOf(tok::kw_for, tok::kw_while, tok::kw_switch)) {
6231+
return Style.AlignAfterControlStatement == FormatStyle::BACSS_MultiLine;
6232+
}
6233+
return true;
62286234
}
62296235

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

clang/unittests/Format/ConfigParseTest.cpp

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

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

0 commit comments

Comments
 (0)