Quuxplusone / LLVMBugzillaTest

0 stars 0 forks source link

Excessive line breaks when using requires-clauses and requires-expressions #47875

Open Quuxplusone opened 3 years ago

Quuxplusone commented 3 years ago
Bugzilla Link PR48906
Status NEW
Importance P enhancement
Reported by Johel Ernesto Guerrero Peña (johelegp@gmail.com)
Reported on 2021-01-27 14:10:15 -0800
Last modified on 2021-01-30 18:14:52 -0800
Version trunk
Hardware PC Linux
CC djasper@google.com, johelegp@gmail.com, klimek@google.com, llvm-bugs@lists.llvm.org
Fixed by commit(s)
Attachments
Blocks
Blocked by
See also

This was mentioned in [D79773](https://reviews.llvm.org/D79773).

Since a compound requirement is neither a control statement nor a function, I suppose it might eventually need its own BraceWrapping nested configuration flag. For now, I'd prefer if they never break.


Input and expected formatted result:

template <typename T>
concept EqualityComparable = requires(T a, T b) {
  { a == b } -> bool;
};

.clang-format:

BraceWrapping:
  AfterFunction: true
BreakBeforeBraces: Custom

Actual output:

template <typename T>
concept EqualityComparable = requires(T a, T b)
{
  {
    a == b
    } -> bool;
};

.clang-format:

BraceWrapping:
  AfterControlStatement: MultiLine
BreakBeforeBraces: Custom

Actual output:

template <typename T>
concept EqualityComparable = requires(T a, T b) {
  {
    a == b
    } -> bool;
};

.clang-format:

BraceWrapping:
  AfterFunction: true
BreakBeforeBraces: Custom

Input and expected formatted result:

template <typename T> void f(T) requires requires(T a, T b) {
  { a == b } -> bool;
}
{}

Actual output:

template <typename T> void f(T) requires requires(T a, T b)
{
  {
    a == b
    } -> bool;
} {}
Quuxplusone commented 3 years ago

Actually:

Input and expected formatted result:

template <typename T>
concept EqualityComparable = requires(T a, T b) { { a == b } -> bool; };

Input and expected formatted result:

template <typename T> void f(T) requires requires(T a, T b) { { a == b } -> bool; }
{}

That's because for other constructs, ClangFormat tries to minimize lines while following the config. Similar to functions and lambda bodies with two statements, I'd expect those to always be broken into multiple lines.

Quuxplusone commented 3 years ago

Here's another case of more breaks than necessary.

Input and output:

/*export*/ template <units::Dimension D, units::UnitOf<D> U, units::QuantityValue Rep> struct subplane {
    [[nodiscard]] friend constexpr bool operator==(const subplane& l, const specialization_of<jge::subplane> auto& r) //
        requires std::equality_comparable_with<decltype(l.size), decltype(r.size)> {
        return l.top_left == r.top_left and l.size == r.size;
    }
};

Notice the // comment. Without it:

Input and expected formatted result:

/*export*/ template <units::Dimension D, units::UnitOf<D> U, units::QuantityValue Rep> struct subplane {
    [[nodiscard]] friend constexpr bool operator==(const subplane& l, const specialization_of<jge::subplane> auto& r)
        requires std::equality_comparable_with<decltype(l.size), decltype(r.size)> {
        return l.top_left == r.top_left and l.size == r.size;
    }
};

Actual output:

/*export*/ template <units::Dimension D, units::UnitOf<D> U, units::QuantityValue Rep> struct subplane {
    [[nodiscard]] friend constexpr bool
    operator==(const subplane& l, const specialization_of<jge::subplane> auto& r) requires
        std::equality_comparable_with<decltype(l.size), decltype(r.size)> {
        return l.top_left == r.top_left and l.size == r.size;
    }
};

.clang-format:

# Clang 11
AccessModifierOffset: -4
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: true
AlignConsecutiveBitFields: true
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Always
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: No
BinPackArguments: true
BinPackParameters: true
BitFieldColonSpacing: Both
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeConceptDeclarations: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
ColumnLimit: 120
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: false
DerivePointerAlignment: false
DisableFormat: false
FixNamespaceComments: true
IncludeBlocks: Merge
IncludeCategories:
    -   Regex:      '<[a-z_]+>' # C++ standard library
        Priority:   1
IndentCaseBlocks: false
IndentCaseLabels: false
IndentPPDirectives: AfterHash
IndentRequires: true
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
MaxEmptyLinesToKeep: 1
NamespaceIndentation: Inner
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterLogicalNot: true
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: c++20
TabWidth: 4
UseCRLF: false
UseTab: Never
Quuxplusone commented 3 years ago

With the same .clang-format as in comment 2, having a requires-clause disrespects the options to keep things on a single line.

Input and expected formatted result:

template <class T> struct A {
    static constexpr bool B{sizeof(T) == 42};
    template <class T> struct rep { using type = T::value_type; };
    template <class T> requires B struct rep { using type = T::value_type; };
    template <class T> requires(B) struct rep { using type = T::value_type; };
    template <class T> void f() { }
    template <class T> requires B void f() { }
    template <class T> requires(B) void f() { }
};

Actual output:

template <class T> struct A {
    static constexpr bool B{sizeof(T) == 42};
    template <class T> struct rep { using type = T::value_type; };
    template <class T>
    requires B struct rep {
        using type = T::value_type;
    };
    template <class T>
        requires(B)
    struct rep {
        using type = T::value_type;
    };
    template <class T> void f() { }
    template <class T>
    requires B void f() { }
    template <class T>
        requires(B)
    void f() { }
};