Format guard clauses in switches. (#1383)
There were enough tweaks in the formatting specific to the two contexts
where the guards can appear that I ultimately decided to make new Piece
classes for switch expression cases and switch statement cases. I was
hoping for more code reuse, but I think it would have been harder to
maintain if I jammed all of this into a single Piece class.
I also slightly tweaked the indentation style of switch expression
cases. When I'd first added support for switch expressions in the new
formatter, I changed the style from what the old formatter had, for
reasons that aren't entirely clear. Maybe I thought it made it more
consistent to indent +4?
Either way, once guard clauses came into play, it became clear that the
old style made more sense because it gives a clearer indentation level
for the `when` clause when it splits. So this change also makes switch
expression formatting more similar to the old style.
diff --git a/lib/src/front_end/ast_node_visitor.dart b/lib/src/front_end/ast_node_visitor.dart
index 40b18f5..4775553 100644
--- a/lib/src/front_end/ast_node_visitor.dart
+++ b/lib/src/front_end/ast_node_visitor.dart
@@ -12,6 +12,7 @@
import '../piece/adjacent.dart';
import '../piece/adjacent_strings.dart';
import '../piece/assign.dart';
+import '../piece/case.dart';
import '../piece/constructor.dart';
import '../piece/for.dart';
import '../piece/if.dart';
@@ -1005,8 +1006,7 @@
@override
Piece visitIndexExpression(IndexExpression node) {
- Piece? targetPiece;
- if (node.target case var target?) targetPiece = nodePiece(target);
+ var targetPiece = optionalNodePiece(node.target);
return createIndexExpression(targetPiece, node);
}
@@ -1135,9 +1135,16 @@
@override
Piece visitLogicalOrPattern(LogicalOrPattern node) {
+ // If a logical or pattern is the outermost pattern in a switch expression
+ // case, we want to format it like parallel cases and not indent the
+ // subsequent operands.
+ var indent = node.parent is! GuardedPattern ||
+ node.parent!.parent is! SwitchExpressionCase;
+
return createInfixChain<LogicalOrPattern>(
node,
precedence: node.operator.type.precedence,
+ indent: indent,
(expression) => (
expression.leftOperand,
expression.operator,
@@ -1627,10 +1634,16 @@
@override
Piece visitSwitchExpressionCase(SwitchExpressionCase node) {
- if (node.guardedPattern.whenClause != null) throw UnimplementedError();
+ var patternPiece = nodePiece(node.guardedPattern.pattern);
- return createAssignment(
- node.guardedPattern.pattern, node.arrow, node.expression);
+ var guardPiece = optionalNodePiece(node.guardedPattern.whenClause);
+ var arrowPiece = tokenPiece(node.arrow);
+ var bodyPiece = nodePiece(node.expression);
+
+ return CaseExpressionPiece(patternPiece, guardPiece, arrowPiece, bodyPiece,
+ canBlockSplitPattern: node.guardedPattern.pattern.canBlockSplit,
+ patternIsLogicalOr: node.guardedPattern.pattern is LogicalOrPattern,
+ canBlockSplitBody: node.expression.canBlockSplit);
}
@override
@@ -1653,19 +1666,21 @@
var casePiece = buildPiece((b) {
b.token(member.keyword);
- if (member is SwitchCase) {
- b.space();
- b.visit(member.expression);
- } else if (member is SwitchPatternCase) {
- if (member.guardedPattern.whenClause != null) {
- throw UnimplementedError();
- }
+ switch (member) {
+ case SwitchCase():
+ b.space();
+ b.visit(member.expression);
+ case SwitchPatternCase():
+ b.space();
- b.space();
- b.visit(member.guardedPattern.pattern);
- } else {
- assert(member is SwitchDefault);
- // Nothing to do.
+ var patternPiece = nodePiece(member.guardedPattern.pattern);
+ var guardPiece =
+ optionalNodePiece(member.guardedPattern.whenClause);
+
+ b.add(CaseStatementPiece(patternPiece, guardPiece));
+
+ case SwitchDefault():
+ break; // Nothing to do.
}
b.token(member.colon);
@@ -1804,6 +1819,15 @@
}
@override
+ Piece visitWhenClause(WhenClause node) {
+ return buildPiece((b) {
+ b.token(node.whenKeyword);
+ b.space();
+ b.visit(node.expression);
+ });
+ }
+
+ @override
Piece visitWhileStatement(WhileStatement node) {
var condition = buildPiece((b) {
b.add(startControlFlow(node.whileKeyword, node.leftParenthesis,
@@ -1858,4 +1882,13 @@
return result;
}
+
+ /// Visits [node] and creates a piece from it if not `null`.
+ ///
+ /// Otherwise returns `null`.
+ @override
+ Piece? optionalNodePiece(AstNode? node) {
+ if (node == null) return null;
+ return nodePiece(node);
+ }
}
diff --git a/lib/src/front_end/piece_factory.dart b/lib/src/front_end/piece_factory.dart
index edf6de3..f79bf9a 100644
--- a/lib/src/front_end/piece_factory.dart
+++ b/lib/src/front_end/piece_factory.dart
@@ -55,6 +55,8 @@
Piece nodePiece(AstNode node, {bool commaAfter = false});
+ Piece? optionalNodePiece(AstNode? node);
+
/// Creates a [ListPiece] for an argument list.
Piece createArgumentList(
Token leftBracket, Iterable<AstNode> elements, Token rightBracket) {
@@ -478,14 +480,8 @@
b.visit(caseClause.guardedPattern.pattern);
});
- Piece? guardPiece;
- if (caseClause.guardedPattern.whenClause case var whenClause?) {
- guardPiece = buildPiece((b) {
- b.token(whenClause.whenKeyword);
- b.space();
- b.visit(whenClause.expression);
- });
- }
+ var guardPiece =
+ optionalNodePiece(caseClause.guardedPattern.whenClause);
b.add(IfCasePiece(expressionPiece, casePiece, guardPiece,
canBlockSplitPattern:
@@ -690,7 +686,7 @@
/// same precedence.
Piece createInfixChain<T extends AstNode>(
T node, BinaryOperation Function(T node) destructure,
- {int? precedence}) {
+ {int? precedence, bool indent = true}) {
var builder = AdjacentBuilder(this);
var operands = <Piece>[];
@@ -716,7 +712,7 @@
traverse(node);
operands.add(builder.build());
- return InfixPiece(operands);
+ return InfixPiece(operands, indent: indent);
}
/// Creates a [ListPiece] for the given bracket-delimited set of elements.
diff --git a/lib/src/piece/case.dart b/lib/src/piece/case.dart
new file mode 100644
index 0000000..1ffe0ef
--- /dev/null
+++ b/lib/src/piece/case.dart
@@ -0,0 +1,189 @@
+// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import '../back_end/code_writer.dart';
+import '../constants.dart';
+import 'piece.dart';
+
+/// Piece for a case pattern, guard, and body in a switch expression.
+class CaseExpressionPiece extends Piece {
+ /// Split after the `=>` before the body.
+ static const State _beforeBody = State(1);
+
+ /// Split before the `when` guard clause.
+ static const State _beforeWhen = State(2);
+
+ /// Split before the `when` guard clause and after the `=>`.
+ static const State _beforeWhenAndBody = State(3);
+
+ /// The pattern the value is matched against along with the leading `case`.
+ final Piece _pattern;
+
+ /// If there is a `when` clause, that clause.
+ final Piece? _guard;
+
+ /// The `=>` token separating the pattern and body.
+ final Piece _arrow;
+
+ /// The case body expression.
+ final Piece _body;
+
+ /// Whether the pattern can be block formatted.
+ final bool _canBlockSplitPattern;
+
+ /// Whether the outermost pattern is a logical-or pattern.
+ ///
+ /// We format these specially to make them look like parallel cases:
+ ///
+ /// switch (obj) {
+ /// firstPattern ||
+ /// secondPattern ||
+ /// thirdPattern =>
+ /// body;
+ /// }
+ final bool _patternIsLogicalOr;
+
+ /// Whether the body expression can be block formatted.
+ final bool _canBlockSplitBody;
+
+ CaseExpressionPiece(this._pattern, this._guard, this._arrow, this._body,
+ {required bool canBlockSplitPattern,
+ required bool patternIsLogicalOr,
+ required bool canBlockSplitBody})
+ : _canBlockSplitPattern = canBlockSplitPattern,
+ _patternIsLogicalOr = patternIsLogicalOr,
+ _canBlockSplitBody = canBlockSplitBody;
+
+ @override
+ List<State> get additionalStates => [
+ _beforeBody,
+ if (_guard != null) ...[_beforeWhen, _beforeWhenAndBody],
+ ];
+
+ @override
+ void format(CodeWriter writer, State state) {
+ var allowNewlineInPattern = false;
+ var allowNewlineInGuard = false;
+ var allowNewlineInBody = false;
+
+ switch (state) {
+ case State.unsplit:
+ allowNewlineInBody = _canBlockSplitBody;
+ break;
+
+ case _beforeBody:
+ allowNewlineInPattern = _guard == null || _patternIsLogicalOr;
+ allowNewlineInBody = true;
+
+ case _beforeWhen:
+ // Allow newlines only in the pattern if we split before `when`.
+ allowNewlineInPattern = true;
+
+ case _beforeWhenAndBody:
+ allowNewlineInPattern = true;
+ allowNewlineInGuard = true;
+ allowNewlineInBody = true;
+ }
+
+ // If there is a split guard, then indent the pattern past it.
+ var indentPatternForGuard = !_canBlockSplitPattern &&
+ !_patternIsLogicalOr &&
+ (state == _beforeWhen || state == _beforeWhenAndBody);
+
+ if (indentPatternForGuard) writer.pushIndent(Indent.expression);
+
+ writer.pushAllowNewlines(allowNewlineInPattern);
+ writer.format(_pattern);
+ writer.popAllowNewlines();
+
+ if (indentPatternForGuard) writer.popIndent();
+
+ if (_guard case var guard?) {
+ writer.pushIndent(Indent.expression);
+ writer.pushAllowNewlines(allowNewlineInGuard);
+ writer.splitIf(state == _beforeWhen || state == _beforeWhenAndBody);
+ writer.format(guard);
+ writer.popAllowNewlines();
+ writer.popIndent();
+ }
+
+ writer.space();
+ writer.format(_arrow);
+
+ if (state != State.unsplit) writer.pushIndent(Indent.block);
+
+ writer.splitIf(state == _beforeBody || state == _beforeWhenAndBody);
+ writer.pushAllowNewlines(allowNewlineInBody);
+ writer.format(_body);
+ writer.popAllowNewlines();
+
+ if (state != State.unsplit) writer.popIndent();
+ }
+
+ @override
+ void forEachChild(void Function(Piece piece) callback) {
+ callback(_pattern);
+ if (_guard case var guard?) callback(guard);
+ callback(_arrow);
+ callback(_body);
+ }
+}
+
+/// Piece for a case pattern and guard in a switch statement.
+///
+/// Unlike [CaseExpressionPiece], this doesn't include the case body, because
+/// in a statement, the body is formatted as separate elements in the
+/// surrounding sequence.
+///
+/// This just handles splitting between the pattern and guard.
+///
+/// [State.unsplit] No split before the guard:
+///
+/// case pattern when condition:
+///
+/// [State.split] Split before the `when`:
+///
+/// case someVeryLongPattern ||
+/// anotherSubpattern
+/// when longGuardCondition &&
+/// anotherOperand:
+class CaseStatementPiece extends Piece {
+ /// The pattern the value is matched against along with the leading `case`.
+ final Piece _pattern;
+
+ /// If there is a `when` clause, that clause.
+ final Piece? _guard;
+
+ CaseStatementPiece(this._pattern, this._guard);
+
+ @override
+ List<State> get additionalStates => [
+ if (_guard != null) State.split,
+ ];
+
+ @override
+ void format(CodeWriter writer, State state) {
+ writer.pushAllowNewlines(_guard == null || state == State.split);
+
+ // If there is a guard, then indent the pattern past it.
+ if (_guard != null) writer.pushIndent(Indent.expression);
+ writer.format(_pattern);
+ if (_guard != null) writer.popIndent();
+
+ if (_guard case var guard?) {
+ writer.pushIndent(Indent.expression);
+ writer.splitIf(state == State.split);
+ writer.format(guard);
+ writer.popIndent();
+ }
+
+ writer.popAllowNewlines();
+ }
+
+ @override
+ void forEachChild(void Function(Piece piece) callback) {
+ callback(_pattern);
+ if (_guard case var guard?) callback(guard);
+ }
+}
diff --git a/lib/src/piece/infix.dart b/lib/src/piece/infix.dart
index a951f20..c996e1d 100644
--- a/lib/src/piece/infix.dart
+++ b/lib/src/piece/infix.dart
@@ -17,7 +17,10 @@
/// A leading operator like `foo as int` becomes "Infix(`foo`, `as int`)".
final List<Piece> _operands;
- InfixPiece(this._operands);
+ /// Whether operands after the first should be indented if split.
+ final bool _indent;
+
+ InfixPiece(this._operands, {bool indent = true}) : _indent = indent;
@override
List<State> get additionalStates => const [State.split];
@@ -26,7 +29,7 @@
void format(CodeWriter writer, State state) {
if (state == State.unsplit) {
writer.pushAllowNewlines(false);
- } else {
+ } else if (_indent) {
writer.pushIndent(Indent.expression);
}
@@ -42,7 +45,7 @@
if (state == State.unsplit) {
writer.popAllowNewlines();
- } else {
+ } else if (_indent) {
writer.popIndent();
}
}
diff --git a/test/expression/switch.stmt b/test/expression/switch.stmt
index 4d8817d..ea7c95b 100644
--- a/test/expression/switch.stmt
+++ b/test/expression/switch.stmt
@@ -28,7 +28,7 @@
e = switch (c) {
first => a,
second =>
- veryLongExpression + thatSplits,
+ veryLongExpression + thatSplits,
third => c,
};
>>> Discard newlines between cases.
@@ -79,8 +79,8 @@
<<<
e = switch (obj) {
1 =>
- veryLongExpression +
- thatStillMustSplit,
+ veryLongExpression +
+ thatStillMustSplit,
};
>>> Prefer to split after "=>" instead of body.
e = switch (obj) {
@@ -89,7 +89,7 @@
<<<
e = switch (obj) {
longConstant =>
- longExpression + thatMustSplit,
+ longExpression + thatMustSplit,
};
>>> Split after "=>" and in body.
e = switch (obj) {
@@ -98,6 +98,80 @@
<<<
e = switch (obj) {
longConstant =>
- veryLongLongExpression +
- thatMustSplit,
+ veryLongLongExpression +
+ thatMustSplit,
+};
+>>> Expression split in pattern.
+e = switch (obj) {
+ veryVeryLongPattern && reallyMustSplit => body
+};
+<<<
+e = switch (obj) {
+ veryVeryLongPattern &&
+ reallyMustSplit =>
+ body,
+};
+>>> Outermost logic-or patterns are indented like parallel cases.
+e = switch (obj) {
+ oneConstant || twoConstant || threeConstant => body
+};
+<<<
+e = switch (obj) {
+ oneConstant ||
+ twoConstant ||
+ threeConstant =>
+ body,
+};
+>>> Nested logic-or operands are indented.
+e = switch (obj) {
+ [oneConstant || twoConstant || threeConstant] => body
+};
+<<<
+e = switch (obj) {
+ [
+ oneConstant ||
+ twoConstant ||
+ threeConstant,
+ ] =>
+ body,
+};
+>>> Block split in pattern.
+e = switch (obj) {
+ [veryLongElement,veryLongElement,veryLongElement,] => body
+};
+<<<
+e = switch (obj) {
+ [
+ veryLongElement,
+ veryLongElement,
+ veryLongElement,
+ ] =>
+ body,
+};
+>>> Unsplit pattern with trailing block split body.
+e = switch (obj) {
+ pattern => function(veryLongElement,veryLongElement,veryLongElement)
+};
+<<<
+e = switch (obj) {
+ pattern => function(
+ veryLongElement,
+ veryLongElement,
+ veryLongElement,
+ ),
+};
+>>> Split pattern with block split body.
+e = switch (obj) {
+ pattern && anotherPattern && aThirdOne => function(veryLongElement,veryLongElement,veryLongElement)
+};
+<<<
+e = switch (obj) {
+ pattern &&
+ anotherPattern &&
+ aThirdOne =>
+ function(
+ veryLongElement,
+ veryLongElement,
+ veryLongElement,
+ ),
};
\ No newline at end of file
diff --git a/test/expression/switch_comment.stmt b/test/expression/switch_comment.stmt
index 3807cae..d001e88 100644
--- a/test/expression/switch_comment.stmt
+++ b/test/expression/switch_comment.stmt
@@ -84,4 +84,16 @@
>>> Inline block comment.
e = switch (n) { /* comment */ };
<<<
-e = switch (n) { /* comment */ };
\ No newline at end of file
+e = switch (n) { /* comment */ };
+>>> Line comment before case with guard does not force guard to split.
+e = switch (n) {
+ 0 => zero,
+ // comment
+ 1 when true => one,
+};
+<<<
+e = switch (n) {
+ 0 => zero,
+ // comment
+ 1 when true => one,
+};
\ No newline at end of file
diff --git a/test/expression/switch_guard.stmt b/test/expression/switch_guard.stmt
new file mode 100644
index 0000000..baa1631
--- /dev/null
+++ b/test/expression/switch_guard.stmt
@@ -0,0 +1,177 @@
+40 columns |
+>>> Pattern and guard on same line.
+e = switch (obj) {
+ constant when condition => body
+};
+<<<
+e = switch (obj) {
+ constant when condition => body,
+};
+>>> Pattern and guard on same line, split after `=>`.
+e = switch (obj) {
+ constant when condition => veryLongBody
+};
+<<<
+e = switch (obj) {
+ constant when condition =>
+ veryLongBody,
+};
+>>> Prefer to split at `=>` before guard.
+e = switch (obj) {
+ veryLongConstant when longCondition => body
+};
+<<<
+e = switch (obj) {
+ veryLongConstant when longCondition =>
+ body,
+};
+>>> No split in pattern, expression split in guard.
+e = switch (obj) {
+ longConstant when veryLongCondition || anotherCondition => body
+};
+<<<
+e = switch (obj) {
+ longConstant
+ when veryLongCondition ||
+ anotherCondition =>
+ body,
+};
+>>> No split in pattern, block split in guard.
+e = switch (obj) {
+ constant when [veryLongElement,veryLongElement,veryLongElement,] => body
+};
+<<<
+e = switch (obj) {
+ constant
+ when [
+ veryLongElement,
+ veryLongElement,
+ veryLongElement,
+ ] =>
+ body,
+};
+>>> Expression split in pattern forces guard to split.
+e = switch (obj) {
+ veryVeryLongPattern && reallyMustSplitHere when true => body
+};
+<<<
+e = switch (obj) {
+ veryVeryLongPattern &&
+ reallyMustSplitHere
+ when true =>
+ body,
+};
+>>> Expression split in pattern, expression split in guard.
+e = switch (obj) {
+ veryVeryLongPattern && reallyMustSplitToo when veryLongCondition
+ || anotherLongCondition => body
+};
+<<<
+e = switch (obj) {
+ veryVeryLongPattern &&
+ reallyMustSplitToo
+ when veryLongCondition ||
+ anotherLongCondition =>
+ body,
+};
+>>> Expression split in pattern, block split in guard.
+e = switch (obj) {
+ veryLongPattern && reallyMustSplitAgain when [veryLongElement,veryLongElement,veryLongElement,] => body
+};
+<<<
+e = switch (obj) {
+ veryLongPattern &&
+ reallyMustSplitAgain
+ when [
+ veryLongElement,
+ veryLongElement,
+ veryLongElement,
+ ] =>
+ body,
+};
+>>> Outermost logic-or split does not force guard to split.
+e = switch (obj) {
+ veryVeryLongPattern || reallyMustSplitHere when true => body
+};
+<<<
+e = switch (obj) {
+ veryVeryLongPattern ||
+ reallyMustSplitHere when true =>
+ body,
+};
+>>> Outermost logic-or split in pattern, expression split in guard.
+e = switch (obj) {
+ veryVeryLongPattern || reallyMustSplitToo when veryLongCondition
+ || anotherLongCondition => body
+};
+<<<
+e = switch (obj) {
+ veryVeryLongPattern ||
+ reallyMustSplitToo
+ when veryLongCondition ||
+ anotherLongCondition =>
+ body,
+};
+>>> Outermost logic-or split in pattern, block split in guard.
+e = switch (obj) {
+ veryLongPattern || reallyMustSplitAgain when [veryLongElement,veryLongElement,veryLongElement,] => body
+};
+<<<
+e = switch (obj) {
+ veryLongPattern ||
+ reallyMustSplitAgain
+ when [
+ veryLongElement,
+ veryLongElement,
+ veryLongElement,
+ ] =>
+ body,
+};
+>>> Block split in pattern forces guard to split.
+e = switch (obj) {
+ [veryLongElement,veryLongElement,veryLongElement,] when true => body
+};
+<<<
+e = switch (obj) {
+ [
+ veryLongElement,
+ veryLongElement,
+ veryLongElement,
+ ]
+ when true =>
+ body,
+};
+>>> Block split in pattern, expression split in guard.
+e = switch (obj) {
+ [veryLongElement,veryLongElement,veryLongElement,] when longCondition || anotherLongCondition => body
+};
+<<<
+e = switch (obj) {
+ [
+ veryLongElement,
+ veryLongElement,
+ veryLongElement,
+ ]
+ when longCondition ||
+ anotherLongCondition =>
+ body,
+};
+>>> Block split in pattern, block split in guard.
+e = switch (obj) {
+ [veryLongElement,veryLongElement,veryLongElement,] when
+ [veryLongElement,veryLongElement,veryLongElement,] => body
+};
+<<<
+e = switch (obj) {
+ [
+ veryLongElement,
+ veryLongElement,
+ veryLongElement,
+ ]
+ when [
+ veryLongElement,
+ veryLongElement,
+ veryLongElement,
+ ] =>
+ body,
+};
\ No newline at end of file
diff --git a/test/statement/switch.stmt b/test/statement/switch.stmt
index 7b3cacb..3e58932 100644
--- a/test/statement/switch.stmt
+++ b/test/statement/switch.stmt
@@ -273,4 +273,30 @@
]) {
case 0:
return "ok";
+}
+>>> Block split in pattern.
+switch (obj) {
+ case [veryLongElement,veryLongElement,veryLongElement,]: body;
+}
+<<<
+switch (obj) {
+ case [
+ veryLongElement,
+ veryLongElement,
+ veryLongElement,
+ ]:
+ body;
+}
+>>> Infix `||` pattern as outermost.
+### Switch expressions have some special formatting rules so here we just
+### validate that those rules don't inadvertently affect switch statements too.
+switch (obj) {
+ case oneConstant || twoConstant || threeConstant: body;
+}
+<<<
+switch (obj) {
+ case oneConstant ||
+ twoConstant ||
+ threeConstant:
+ body;
}
\ No newline at end of file
diff --git a/test/statement/switch_guard.stmt b/test/statement/switch_guard.stmt
new file mode 100644
index 0000000..b0c00ee
--- /dev/null
+++ b/test/statement/switch_guard.stmt
@@ -0,0 +1,149 @@
+40 columns |
+>>> Pattern and guard on same line.
+switch (obj) {
+ case constant when condition: body;
+}
+<<<
+switch (obj) {
+ case constant when condition:
+ body;
+}
+>>> Pattern and guard on same line, split after ":".
+switch (obj) {
+ case constant when condition: longBody;
+}
+<<<
+switch (obj) {
+ case constant when condition:
+ longBody;
+}
+>>> Prefer to split guard before case.
+switch (obj) {
+ case veryLongConstant when longCondition: body;
+}
+<<<
+switch (obj) {
+ case veryLongConstant
+ when longCondition:
+ body;
+}
+>>> No split in pattern, expression split in guard.
+switch (obj) {
+ case longConstant when condition || anotherCondition: body;
+}
+<<<
+switch (obj) {
+ case longConstant
+ when condition ||
+ anotherCondition:
+ body;
+}
+>>> No split in pattern, block split in guard.
+switch (obj) {
+ case constant when [veryLongElement,veryLongElement,]: body;
+}
+<<<
+switch (obj) {
+ case constant
+ when [
+ veryLongElement,
+ veryLongElement,
+ ]:
+ body;
+}
+>>> Expression split in pattern.
+switch (obj) {
+ case veryLongPattern || reallyMustSplit: body;
+}
+<<<
+switch (obj) {
+ case veryLongPattern ||
+ reallyMustSplit:
+ body;
+}
+>>> Expression split in pattern forces guard to split.
+switch (obj) {
+ case veryLongPattern || reallyMustSplit when true: body;
+}
+<<<
+switch (obj) {
+ case veryLongPattern ||
+ reallyMustSplit
+ when true:
+ body;
+}
+>>> Expression split in pattern, expression split in guard.
+switch (obj) {
+ case veryLongPattern || reallyMustSplit when veryLongCondition
+ || anotherLongCondition: body;
+}
+<<<
+switch (obj) {
+ case veryLongPattern ||
+ reallyMustSplit
+ when veryLongCondition ||
+ anotherLongCondition:
+ body;
+}
+>>> Expression split in pattern, block split in guard.
+switch (obj) {
+ case veryLongPattern || reallyMustSplit when [veryLongElement,veryLongElement,]: body;
+}
+<<<
+switch (obj) {
+ case veryLongPattern ||
+ reallyMustSplit
+ when [
+ veryLongElement,
+ veryLongElement,
+ ]:
+ body;
+}
+>>> Block split in pattern forces guard to split.
+switch (obj) {
+ case [veryLongElement,veryLongElement,veryLongElement,] when true: body;
+}
+<<<
+switch (obj) {
+ case [
+ veryLongElement,
+ veryLongElement,
+ veryLongElement,
+ ]
+ when true:
+ body;
+}
+>>> Block split in pattern, expression split in guard.
+switch (obj) {
+ case [veryLongElement,veryLongElement,veryLongElement,] when longCondition || anotherLongCondition: body;
+}
+<<<
+switch (obj) {
+ case [
+ veryLongElement,
+ veryLongElement,
+ veryLongElement,
+ ]
+ when longCondition ||
+ anotherLongCondition:
+ body;
+}
+>>> Block split in pattern, block split in guard.
+switch (obj) {
+ case [veryLongElement,veryLongElement,veryLongElement,] when
+ [veryLongElement,veryLongElement,veryLongElement,]: body;
+}
+<<<
+switch (obj) {
+ case [
+ veryLongElement,
+ veryLongElement,
+ veryLongElement,
+ ]
+ when [
+ veryLongElement,
+ veryLongElement,
+ veryLongElement,
+ ]:
+ body;
+}
\ No newline at end of file