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