-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang-format] Add BreakBinaryOperations configuration #95013
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-format Author: Ameer J (ameerj) ChangesBy default, clang-format packs binary operations, but it may be desirable to have compound operations be on individual lines instead of being packed. This PR adds the option This applies to all logical and arithmetic/bitwise binary operations Maybe partially addresses #79487 ? Full diff: https://github.com/llvm/llvm-project/pull/95013.diff 5 Files Affected:
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index bb00c20922d36..246d98eef63e8 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -2065,6 +2065,23 @@ the configuration (without a prefix: ``Auto``).
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);
}
+.. _BinPackBinaryOperations:
+
+**BinPackBinaryOperations** (``Boolean``) :versionbadge:`clang-format 19` :ref:`¶ <BinPackBinaryOperations>`
+ If ``false``, binary operations will either be all on the same line
+ or each operation will have one line each.
+
+ .. code-block:: c++
+
+ true:
+ aaaaaaaaaaaaaaaaaaaa && aaaaaaaaaaaaaaaaaaaa ||
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;
+
+ false:
+ aaaaaaaaaaaaaaaaaaaa &&
+ aaaaaaaaaaaaaaaaaaaa ||
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;
+
.. _BinPackParameters:
**BinPackParameters** (``Boolean``) :versionbadge:`clang-format 3.7` :ref:`¶ <BinPackParameters>`
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 4fd6e013df25b..19b9c60ab6e99 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -1192,6 +1192,21 @@ struct FormatStyle {
/// \version 3.7
bool BinPackArguments;
+ /// If ``false``, binary operations will either be all on the same line
+ /// or each operation will have one line each.
+ /// \code
+ /// true:
+ /// aaaaaaaaaaaaaaaaaaaa && aaaaaaaaaaaaaaaaaaaa ||
+ /// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;
+ ///
+ /// false:
+ /// aaaaaaaaaaaaaaaaaaaa &&
+ /// aaaaaaaaaaaaaaaaaaaa ||
+ /// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;
+ /// \endcode
+ /// \version 19
+ bool BinPackBinaryOperations;
+
/// If ``false``, a function declaration's or function definition's
/// parameters will either all be on the same line or will have one line each.
/// \code
@@ -4978,6 +4993,7 @@ struct FormatStyle {
R.AlwaysBreakBeforeMultilineStrings &&
AttributeMacros == R.AttributeMacros &&
BinPackArguments == R.BinPackArguments &&
+ BinPackBinaryOperations == R.BinPackBinaryOperations &&
BinPackParameters == R.BinPackParameters &&
BitFieldColonSpacing == R.BitFieldColonSpacing &&
BracedInitializerIndentWidth == R.BracedInitializerIndentWidth &&
diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp
index b07360425ca6e..0780d219b68b4 100644
--- a/clang/lib/Format/ContinuationIndenter.cpp
+++ b/clang/lib/Format/ContinuationIndenter.cpp
@@ -146,6 +146,14 @@ static bool startsNextParameter(const FormatToken &Current,
Style.BreakInheritanceList != FormatStyle::BILS_BeforeComma));
}
+// Returns \c true if \c Current starts a new operand in a binary operation.
+static bool startsNextOperand(const FormatToken &Current,
+ const FormatStyle &Style) {
+ const FormatToken &Previous = *Current.Previous;
+ return Previous.is(TT_BinaryOperator) && !Current.isTrailingComment() &&
+ (Previous.getPrecedence() > prec::Conditional);
+}
+
static bool opensProtoMessageField(const FormatToken &LessTok,
const FormatStyle &Style) {
if (LessTok.isNot(tok::less))
@@ -837,6 +845,9 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
}
if (CurrentState.AvoidBinPacking && startsNextParameter(Current, Style))
CurrentState.NoLineBreak = true;
+ if (!Style.BinPackBinaryOperations && startsNextOperand(Current, Style))
+ CurrentState.NoLineBreak = true;
+
if (startsSegmentOfBuilderTypeCall(Current) &&
State.Column > getNewLineColumn(State)) {
CurrentState.ContainsUnwrappedBuilder = true;
@@ -1203,6 +1214,10 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
}
}
+ if (!Style.BinPackBinaryOperations && Previous.is(TT_BinaryOperator) &&
+ (Previous.getPrecedence() > prec::Conditional)) {
+ CurrentState.BreakBeforeParameter = true;
+ }
return Penalty;
}
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index c015e03fa15e7..4d71ce95afe1d 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -935,6 +935,7 @@ template <> struct MappingTraits<FormatStyle> {
Style.AlwaysBreakBeforeMultilineStrings);
IO.mapOptional("AttributeMacros", Style.AttributeMacros);
IO.mapOptional("BinPackArguments", Style.BinPackArguments);
+ IO.mapOptional("BinPackBinaryOperations", Style.BinPackBinaryOperations);
IO.mapOptional("BinPackParameters", Style.BinPackParameters);
IO.mapOptional("BitFieldColonSpacing", Style.BitFieldColonSpacing);
IO.mapOptional("BracedInitializerIndentWidth",
@@ -1439,6 +1440,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.AlwaysBreakBeforeMultilineStrings = false;
LLVMStyle.AttributeMacros.push_back("__capability");
LLVMStyle.BinPackArguments = true;
+ LLVMStyle.BinPackBinaryOperations = true;
LLVMStyle.BinPackParameters = true;
LLVMStyle.BitFieldColonSpacing = FormatStyle::BFCS_Both;
LLVMStyle.BracedInitializerIndentWidth = std::nullopt;
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index fb57333858529..cb20c5db4884b 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -27492,6 +27492,86 @@ TEST_F(FormatTest, SpaceBetweenKeywordAndLiteral) {
verifyFormat("return sizeof \"5\";");
}
+TEST_F(FormatTest, BinPackBinaryOperations) {
+ auto Style = getLLVMStyle();
+ Style.BinPackBinaryOperations = false;
+
+ // Logical operations
+ verifyFormat("if (condition1 && condition2) {\n"
+ "}",
+ Style);
+ verifyFormat("if (condition1 && // comment\n"
+ " condition2 &&\n"
+ " (condition3 || condition4) && // comment\n"
+ " condition5 &&\n"
+ " condition6) {\n"
+ "}",
+ Style);
+ verifyFormat("if (loooooooooooooooooooooongcondition1 &&\n"
+ " loooooooooooooooooooooongcondition2) {\n"
+ "}",
+ Style);
+
+ // Arithmetic
+ verifyFormat("const int result = lhs + rhs;\n", Style);
+ verifyFormat("const int result = loooooooooooooooooooooongop1 +\n"
+ " loooooooooooooooooooooongop2 +\n"
+ " loooooooooooooooooooooongop3;\n",
+ Style);
+
+ // clang-format off
+ verifyFormat("const int result =\n"
+ " operand1 + operand2 - (operand3 + operand4) - operand5 * operand6;\n",
+ Style);
+ // clang-format on
+
+ verifyFormat("const int result = longOperand1 +\n"
+ " longOperand2 -\n"
+ " (longOperand3 + longOperand4) -\n"
+ " longOperand5 * longOperand6;\n",
+ Style);
+
+ Style.BinPackBinaryOperations = true;
+
+ // Logical operations
+ verifyFormat("if (condition1 && condition2) {\n"
+ "}",
+ Style);
+
+ // clang-format off
+ verifyFormat("if (condition1 && condition2 && (condition3 || condition4) && condition5 &&\n"
+ " condition6) {\n"
+ "}",
+ Style);
+ // clang-format on
+
+ verifyFormat("if (loooooooooooooooooooooongcondition1 &&\n"
+ " loooooooooooooooooooooongcondition2) {\n"
+ "}",
+ Style);
+
+ // Arithmetic
+ verifyFormat("const int result = lhs + rhs;\n", Style);
+
+ // clang-format off
+ verifyFormat("const int result = loooooooooooooooooooooongop1 + loooooooooooooooooooooongop2 +\n"
+ " loooooooooooooooooooooongop3;\n",
+ Style);
+ // clang-format on
+
+ // clang-format off
+ verifyFormat("const int result = longOperand1 + longOperand2 - (longOperand3 + longOperand4) -\n"
+ " longOperand5 * longOperand6;\n",
+ Style);
+ // clang-format on
+
+ // clang-format off
+ verifyFormat("const int result =\n"
+ " operand1 + operand2 - (operand3 + operand4) - operand5 * operand6;\n",
+ Style);
+ // clang-format on
+}
+
} // namespace
} // namespace test
} // namespace format
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please add a note to clang/docs/ReleaseNotes.rst
.
Thanks for the review and approval @HazardyKnusperkeks ! I don't have write permissions to the repo so please merge this for me when you have a moment. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, we need ConfigParseTest for the new option.
This patch doesn't seem to work with the last two examples in #79487:
The logical |
It seems that this patch implements the new option without regard to the relative precedence of the binary operators:
@mydeveloperday @HazardyKnusperkeks @rymiel are you okay with this? |
Per documentation that is exactly intended. So I'm in favor of that behavior. |
I'm not finding the true/false every clear, can we use an enum? |
It seems AlignOperands is supposed to do what this new option would, so maybe we should fix/extend |
@owenca this should be fixed in the latest commit |
This new setting is only responsible for breaking long expressions, not aligning. |
Reworked this to now be an enum. Please let me know if the intent is more clear now or if anything needs further clarification. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the enum is clearer and leave you open to add new options as you did, thank you,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no major objection I'll go with whatever @owenca and @HazardyKnusperkeks agree
Fine by me. The build states: Failed Tests (1): Is this from this change? |
Fixed in the latest commit |
@owenca Can you review once more please? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the review feedback @owenca @HazardyKnusperkeks |
I wonder if anyone can come up with a better name than |
Nah, I can't find a better name, which isn't a whole sentence. |
By default, clang-format packs binary operations, but it may be desirable to have compound operations be on individual lines instead of being packed.
This PR adds the option
BreakBinaryOperations
to break up large compound binary operations to be on one line each.This applies to all logical and arithmetic/bitwise binary operations
Maybe partially addresses #79487 ?
Closes #58014
Closes #57280