Skip to content

[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

Merged
merged 22 commits into from
Aug 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3300,6 +3300,46 @@ the configuration (without a prefix: ``Auto``).
firstValue :
SecondValueVeryVeryVeryVeryLong;

.. _BreakBinaryOperations:

**BreakBinaryOperations** (``BreakBinaryOperationsStyle``) :versionbadge:`clang-format 20` :ref:`¶ <BreakBinaryOperations>`
The break constructor initializers style to use.

Possible values:

* ``BBO_Never`` (in configuration: ``Never``)
Don't break binary operations

.. code-block:: c++

aaa + bbbb * ccccc - ddddd +
eeeeeeeeeeeeeeee;

* ``BBO_OnePerLine`` (in configuration: ``OnePerLine``)
Binary operations will either be all on the same line, or each operation
will have one line each.

.. code-block:: c++

aaa +
bbbb *
ccccc -
ddddd +
eeeeeeeeeeeeeeee;

* ``BBO_RespectPrecedence`` (in configuration: ``RespectPrecedence``)
Binary operations of a particular precedence that exceed the column
limit will have one line each.

.. code-block:: c++

aaa +
bbbb * ccccc -
ddddd +
eeeeeeeeeeeeeeee;



.. _BreakConstructorInitializers:

**BreakConstructorInitializers** (``BreakConstructorInitializersStyle``) :versionbadge:`clang-format 5` :ref:`¶ <BreakConstructorInitializers>`
Expand Down
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ AST Matchers
clang-format
------------

- Adds ``BreakBinaryOperations`` option.

libclang
--------

Expand Down
36 changes: 36 additions & 0 deletions clang/include/clang/Format/Format.h
Original file line number Diff line number Diff line change
Expand Up @@ -2231,6 +2231,41 @@ struct FormatStyle {
/// \version 3.7
bool BreakBeforeTernaryOperators;

/// Different ways to break binary operations.
enum BreakBinaryOperationsStyle : int8_t {
/// Don't break binary operations
/// \code
/// aaa + bbbb * ccccc - ddddd +
/// eeeeeeeeeeeeeeee;
/// \endcode
BBO_Never,

/// Binary operations will either be all on the same line, or each operation
/// will have one line each.
/// \code
/// aaa +
/// bbbb *
/// ccccc -
/// ddddd +
/// eeeeeeeeeeeeeeee;
/// \endcode
BBO_OnePerLine,

/// Binary operations of a particular precedence that exceed the column
/// limit will have one line each.
/// \code
/// aaa +
/// bbbb * ccccc -
/// ddddd +
/// eeeeeeeeeeeeeeee;
/// \endcode
BBO_RespectPrecedence
};

/// The break constructor initializers style to use.
/// \version 20
BreakBinaryOperationsStyle BreakBinaryOperations;

/// Different ways to break initializers.
enum BreakConstructorInitializersStyle : int8_t {
/// Break constructor initializers before the colon and after the commas.
Expand Down Expand Up @@ -5037,6 +5072,7 @@ struct FormatStyle {
BreakBeforeConceptDeclarations == R.BreakBeforeConceptDeclarations &&
BreakBeforeInlineASMColon == R.BreakBeforeInlineASMColon &&
BreakBeforeTernaryOperators == R.BreakBeforeTernaryOperators &&
BreakBinaryOperations == R.BreakBinaryOperations &&
BreakConstructorInitializers == R.BreakConstructorInitializers &&
BreakFunctionDefinitionParameters ==
R.BreakFunctionDefinitionParameters &&
Expand Down
34 changes: 33 additions & 1 deletion clang/lib/Format/ContinuationIndenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ static bool startsSegmentOfBuilderTypeCall(const FormatToken &Tok) {
// Returns \c true if \c Current starts a new parameter.
static bool startsNextParameter(const FormatToken &Current,
const FormatStyle &Style) {
const FormatToken &Previous = *Current.Previous;
assert(Current.Previous);
const auto &Previous = *Current.Previous;
if (Current.is(TT_CtorInitializerComma) &&
Style.BreakConstructorInitializers == FormatStyle::BCIS_BeforeComma) {
return true;
Expand All @@ -146,6 +147,31 @@ static bool startsNextParameter(const FormatToken &Current,
Style.BreakInheritanceList != FormatStyle::BILS_BeforeComma));
}

// Returns \c true if \c Token in an alignable binary operator
static bool isAlignableBinaryOperator(const FormatToken &Token) {
// No need to align binary operators that only have two operands.
bool HasTwoOperands = Token.OperatorIndex == 0 && !Token.NextOperator;
return Token.is(TT_BinaryOperator) && !HasTwoOperands &&
Token.getPrecedence() > prec::Conditional &&
Token.getPrecedence() < prec::PointerToMember;
}

// Returns \c true if \c Current starts the next operand in a binary operation.
static bool startsNextOperand(const FormatToken &Current) {
assert(Current.Previous);
const auto &Previous = *Current.Previous;
return isAlignableBinaryOperator(Previous) && !Current.isTrailingComment();
}

// Returns \c true if \c Current is a binary operation that must break.
static bool mustBreakBinaryOperation(const FormatToken &Current,
const FormatStyle &Style) {
return Style.BreakBinaryOperations != FormatStyle::BBO_Never &&
(Style.BreakBeforeBinaryOperators == FormatStyle::BOS_None
? startsNextOperand
: isAlignableBinaryOperator)(Current);
}

static bool opensProtoMessageField(const FormatToken &LessTok,
const FormatStyle &Style) {
if (LessTok.isNot(tok::less))
Expand Down Expand Up @@ -837,6 +863,9 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
}
if (CurrentState.AvoidBinPacking && startsNextParameter(Current, Style))
CurrentState.NoLineBreak = true;
if (mustBreakBinaryOperation(Current, Style))
CurrentState.NoLineBreak = true;

if (startsSegmentOfBuilderTypeCall(Current) &&
State.Column > getNewLineColumn(State)) {
CurrentState.ContainsUnwrappedBuilder = true;
Expand Down Expand Up @@ -1203,6 +1232,9 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
}
}

if (mustBreakBinaryOperation(Current, Style))
CurrentState.BreakBeforeParameter = true;

return Penalty;
}

Expand Down
12 changes: 12 additions & 0 deletions clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,16 @@ struct ScalarEnumerationTraits<FormatStyle::BreakBeforeInlineASMColonStyle> {
}
};

template <>
struct ScalarEnumerationTraits<FormatStyle::BreakBinaryOperationsStyle> {
static void enumeration(IO &IO,
FormatStyle::BreakBinaryOperationsStyle &Value) {
IO.enumCase(Value, "Never", FormatStyle::BBO_Never);
IO.enumCase(Value, "OnePerLine", FormatStyle::BBO_OnePerLine);
IO.enumCase(Value, "RespectPrecedence", FormatStyle::BBO_RespectPrecedence);
}
};

template <>
struct ScalarEnumerationTraits<FormatStyle::BreakConstructorInitializersStyle> {
static void
Expand Down Expand Up @@ -968,6 +978,7 @@ template <> struct MappingTraits<FormatStyle> {
Style.BreakBeforeInlineASMColon);
IO.mapOptional("BreakBeforeTernaryOperators",
Style.BreakBeforeTernaryOperators);
IO.mapOptional("BreakBinaryOperations", Style.BreakBinaryOperations);
IO.mapOptional("BreakConstructorInitializers",
Style.BreakConstructorInitializers);
IO.mapOptional("BreakFunctionDefinitionParameters",
Expand Down Expand Up @@ -1480,6 +1491,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.BreakBeforeConceptDeclarations = FormatStyle::BBCDS_Always;
LLVMStyle.BreakBeforeInlineASMColon = FormatStyle::BBIAS_OnlyMultiline;
LLVMStyle.BreakBeforeTernaryOperators = true;
LLVMStyle.BreakBinaryOperations = FormatStyle::BBO_Never;
LLVMStyle.BreakConstructorInitializers = FormatStyle::BCIS_BeforeColon;
LLVMStyle.BreakFunctionDefinitionParameters = false;
LLVMStyle.BreakInheritanceList = FormatStyle::BILS_BeforeColon;
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3164,6 +3164,15 @@ class ExpressionParser {
parse(Precedence + 1);

int CurrentPrecedence = getCurrentPrecedence();
if (Style.BreakBinaryOperations == FormatStyle::BBO_OnePerLine &&
CurrentPrecedence > prec::Conditional &&
CurrentPrecedence < prec::PointerToMember) {
// When BreakBinaryOperations is set to BreakAll,
// all operations will be on the same line or on individual lines.
// Override precedence to avoid adding fake parenthesis which could
// group operations of a different precedence level on the same line
CurrentPrecedence = prec::Additive;
}

if (Precedence == CurrentPrecedence && Current &&
Current->is(TT_SelectorName)) {
Expand Down
8 changes: 8 additions & 0 deletions clang/unittests/Format/ConfigParseTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,14 @@ TEST(ConfigParseTest, ParsesConfiguration) {
CHECK_PARSE("BreakBeforeBinaryOperators: true", BreakBeforeBinaryOperators,
FormatStyle::BOS_All);

Style.BreakBinaryOperations = FormatStyle::BBO_Never;
CHECK_PARSE("BreakBinaryOperations: OnePerLine", BreakBinaryOperations,
FormatStyle::BBO_OnePerLine);
CHECK_PARSE("BreakBinaryOperations: RespectPrecedence", BreakBinaryOperations,
FormatStyle::BBO_RespectPrecedence);
CHECK_PARSE("BreakBinaryOperations: Never", BreakBinaryOperations,
FormatStyle::BBO_Never);

Style.BreakConstructorInitializers = FormatStyle::BCIS_BeforeColon;
CHECK_PARSE("BreakConstructorInitializers: BeforeComma",
BreakConstructorInitializers, FormatStyle::BCIS_BeforeComma);
Expand Down
Loading