Skip to content

Commit 3d61b11

Browse files
francoisferrandFrancois Ferrand
authored andcommitted
clang-format: Introduce stricter AlignOperands flag
Summary: Even when BreakBeforeBinaryOperators is set, AlignOperands kept aligning the beginning of the line, even when it could align the actual operands (e.g. after an assignment). With this patch, there is an option to actually align the operands, so that the operator gets right-aligned with the equal sign or return operator: int aaaaa = bbbbbb + cccccc; return aaaaaaa && bbbbbbb; This not happen in parentheses, to avoid 'breaking' the indentation: if (aaaaa && bbbbb) return; Reviewers: krasimir, djasper Subscribers: cfe-commits, klimek Differential Revision: https://reviews.llvm.org/D32478
1 parent 5daa25f commit 3d61b11

File tree

7 files changed

+248
-32
lines changed

7 files changed

+248
-32
lines changed

clang/docs/ClangFormatStyleOptions.rst

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -270,17 +270,49 @@ the configuration (without a prefix: ``Auto``).
270270

271271

272272

273-
**AlignOperands** (``bool``)
273+
**AlignOperands** (``OperandAlignmentStyle``)
274274
If ``true``, horizontally align operands of binary and ternary
275275
expressions.
276276

277-
Specifically, this aligns operands of a single expression that needs to be
278-
split over multiple lines, e.g.:
277+
Possible values:
278+
279+
* ``OAS_DontAlign`` (in configuration: ``DontAlign``)
280+
Do not align operands of binary and ternary expressions.
281+
The wrapped lines are indented ``ContinuationIndentWidth`` spaces from
282+
the start of the line.
283+
284+
* ``OAS_Align`` (in configuration: ``Align``)
285+
Horizontally align operands of binary and ternary expressions.
286+
287+
Specifically, this aligns operands of a single expression that needs
288+
to be split over multiple lines, e.g.:
289+
290+
.. code-block:: c++
291+
292+
int aaa = bbbbbbbbbbbbbbb +
293+
ccccccccccccccc;
294+
295+
When ``BreakBeforeBinaryOperators`` is set, the wrapped operator is
296+
aligned with the operand on the first line.
297+
298+
.. code-block:: c++
299+
300+
int aaa = bbbbbbbbbbbbbbb
301+
+ ccccccccccccccc;
302+
303+
* ``OAS_AlignAfterOperator`` (in configuration: ``AlignAfterOperator``)
304+
Horizontally align operands of binary and ternary expressions.
305+
306+
This is similar to ``AO_Align``, except when
307+
``BreakBeforeBinaryOperators`` is set, the operator is un-indented so
308+
that the wrapped operand is aligned with the operand on the first line.
309+
310+
.. code-block:: c++
311+
312+
int aaa = bbbbbbbbbbbbbbb
313+
+ ccccccccccccccc;
279314

280-
.. code-block:: c++
281315

282-
int aaa = bbbbbbbbbbbbbbb +
283-
ccccccccccccccc;
284316

285317
**AlignTrailingComments** (``bool``)
286318
If ``true``, aligns trailing comments.

clang/include/clang/Format/Format.h

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -153,16 +153,43 @@ struct FormatStyle {
153153
/// Options for aligning backslashes in escaped newlines.
154154
EscapedNewlineAlignmentStyle AlignEscapedNewlines;
155155

156+
/// Different styles for aligning operands.
157+
enum OperandAlignmentStyle {
158+
/// Do not align operands of binary and ternary expressions.
159+
/// The wrapped lines are indented ``ContinuationIndentWidth`` spaces from
160+
/// the start of the line.
161+
OAS_DontAlign,
162+
/// Horizontally align operands of binary and ternary expressions.
163+
///
164+
/// Specifically, this aligns operands of a single expression that needs
165+
/// to be split over multiple lines, e.g.:
166+
/// \code
167+
/// int aaa = bbbbbbbbbbbbbbb +
168+
/// ccccccccccccccc;
169+
/// \endcode
170+
///
171+
/// When ``BreakBeforeBinaryOperators`` is set, the wrapped operator is
172+
/// aligned with the operand on the first line.
173+
/// \code
174+
/// int aaa = bbbbbbbbbbbbbbb
175+
/// + ccccccccccccccc;
176+
/// \endcode
177+
OAS_Align,
178+
/// Horizontally align operands of binary and ternary expressions.
179+
///
180+
/// This is similar to ``AO_Align``, except when
181+
/// ``BreakBeforeBinaryOperators`` is set, the operator is un-indented so
182+
/// that the wrapped operand is aligned with the operand on the first line.
183+
/// \code
184+
/// int aaa = bbbbbbbbbbbbbbb
185+
/// + ccccccccccccccc;
186+
/// \endcode
187+
OAS_AlignAfterOperator,
188+
};
189+
156190
/// If ``true``, horizontally align operands of binary and ternary
157191
/// expressions.
158-
///
159-
/// Specifically, this aligns operands of a single expression that needs to be
160-
/// split over multiple lines, e.g.:
161-
/// \code
162-
/// int aaa = bbbbbbbbbbbbbbb +
163-
/// ccccccccccccccc;
164-
/// \endcode
165-
bool AlignOperands;
192+
OperandAlignmentStyle AlignOperands;
166193

167194
/// If ``true``, aligns trailing comments.
168195
/// \code

clang/lib/Format/ContinuationIndenter.cpp

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,9 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
693693
// does not help.
694694
bool HasTwoOperands =
695695
P->OperatorIndex == 0 && !P->NextOperator && !P->is(TT_ConditionalExpr);
696-
if ((!BreakBeforeOperator && !(HasTwoOperands && Style.AlignOperands)) ||
696+
if ((!BreakBeforeOperator &&
697+
!(HasTwoOperands &&
698+
Style.AlignOperands != FormatStyle::OAS_DontAlign)) ||
697699
(!State.Stack.back().LastOperatorWrapped && BreakBeforeOperator))
698700
State.Stack.back().NoLineBreakInOperand = true;
699701
}
@@ -1039,6 +1041,8 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
10391041
// * not remove the 'lead' ContinuationIndentWidth
10401042
// * always un-indent by the operator when BreakBeforeTernaryOperators=true
10411043
unsigned Indent = State.Stack.back().Indent - Style.ContinuationIndentWidth;
1044+
if (Style.BreakBeforeTernaryOperators && State.Stack.back().UnindentOperator)
1045+
Indent -= 2;
10421046
return Indent;
10431047
}
10441048
return State.Stack.back().QuestionColumn;
@@ -1118,6 +1122,13 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
11181122
return ContinuationIndent;
11191123
if (Current.is(TT_ProtoExtensionLSquare))
11201124
return State.Stack.back().Indent;
1125+
if (Current.isBinaryOperator() && State.Stack.back().UnindentOperator)
1126+
return State.Stack.back().Indent - Current.Tok.getLength() -
1127+
Current.SpacesRequiredBefore;
1128+
if (Current.isOneOf(tok::comment, TT_BlockComment, TT_LineComment) &&
1129+
NextNonComment->isBinaryOperator() && State.Stack.back().UnindentOperator)
1130+
return State.Stack.back().Indent - NextNonComment->Tok.getLength() -
1131+
NextNonComment->SpacesRequiredBefore;
11211132
if (State.Stack.back().Indent == State.FirstIndent && PreviousNonComment &&
11221133
!PreviousNonComment->isOneOf(tok::r_brace, TT_CtorInitializerComma))
11231134
// Ensure that we fall back to the continuation indent width instead of
@@ -1297,7 +1308,7 @@ void ContinuationIndenter::moveStatePastFakeLParens(LineState &State,
12971308
(Previous && (Previous->opensScope() ||
12981309
Previous->isOneOf(tok::semi, tok::kw_return) ||
12991310
(Previous->getPrecedence() == prec::Assignment &&
1300-
Style.AlignOperands) ||
1311+
Style.AlignOperands != FormatStyle::OAS_DontAlign) ||
13011312
Previous->is(TT_ObjCMethodExpr)));
13021313
for (SmallVectorImpl<prec::Level>::const_reverse_iterator
13031314
I = Current.FakeLParens.rbegin(),
@@ -1309,6 +1320,7 @@ void ContinuationIndenter::moveStatePastFakeLParens(LineState &State,
13091320
NewParenState.LastOperatorWrapped = true;
13101321
NewParenState.IsChainedConditional = false;
13111322
NewParenState.IsWrappedConditional = false;
1323+
NewParenState.UnindentOperator = false;
13121324
NewParenState.NoLineBreak =
13131325
NewParenState.NoLineBreak || State.Stack.back().NoLineBreakInOperand;
13141326

@@ -1320,14 +1332,26 @@ void ContinuationIndenter::moveStatePastFakeLParens(LineState &State,
13201332
// a builder type call after 'return' or, if the alignment after opening
13211333
// brackets is disabled.
13221334
if (!Current.isTrailingComment() &&
1323-
(Style.AlignOperands || *I < prec::Assignment) &&
1335+
(Style.AlignOperands != FormatStyle::OAS_DontAlign ||
1336+
*I < prec::Assignment) &&
13241337
(!Previous || Previous->isNot(tok::kw_return) ||
13251338
(Style.Language != FormatStyle::LK_Java && *I > 0)) &&
13261339
(Style.AlignAfterOpenBracket != FormatStyle::BAS_DontAlign ||
1327-
*I != prec::Comma || Current.NestingLevel == 0))
1340+
*I != prec::Comma || Current.NestingLevel == 0)) {
13281341
NewParenState.Indent =
13291342
std::max(std::max(State.Column, NewParenState.Indent),
13301343
State.Stack.back().LastSpace);
1344+
}
1345+
1346+
// If BreakBeforeBinaryOperators is set, un-indent a bit to account for
1347+
// the operator and keep the operands aligned
1348+
if (Style.AlignOperands == FormatStyle::OAS_AlignAfterOperator && Previous &&
1349+
(Previous->getPrecedence() == prec::Assignment ||
1350+
Previous->is(tok::kw_return) ||
1351+
(*I == prec::Conditional && Previous->is(tok::question) &&
1352+
Previous->is(TT_ConditionalExpr))) &&
1353+
!Newline)
1354+
NewParenState.UnindentOperator = true;
13311355

13321356
// Do not indent relative to the fake parentheses inserted for "." or "->".
13331357
// This is a special case to make the following to statements consistent:
@@ -1350,6 +1374,7 @@ void ContinuationIndenter::moveStatePastFakeLParens(LineState &State,
13501374
Previous->is(TT_ConditionalExpr) && I == Current.FakeLParens.rbegin() &&
13511375
!State.Stack.back().IsWrappedConditional) {
13521376
NewParenState.IsChainedConditional = true;
1377+
NewParenState.UnindentOperator = State.Stack.back().UnindentOperator;
13531378
} else if (*I == prec::Conditional ||
13541379
(!SkipFirstExtraIndent && *I > prec::Assignment &&
13551380
!Current.isTrailingComment())) {

clang/lib/Format/ContinuationIndenter.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ struct ParenState {
210210
ObjCSelectorNameFound(false), HasMultipleNestedBlocks(false),
211211
NestedBlockInlined(false), IsInsideObjCArrayLiteral(false),
212212
IsCSharpGenericTypeConstraint(false), IsChainedConditional(false),
213-
IsWrappedConditional(false) {}
213+
IsWrappedConditional(false), UnindentOperator(false) {}
214214

215215
/// \brief The token opening this parenthesis level, or nullptr if this level
216216
/// is opened by fake parenthesis.
@@ -344,6 +344,10 @@ struct ParenState {
344344
/// question mark)
345345
bool IsWrappedConditional : 1;
346346

347+
/// \brief Indicates the indent should be reduced by the length of the
348+
/// operator.
349+
bool UnindentOperator : 1;
350+
347351
bool operator<(const ParenState &Other) const {
348352
if (Indent != Other.Indent)
349353
return Indent < Other.Indent;
@@ -389,6 +393,8 @@ struct ParenState {
389393
return IsChainedConditional;
390394
if (IsWrappedConditional != Other.IsWrappedConditional)
391395
return IsWrappedConditional;
396+
if (UnindentOperator != Other.UnindentOperator)
397+
return UnindentOperator;
392398
return false;
393399
}
394400
};

clang/lib/Format/Format.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,18 @@ struct ScalarEnumerationTraits<FormatStyle::EscapedNewlineAlignmentStyle> {
308308
}
309309
};
310310

311+
template <> struct ScalarEnumerationTraits<FormatStyle::OperandAlignmentStyle> {
312+
static void enumeration(IO &IO, FormatStyle::OperandAlignmentStyle &Value) {
313+
IO.enumCase(Value, "DontAlign", FormatStyle::OAS_DontAlign);
314+
IO.enumCase(Value, "Align", FormatStyle::OAS_Align);
315+
IO.enumCase(Value, "AlignAfterOperator", FormatStyle::OAS_AlignAfterOperator);
316+
317+
// For backward compatibility.
318+
IO.enumCase(Value, "true", FormatStyle::OAS_Align);
319+
IO.enumCase(Value, "false", FormatStyle::OAS_DontAlign);
320+
}
321+
};
322+
311323
template <> struct ScalarEnumerationTraits<FormatStyle::PointerAlignmentStyle> {
312324
static void enumeration(IO &IO, FormatStyle::PointerAlignmentStyle &Value) {
313325
IO.enumCase(Value, "Middle", FormatStyle::PAS_Middle);
@@ -744,7 +756,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
744756
LLVMStyle.AccessModifierOffset = -2;
745757
LLVMStyle.AlignEscapedNewlines = FormatStyle::ENAS_Right;
746758
LLVMStyle.AlignAfterOpenBracket = FormatStyle::BAS_Align;
747-
LLVMStyle.AlignOperands = true;
759+
LLVMStyle.AlignOperands = FormatStyle::OAS_Align;
748760
LLVMStyle.AlignTrailingComments = true;
749761
LLVMStyle.AlignConsecutiveAssignments = false;
750762
LLVMStyle.AlignConsecutiveDeclarations = false;
@@ -943,7 +955,7 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) {
943955

944956
if (Language == FormatStyle::LK_Java) {
945957
GoogleStyle.AlignAfterOpenBracket = FormatStyle::BAS_DontAlign;
946-
GoogleStyle.AlignOperands = false;
958+
GoogleStyle.AlignOperands = FormatStyle::OAS_DontAlign;
947959
GoogleStyle.AlignTrailingComments = false;
948960
GoogleStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Empty;
949961
GoogleStyle.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never;
@@ -954,7 +966,7 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) {
954966
GoogleStyle.SpacesBeforeTrailingComments = 1;
955967
} else if (Language == FormatStyle::LK_JavaScript) {
956968
GoogleStyle.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak;
957-
GoogleStyle.AlignOperands = false;
969+
GoogleStyle.AlignOperands = FormatStyle::OAS_DontAlign;
958970
GoogleStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Empty;
959971
// TODO: still under discussion whether to switch to SLS_All.
960972
GoogleStyle.AllowShortLambdasOnASingleLine = FormatStyle::SLS_Empty;
@@ -1085,7 +1097,7 @@ FormatStyle getWebKitStyle() {
10851097
FormatStyle Style = getLLVMStyle();
10861098
Style.AccessModifierOffset = -4;
10871099
Style.AlignAfterOpenBracket = FormatStyle::BAS_DontAlign;
1088-
Style.AlignOperands = false;
1100+
Style.AlignOperands = FormatStyle::OAS_DontAlign;
10891101
Style.AlignTrailingComments = false;
10901102
Style.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Empty;
10911103
Style.BreakBeforeBinaryOperators = FormatStyle::BOS_All;

0 commit comments

Comments
 (0)