Просмотр исходного кода

Refactor `match` parse nodes. (#6870)

Use the same node kind for the body of `case` and `default` handlers. We
don't need to distinguish these in check, so don't create extra node
kinds for them.

In order to make the nodes properly delimited, make the label (`case
...` or `default`) nodes be children of the `=>` node rather than
siblings. This allows us to use the node kind of the `=>` as the
bracketing node for the complete handler, rather than having two
different bracketing node kinds, one for each kind of label.
Richard Smith 1 месяц назад
Родитель
Сommit
99bde2acb3

+ 6 - 17
toolchain/check/handle_match.cpp

@@ -48,16 +48,6 @@ auto HandleParseNode(Context& context, Parse::MatchCaseGuardId node_id)
   return context.TODO(node_id, "HandleMatchCaseGuard");
 }
 
-auto HandleParseNode(Context& context, Parse::MatchCaseEqualGreaterId node_id)
-    -> bool {
-  return context.TODO(node_id, "HandleMatchCaseEqualGreater");
-}
-
-auto HandleParseNode(Context& context, Parse::MatchCaseStartId node_id)
-    -> bool {
-  return context.TODO(node_id, "HandleMatchCaseStart");
-}
-
 auto HandleParseNode(Context& context, Parse::MatchCaseId node_id) -> bool {
   return context.TODO(node_id, "HandleMatchCase");
 }
@@ -67,18 +57,17 @@ auto HandleParseNode(Context& context, Parse::MatchDefaultIntroducerId node_id)
   return context.TODO(node_id, "MatchDefaultIntroducer");
 }
 
-auto HandleParseNode(Context& context,
-                     Parse::MatchDefaultEqualGreaterId node_id) -> bool {
-  return context.TODO(node_id, "MatchDefaultEqualGreater");
+auto HandleParseNode(Context& context, Parse::MatchDefaultId node_id) -> bool {
+  return context.TODO(node_id, "HandleMatchDefault");
 }
 
-auto HandleParseNode(Context& context, Parse::MatchDefaultStartId node_id)
+auto HandleParseNode(Context& context, Parse::MatchHandlerStartId node_id)
     -> bool {
-  return context.TODO(node_id, "HandleMatchDefaultStart");
+  return context.TODO(node_id, "HandleMatchHandlerStart");
 }
 
-auto HandleParseNode(Context& context, Parse::MatchDefaultId node_id) -> bool {
-  return context.TODO(node_id, "HandleMatchDefault");
+auto HandleParseNode(Context& context, Parse::MatchHandlerId node_id) -> bool {
+  return context.TODO(node_id, "HandleMatchHandler");
 }
 
 auto HandleParseNode(Context& context, Parse::MatchStatementId node_id)

+ 2 - 4
toolchain/check/node_stack.h

@@ -513,18 +513,16 @@ class NodeStack {
       case Parse::NodeKind::InlineImportSpecifier:
       case Parse::NodeKind::InlineImportBody:
       case Parse::NodeKind::MatchCase:
-      case Parse::NodeKind::MatchCaseEqualGreater:
       case Parse::NodeKind::MatchCaseGuard:
       case Parse::NodeKind::MatchCaseGuardIntroducer:
       case Parse::NodeKind::MatchCaseGuardStart:
       case Parse::NodeKind::MatchCaseIntroducer:
-      case Parse::NodeKind::MatchCaseStart:
       case Parse::NodeKind::MatchCondition:
       case Parse::NodeKind::MatchConditionStart:
       case Parse::NodeKind::MatchDefault:
-      case Parse::NodeKind::MatchDefaultEqualGreater:
       case Parse::NodeKind::MatchDefaultIntroducer:
-      case Parse::NodeKind::MatchDefaultStart:
+      case Parse::NodeKind::MatchHandlerStart:
+      case Parse::NodeKind::MatchHandler:
       case Parse::NodeKind::MatchIntroducer:
       case Parse::NodeKind::MatchStatementStart:
       case Parse::NodeKind::NamespaceStart:

+ 27 - 31
toolchain/parse/handle_match.cpp

@@ -7,9 +7,8 @@
 
 namespace Carbon::Parse {
 
-static auto HandleStatementsBlockStart(Context& context, StateKind finish,
-                                       NodeKind equal_greater, NodeKind starter,
-                                       NodeKind complete) -> void {
+static auto HandleMatchHandlerStart(Context& context, NodeKind label_kind)
+    -> void {
   auto state = context.PopState();
 
   if (!context.PositionIs(Lex::TokenKind::EqualGreater)) {
@@ -19,14 +18,16 @@ static auto HandleStatementsBlockStart(Context& context, StateKind finish,
       context.emitter().Emit(*context.position(), ExpectedMatchCaseArrow);
     }
 
-    context.AddLeafNode(equal_greater, *context.position(), /*has_error=*/true);
-    context.AddNode(starter, *context.position(), /*has_error=*/true);
-    context.AddNode(complete, *context.position(), /*has_error=*/true);
+    context.AddNode(label_kind, *context.position(), /*has_error=*/true);
+    context.AddNode(NodeKind::MatchHandlerStart, *context.position(),
+                    /*has_error=*/true);
+    context.AddNode(NodeKind::MatchHandler, *context.position(),
+                    /*has_error=*/true);
     context.SkipPastLikelyEnd(*context.position());
     return;
   }
 
-  context.AddLeafNode(equal_greater, context.Consume());
+  context.AddLeafNode(label_kind, context.Consume());
 
   if (!context.PositionIs(Lex::TokenKind::OpenCurlyBrace)) {
     if (!state.has_error) {
@@ -35,14 +36,17 @@ static auto HandleStatementsBlockStart(Context& context, StateKind finish,
       context.emitter().Emit(*context.position(), ExpectedMatchCaseBlock);
     }
 
-    context.AddNode(starter, *context.position(), /*has_error=*/true);
-    context.AddNode(complete, *context.position(), /*has_error=*/true);
+    context.AddNode(NodeKind::MatchHandlerStart, *context.position(),
+                    /*has_error=*/true);
+    context.AddNode(NodeKind::MatchHandler, *context.position(),
+                    /*has_error=*/true);
     context.SkipPastLikelyEnd(*context.position());
     return;
   }
 
-  context.AddNode(starter, context.Consume(), state.has_error);
-  context.PushState(state, finish);
+  context.AddNode(NodeKind::MatchHandlerStart, context.Consume(),
+                  state.has_error);
+  context.PushState(state, StateKind::MatchHandlerFinish);
   context.PushState(StateKind::StatementScopeLoop);
 }
 
@@ -144,10 +148,12 @@ auto HandleMatchCaseIntroducer(Context& context) -> void {
 auto HandleMatchCaseAfterPattern(Context& context) -> void {
   auto state = context.PopState();
   if (state.has_error) {
-    context.AddNode(NodeKind::MatchCaseStart, *context.position(),
-                    /*has_error=*/true);
     context.AddNode(NodeKind::MatchCase, *context.position(),
                     /*has_error=*/true);
+    context.AddNode(NodeKind::MatchHandlerStart, *context.position(),
+                    /*has_error=*/true);
+    context.AddNode(NodeKind::MatchHandler, *context.position(),
+                    /*has_error=*/true);
     context.SkipPastLikelyEnd(*context.position());
     return;
   }
@@ -175,10 +181,12 @@ auto HandleMatchCaseAfterPattern(Context& context) -> void {
       context.AddNode(NodeKind::MatchCaseGuard, *context.position(),
                       /*has_error=*/true);
       state = context.PopState();
-      context.AddNode(NodeKind::MatchCaseStart, *context.position(),
-                      /*has_error=*/true);
       context.AddNode(NodeKind::MatchCase, *context.position(),
                       /*has_error=*/true);
+      context.AddNode(NodeKind::MatchHandlerStart, *context.position(),
+                      /*has_error=*/true);
+      context.AddNode(NodeKind::MatchHandler, *context.position(),
+                      /*has_error=*/true);
       context.SkipPastLikelyEnd(*context.position());
       return;
     }
@@ -208,30 +216,18 @@ auto HandleMatchCaseGuardFinish(Context& context) -> void {
 }
 
 auto HandleMatchCaseStart(Context& context) -> void {
-  HandleStatementsBlockStart(context, StateKind::MatchCaseFinish,
-                             NodeKind::MatchCaseEqualGreater,
-                             NodeKind::MatchCaseStart, NodeKind::MatchCase);
-}
-
-auto HandleMatchCaseFinish(Context& context) -> void {
-  auto state = context.PopState();
-  context.AddNode(NodeKind::MatchCase,
-                  context.ConsumeChecked(Lex::TokenKind::CloseCurlyBrace),
-                  state.has_error);
+  HandleMatchHandlerStart(context, NodeKind::MatchCase);
 }
 
 auto HandleMatchDefaultIntroducer(Context& context) -> void {
   context.AddLeafNode(NodeKind::MatchDefaultIntroducer, context.Consume());
 
-  HandleStatementsBlockStart(context, StateKind::MatchDefaultFinish,
-                             NodeKind::MatchDefaultEqualGreater,
-                             NodeKind::MatchDefaultStart,
-                             NodeKind::MatchDefault);
+  HandleMatchHandlerStart(context, NodeKind::MatchDefault);
 }
 
-auto HandleMatchDefaultFinish(Context& context) -> void {
+auto HandleMatchHandlerFinish(Context& context) -> void {
   auto state = context.PopState();
-  context.AddNode(NodeKind::MatchDefault,
+  context.AddNode(NodeKind::MatchHandler,
                   context.ConsumeChecked(Lex::TokenKind::CloseCurlyBrace),
                   state.has_error);
 }

+ 2 - 4
toolchain/parse/node_kind.def

@@ -417,13 +417,11 @@ CARBON_PARSE_NODE_KIND(MatchCaseIntroducer)
 CARBON_PARSE_NODE_KIND(MatchCaseGuardIntroducer)
 CARBON_PARSE_NODE_KIND(MatchCaseGuardStart)
 CARBON_PARSE_NODE_KIND(MatchCaseGuard)
-CARBON_PARSE_NODE_KIND(MatchCaseEqualGreater)
-CARBON_PARSE_NODE_KIND(MatchCaseStart)
 CARBON_PARSE_NODE_KIND(MatchCase)
 CARBON_PARSE_NODE_KIND(MatchDefaultIntroducer)
-CARBON_PARSE_NODE_KIND(MatchDefaultEqualGreater)
-CARBON_PARSE_NODE_KIND(MatchDefaultStart)
 CARBON_PARSE_NODE_KIND(MatchDefault)
+CARBON_PARSE_NODE_KIND(MatchHandlerStart)
+CARBON_PARSE_NODE_KIND(MatchHandler)
 
 #undef CARBON_PARSE_NODE_KIND
 #undef CARBON_PARSE_NODE_KIND_INFIX_OPERATOR

+ 8 - 11
toolchain/parse/state.def

@@ -1817,7 +1817,7 @@ CARBON_PARSE_STATE(MatchCaseGuardFinish)
 // match (...) { case ... => {...} }
 //                        ^~~~
 //   1. StatementScopeLoop
-//   2. MatchCaseFinish
+//   2. MatchHandlerFinish
 //
 // match (...) { case ... ??? }
 //                        ^
@@ -1828,19 +1828,12 @@ CARBON_PARSE_STATE(MatchCaseGuardFinish)
 //   (state done)
 CARBON_PARSE_STATE(MatchCaseStart)
 
-// Handles `match` case statements block closing `}`.
-//
-// match (...) { case ... => {...} }
-//                               ^
-//   (state done)
-CARBON_PARSE_STATE(MatchCaseFinish)
-
 // Handles `match` default introducer, `=>` and `{` opening statements block.
 //
 // match (...) { default => {...} }
 //               ^~~~~~~~~~~~
 //   1. StatementScopeLoop
-//   2. MatchDefaultFinish
+//   2. MatchHandlerFinish
 //
 // match (...) { default ??? }
 //                       ^
@@ -1851,12 +1844,16 @@ CARBON_PARSE_STATE(MatchCaseFinish)
 //   (state done)
 CARBON_PARSE_STATE(MatchDefaultIntroducer)
 
-// Handles `match` default case statements block closing `}`.
+// Handles `match` handler block closing `}`.
+//
+// match (...) { case ... => {...} }
+//                               ^
+//   (state done)
 //
 // match (...) { default => {...} }
 //                              ^
 //   (state done)
-CARBON_PARSE_STATE(MatchDefaultFinish)
+CARBON_PARSE_STATE(MatchHandlerFinish)
 
 // Handles `match` cases after the `default` case.
 //

+ 17 - 17
toolchain/parse/testdata/auto/match_case.carbon

@@ -30,29 +30,29 @@ fn f() -> bool {
 // CHECK:STDOUT:             {kind: 'BoolLiteralTrue', text: 'true'},
 // CHECK:STDOUT:           {kind: 'MatchCondition', text: ')', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'MatchStatementStart', text: '{', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'MatchCaseIntroducer', text: 'case'},
-// CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeSignature', text: 'x'},
-// CHECK:STDOUT:               {kind: 'AutoTypeLiteral', text: 'auto'},
-// CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardIntroducer', text: 'if'},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardStart', text: '('},
-// CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'x'},
-// CHECK:STDOUT:                 {kind: 'BoolLiteralTrue', text: 'true'},
-// CHECK:STDOUT:               {kind: 'InfixOperatorEqualEqual', text: '==', subtree_size: 3},
-// CHECK:STDOUT:             {kind: 'MatchCaseGuard', text: ')', subtree_size: 6},
-// CHECK:STDOUT:             {kind: 'MatchCaseEqualGreater', text: '=>'},
-// CHECK:STDOUT:           {kind: 'MatchCaseStart', text: '{', subtree_size: 12},
+// CHECK:STDOUT:               {kind: 'MatchCaseIntroducer', text: 'case'},
+// CHECK:STDOUT:                 {kind: 'IdentifierNameNotBeforeSignature', text: 'x'},
+// CHECK:STDOUT:                 {kind: 'AutoTypeLiteral', text: 'auto'},
+// CHECK:STDOUT:               {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardIntroducer', text: 'if'},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardStart', text: '('},
+// CHECK:STDOUT:                   {kind: 'IdentifierNameExpr', text: 'x'},
+// CHECK:STDOUT:                   {kind: 'BoolLiteralTrue', text: 'true'},
+// CHECK:STDOUT:                 {kind: 'InfixOperatorEqualEqual', text: '==', subtree_size: 3},
+// CHECK:STDOUT:               {kind: 'MatchCaseGuard', text: ')', subtree_size: 6},
+// CHECK:STDOUT:             {kind: 'MatchCase', text: '=>', subtree_size: 11},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '{', subtree_size: 12},
 // CHECK:STDOUT:             {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:             {kind: 'BoolLiteralTrue', text: 'true'},
 // CHECK:STDOUT:           {kind: 'ReturnStatement', text: ';', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'MatchCase', text: '}', subtree_size: 16},
-// CHECK:STDOUT:             {kind: 'MatchDefaultIntroducer', text: 'default'},
-// CHECK:STDOUT:             {kind: 'MatchDefaultEqualGreater', text: '=>'},
-// CHECK:STDOUT:           {kind: 'MatchDefaultStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '}', subtree_size: 16},
+// CHECK:STDOUT:               {kind: 'MatchDefaultIntroducer', text: 'default'},
+// CHECK:STDOUT:             {kind: 'MatchDefault', text: '=>', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '{', subtree_size: 3},
 // CHECK:STDOUT:             {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:             {kind: 'BoolLiteralFalse', text: 'false'},
 // CHECK:STDOUT:           {kind: 'ReturnStatement', text: ';', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'MatchDefault', text: '}', subtree_size: 7},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '}', subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'MatchStatement', text: '}', subtree_size: 29},
 // CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 37},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 4 - 4
toolchain/parse/testdata/match/fail_cases_after_default.carbon

@@ -54,13 +54,13 @@ fn f() -> i32 {
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'x'},
 // CHECK:STDOUT:           {kind: 'MatchCondition', text: ')', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'MatchStatementStart', text: '{', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'MatchDefaultIntroducer', text: 'default'},
-// CHECK:STDOUT:             {kind: 'MatchDefaultEqualGreater', text: '=>'},
-// CHECK:STDOUT:           {kind: 'MatchDefaultStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:               {kind: 'MatchDefaultIntroducer', text: 'default'},
+// CHECK:STDOUT:             {kind: 'MatchDefault', text: '=>', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '{', subtree_size: 3},
 // CHECK:STDOUT:             {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '1'},
 // CHECK:STDOUT:           {kind: 'ReturnStatement', text: ';', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'MatchDefault', text: '}', subtree_size: 7},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '}', subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'MatchStatement', text: '}', has_error: yes, subtree_size: 13},
 // CHECK:STDOUT:         {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:         {kind: 'IntLiteral', text: '0'},

+ 20 - 20
toolchain/parse/testdata/match/fail_missing_case_arrow.carbon

@@ -39,26 +39,26 @@ fn f() -> i32 {
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '3'},
 // CHECK:STDOUT:           {kind: 'MatchCondition', text: ')', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'MatchStatementStart', text: '{', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'MatchCaseIntroducer', text: 'case'},
-// CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeSignature', text: 'x'},
-// CHECK:STDOUT:               {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:             {kind: 'MatchCaseEqualGreater', text: '{', has_error: yes},
-// CHECK:STDOUT:           {kind: 'MatchCaseStart', text: '{', has_error: yes, subtree_size: 6},
-// CHECK:STDOUT:         {kind: 'MatchCase', text: '{', has_error: yes, subtree_size: 7},
-// CHECK:STDOUT:             {kind: 'MatchCaseIntroducer', text: 'case'},
-// CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeSignature', text: 'x'},
-// CHECK:STDOUT:               {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardIntroducer', text: 'if'},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardStart', text: '('},
-// CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'x'},
-// CHECK:STDOUT:                 {kind: 'IntLiteral', text: '3'},
-// CHECK:STDOUT:               {kind: 'InfixOperatorEqualEqual', text: '==', subtree_size: 3},
-// CHECK:STDOUT:             {kind: 'MatchCaseGuard', text: ')', subtree_size: 6},
-// CHECK:STDOUT:             {kind: 'MatchCaseEqualGreater', text: '{', has_error: yes},
-// CHECK:STDOUT:           {kind: 'MatchCaseStart', text: '{', has_error: yes, subtree_size: 12},
-// CHECK:STDOUT:         {kind: 'MatchCase', text: '{', has_error: yes, subtree_size: 13},
+// CHECK:STDOUT:               {kind: 'MatchCaseIntroducer', text: 'case'},
+// CHECK:STDOUT:                 {kind: 'IdentifierNameNotBeforeSignature', text: 'x'},
+// CHECK:STDOUT:                 {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:               {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:             {kind: 'MatchCase', text: '{', has_error: yes, subtree_size: 5},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '{', has_error: yes, subtree_size: 6},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '{', has_error: yes, subtree_size: 7},
+// CHECK:STDOUT:               {kind: 'MatchCaseIntroducer', text: 'case'},
+// CHECK:STDOUT:                 {kind: 'IdentifierNameNotBeforeSignature', text: 'x'},
+// CHECK:STDOUT:                 {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:               {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardIntroducer', text: 'if'},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardStart', text: '('},
+// CHECK:STDOUT:                   {kind: 'IdentifierNameExpr', text: 'x'},
+// CHECK:STDOUT:                   {kind: 'IntLiteral', text: '3'},
+// CHECK:STDOUT:                 {kind: 'InfixOperatorEqualEqual', text: '==', subtree_size: 3},
+// CHECK:STDOUT:               {kind: 'MatchCaseGuard', text: ')', subtree_size: 6},
+// CHECK:STDOUT:             {kind: 'MatchCase', text: '{', has_error: yes, subtree_size: 11},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '{', has_error: yes, subtree_size: 12},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '{', has_error: yes, subtree_size: 13},
 // CHECK:STDOUT:       {kind: 'MatchStatement', text: '}', subtree_size: 26},
 // CHECK:STDOUT:         {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:         {kind: 'IntLiteral', text: '0'},

+ 9 - 8
toolchain/parse/testdata/match/fail_missing_case_pattern.carbon

@@ -34,16 +34,17 @@ fn f() -> i32 {
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '3'},
 // CHECK:STDOUT:           {kind: 'MatchCondition', text: ')', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'MatchStatementStart', text: '{', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'MatchCaseIntroducer', text: 'case'},
-// CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeSignature', text: '=>', has_error: yes},
-// CHECK:STDOUT:               {kind: 'InvalidParse', text: '=>', has_error: yes},
-// CHECK:STDOUT:             {kind: 'LetBindingPattern', text: '=>', has_error: yes, subtree_size: 3},
-// CHECK:STDOUT:           {kind: 'MatchCaseStart', text: '=>', has_error: yes, subtree_size: 5},
-// CHECK:STDOUT:         {kind: 'MatchCase', text: '=>', has_error: yes, subtree_size: 6},
-// CHECK:STDOUT:       {kind: 'MatchStatement', text: '}', subtree_size: 12},
+// CHECK:STDOUT:               {kind: 'MatchCaseIntroducer', text: 'case'},
+// CHECK:STDOUT:                 {kind: 'IdentifierNameNotBeforeSignature', text: '=>', has_error: yes},
+// CHECK:STDOUT:                 {kind: 'InvalidParse', text: '=>', has_error: yes},
+// CHECK:STDOUT:               {kind: 'LetBindingPattern', text: '=>', has_error: yes, subtree_size: 3},
+// CHECK:STDOUT:             {kind: 'MatchCase', text: '=>', has_error: yes, subtree_size: 5},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '=>', has_error: yes, subtree_size: 6},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '=>', has_error: yes, subtree_size: 7},
+// CHECK:STDOUT:       {kind: 'MatchStatement', text: '}', subtree_size: 13},
 // CHECK:STDOUT:         {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:         {kind: 'IntLiteral', text: '0'},
 // CHECK:STDOUT:       {kind: 'ReturnStatement', text: ';', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 23},
+// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 24},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 7 - 7
toolchain/parse/testdata/match/fail_missing_case_statements_block.carbon

@@ -35,13 +35,13 @@ fn f() -> i32 {
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '3'},
 // CHECK:STDOUT:           {kind: 'MatchCondition', text: ')', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'MatchStatementStart', text: '{', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'MatchCaseIntroducer', text: 'case'},
-// CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeSignature', text: 'x'},
-// CHECK:STDOUT:               {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:             {kind: 'MatchCaseEqualGreater', text: '=>'},
-// CHECK:STDOUT:           {kind: 'MatchCaseStart', text: 'default', has_error: yes, subtree_size: 6},
-// CHECK:STDOUT:         {kind: 'MatchCase', text: 'default', has_error: yes, subtree_size: 7},
+// CHECK:STDOUT:               {kind: 'MatchCaseIntroducer', text: 'case'},
+// CHECK:STDOUT:                 {kind: 'IdentifierNameNotBeforeSignature', text: 'x'},
+// CHECK:STDOUT:                 {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:               {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:             {kind: 'MatchCase', text: '=>', subtree_size: 5},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: 'default', has_error: yes, subtree_size: 6},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: 'default', has_error: yes, subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'MatchStatement', text: '}', subtree_size: 13},
 // CHECK:STDOUT:         {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:         {kind: 'IntLiteral', text: '0'},

+ 4 - 4
toolchain/parse/testdata/match/fail_missing_default_arrow.carbon

@@ -34,10 +34,10 @@ fn f() -> i32 {
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '3'},
 // CHECK:STDOUT:           {kind: 'MatchCondition', text: ')', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'MatchStatementStart', text: '{', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'MatchDefaultIntroducer', text: 'default'},
-// CHECK:STDOUT:             {kind: 'MatchDefaultEqualGreater', text: '{', has_error: yes},
-// CHECK:STDOUT:           {kind: 'MatchDefaultStart', text: '{', has_error: yes, subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'MatchDefault', text: '{', has_error: yes, subtree_size: 4},
+// CHECK:STDOUT:               {kind: 'MatchDefaultIntroducer', text: 'default'},
+// CHECK:STDOUT:             {kind: 'MatchDefault', text: '{', has_error: yes, subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '{', has_error: yes, subtree_size: 3},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '{', has_error: yes, subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'MatchStatement', text: '}', subtree_size: 10},
 // CHECK:STDOUT:         {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:         {kind: 'IntLiteral', text: '0'},

+ 4 - 4
toolchain/parse/testdata/match/fail_missing_default_statements_block.carbon

@@ -34,10 +34,10 @@ fn f() -> i32 {
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '3'},
 // CHECK:STDOUT:           {kind: 'MatchCondition', text: ')', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'MatchStatementStart', text: '{', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'MatchDefaultIntroducer', text: 'default'},
-// CHECK:STDOUT:             {kind: 'MatchDefaultEqualGreater', text: '=>'},
-// CHECK:STDOUT:           {kind: 'MatchDefaultStart', text: '}', has_error: yes, subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'MatchDefault', text: '}', has_error: yes, subtree_size: 4},
+// CHECK:STDOUT:               {kind: 'MatchDefaultIntroducer', text: 'default'},
+// CHECK:STDOUT:             {kind: 'MatchDefault', text: '=>', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '}', has_error: yes, subtree_size: 3},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '}', has_error: yes, subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'MatchStatement', text: '}', subtree_size: 10},
 // CHECK:STDOUT:         {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:         {kind: 'IntLiteral', text: '0'},

+ 11 - 11
toolchain/parse/testdata/match/fail_missing_guard_close_paren.carbon

@@ -38,17 +38,17 @@ fn f() -> i32 {
 // CHECK:STDOUT:             {kind: 'BoolLiteralTrue', text: 'true'},
 // CHECK:STDOUT:           {kind: 'MatchCondition', text: ')', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'MatchStatementStart', text: '{', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'MatchCaseIntroducer', text: 'case'},
-// CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeSignature', text: 'x'},
-// CHECK:STDOUT:               {kind: 'BoolTypeLiteral', text: 'bool'},
-// CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardIntroducer', text: 'if'},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardStart', text: '('},
-// CHECK:STDOUT:               {kind: 'BoolLiteralFalse', text: 'false'},
-// CHECK:STDOUT:             {kind: 'MatchCaseGuard', text: '=>', has_error: yes, subtree_size: 4},
-// CHECK:STDOUT:             {kind: 'MatchCaseEqualGreater', text: '}', has_error: yes},
-// CHECK:STDOUT:           {kind: 'MatchCaseStart', text: '}', has_error: yes, subtree_size: 10},
-// CHECK:STDOUT:         {kind: 'MatchCase', text: '}', has_error: yes, subtree_size: 11},
+// CHECK:STDOUT:               {kind: 'MatchCaseIntroducer', text: 'case'},
+// CHECK:STDOUT:                 {kind: 'IdentifierNameNotBeforeSignature', text: 'x'},
+// CHECK:STDOUT:                 {kind: 'BoolTypeLiteral', text: 'bool'},
+// CHECK:STDOUT:               {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardIntroducer', text: 'if'},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardStart', text: '('},
+// CHECK:STDOUT:                 {kind: 'BoolLiteralFalse', text: 'false'},
+// CHECK:STDOUT:               {kind: 'MatchCaseGuard', text: '=>', has_error: yes, subtree_size: 4},
+// CHECK:STDOUT:             {kind: 'MatchCase', text: '}', has_error: yes, subtree_size: 9},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '}', has_error: yes, subtree_size: 10},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '}', has_error: yes, subtree_size: 11},
 // CHECK:STDOUT:       {kind: 'MatchStatement', text: '}', subtree_size: 17},
 // CHECK:STDOUT:         {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:         {kind: 'IntLiteral', text: '0'},

+ 13 - 12
toolchain/parse/testdata/match/fail_missing_guard_open_paren.carbon

@@ -38,20 +38,21 @@ fn f() -> i32 {
 // CHECK:STDOUT:             {kind: 'BoolLiteralTrue', text: 'true'},
 // CHECK:STDOUT:           {kind: 'MatchCondition', text: ')', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'MatchStatementStart', text: '{', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'MatchCaseIntroducer', text: 'case'},
-// CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeSignature', text: 'x'},
-// CHECK:STDOUT:               {kind: 'BoolTypeLiteral', text: 'bool'},
-// CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardIntroducer', text: 'if'},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardStart', text: 'false', has_error: yes},
-// CHECK:STDOUT:               {kind: 'InvalidParse', text: 'false', has_error: yes},
-// CHECK:STDOUT:             {kind: 'MatchCaseGuard', text: 'false', has_error: yes, subtree_size: 4},
-// CHECK:STDOUT:           {kind: 'MatchCaseStart', text: 'false', has_error: yes, subtree_size: 9},
-// CHECK:STDOUT:         {kind: 'MatchCase', text: 'false', has_error: yes, subtree_size: 10},
-// CHECK:STDOUT:       {kind: 'MatchStatement', text: '}', subtree_size: 16},
+// CHECK:STDOUT:               {kind: 'MatchCaseIntroducer', text: 'case'},
+// CHECK:STDOUT:                 {kind: 'IdentifierNameNotBeforeSignature', text: 'x'},
+// CHECK:STDOUT:                 {kind: 'BoolTypeLiteral', text: 'bool'},
+// CHECK:STDOUT:               {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardIntroducer', text: 'if'},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardStart', text: 'false', has_error: yes},
+// CHECK:STDOUT:                 {kind: 'InvalidParse', text: 'false', has_error: yes},
+// CHECK:STDOUT:               {kind: 'MatchCaseGuard', text: 'false', has_error: yes, subtree_size: 4},
+// CHECK:STDOUT:             {kind: 'MatchCase', text: 'false', has_error: yes, subtree_size: 9},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: 'false', has_error: yes, subtree_size: 10},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: 'false', has_error: yes, subtree_size: 11},
+// CHECK:STDOUT:       {kind: 'MatchStatement', text: '}', subtree_size: 17},
 // CHECK:STDOUT:         {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:         {kind: 'IntLiteral', text: '0'},
 // CHECK:STDOUT:       {kind: 'ReturnStatement', text: ';', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 27},
+// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 28},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 26 - 25
toolchain/parse/testdata/match/fail_missing_guard_parens_only_parse_errors.carbon

@@ -40,33 +40,34 @@ fn f() -> i32 {
 // CHECK:STDOUT:             {kind: 'BoolLiteralTrue', text: 'true'},
 // CHECK:STDOUT:           {kind: 'MatchCondition', text: ')', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'MatchStatementStart', text: '{', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'MatchCaseIntroducer', text: 'case'},
-// CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeSignature', text: 'x'},
-// CHECK:STDOUT:               {kind: 'BoolTypeLiteral', text: 'bool'},
-// CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardIntroducer', text: 'if'},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardStart', text: '('},
-// CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'x'},
-// CHECK:STDOUT:                 {kind: 'BoolLiteralFalse', text: 'false'},
-// CHECK:STDOUT:               {kind: 'InfixOperatorEqualEqual', text: '==', subtree_size: 3},
-// CHECK:STDOUT:             {kind: 'MatchCaseGuard', text: '=>', has_error: yes, subtree_size: 6},
-// CHECK:STDOUT:             {kind: 'MatchCaseEqualGreater', text: 'case', has_error: yes},
-// CHECK:STDOUT:           {kind: 'MatchCaseStart', text: 'case', has_error: yes, subtree_size: 12},
-// CHECK:STDOUT:         {kind: 'MatchCase', text: 'case', has_error: yes, subtree_size: 13},
-// CHECK:STDOUT:             {kind: 'MatchCaseIntroducer', text: 'case'},
-// CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeSignature', text: 'z'},
-// CHECK:STDOUT:               {kind: 'BoolTypeLiteral', text: 'bool'},
-// CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardIntroducer', text: 'if'},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardStart', text: 'true', has_error: yes},
-// CHECK:STDOUT:               {kind: 'InvalidParse', text: 'true', has_error: yes},
-// CHECK:STDOUT:             {kind: 'MatchCaseGuard', text: 'true', has_error: yes, subtree_size: 4},
-// CHECK:STDOUT:           {kind: 'MatchCaseStart', text: 'true', has_error: yes, subtree_size: 9},
-// CHECK:STDOUT:         {kind: 'MatchCase', text: 'true', has_error: yes, subtree_size: 10},
-// CHECK:STDOUT:       {kind: 'MatchStatement', text: '}', subtree_size: 29},
+// CHECK:STDOUT:               {kind: 'MatchCaseIntroducer', text: 'case'},
+// CHECK:STDOUT:                 {kind: 'IdentifierNameNotBeforeSignature', text: 'x'},
+// CHECK:STDOUT:                 {kind: 'BoolTypeLiteral', text: 'bool'},
+// CHECK:STDOUT:               {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardIntroducer', text: 'if'},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardStart', text: '('},
+// CHECK:STDOUT:                   {kind: 'IdentifierNameExpr', text: 'x'},
+// CHECK:STDOUT:                   {kind: 'BoolLiteralFalse', text: 'false'},
+// CHECK:STDOUT:                 {kind: 'InfixOperatorEqualEqual', text: '==', subtree_size: 3},
+// CHECK:STDOUT:               {kind: 'MatchCaseGuard', text: '=>', has_error: yes, subtree_size: 6},
+// CHECK:STDOUT:             {kind: 'MatchCase', text: 'case', has_error: yes, subtree_size: 11},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: 'case', has_error: yes, subtree_size: 12},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: 'case', has_error: yes, subtree_size: 13},
+// CHECK:STDOUT:               {kind: 'MatchCaseIntroducer', text: 'case'},
+// CHECK:STDOUT:                 {kind: 'IdentifierNameNotBeforeSignature', text: 'z'},
+// CHECK:STDOUT:                 {kind: 'BoolTypeLiteral', text: 'bool'},
+// CHECK:STDOUT:               {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardIntroducer', text: 'if'},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardStart', text: 'true', has_error: yes},
+// CHECK:STDOUT:                 {kind: 'InvalidParse', text: 'true', has_error: yes},
+// CHECK:STDOUT:               {kind: 'MatchCaseGuard', text: 'true', has_error: yes, subtree_size: 4},
+// CHECK:STDOUT:             {kind: 'MatchCase', text: 'true', has_error: yes, subtree_size: 9},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: 'true', has_error: yes, subtree_size: 10},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: 'true', has_error: yes, subtree_size: 11},
+// CHECK:STDOUT:       {kind: 'MatchStatement', text: '}', subtree_size: 30},
 // CHECK:STDOUT:         {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:         {kind: 'IntLiteral', text: '0'},
 // CHECK:STDOUT:       {kind: 'ReturnStatement', text: ';', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 40},
+// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 41},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 4 - 4
toolchain/parse/testdata/match/fail_missing_matched_expr.carbon

@@ -34,13 +34,13 @@ fn f() -> i32 {
 // CHECK:STDOUT:             {kind: 'InvalidParse', text: '{', has_error: yes},
 // CHECK:STDOUT:           {kind: 'MatchCondition', text: 'match', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'MatchStatementStart', text: '{', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'MatchDefaultIntroducer', text: 'default'},
-// CHECK:STDOUT:             {kind: 'MatchDefaultEqualGreater', text: '=>'},
-// CHECK:STDOUT:           {kind: 'MatchDefaultStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:               {kind: 'MatchDefaultIntroducer', text: 'default'},
+// CHECK:STDOUT:             {kind: 'MatchDefault', text: '=>', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '{', subtree_size: 3},
 // CHECK:STDOUT:             {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '1'},
 // CHECK:STDOUT:           {kind: 'ReturnStatement', text: ';', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'MatchDefault', text: '}', subtree_size: 7},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '}', subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'MatchStatement', text: '}', subtree_size: 13},
 // CHECK:STDOUT:         {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:         {kind: 'IntLiteral', text: '0'},

+ 30 - 30
toolchain/parse/testdata/match/fail_unexpected_tokens_in_cases_block.carbon

@@ -75,45 +75,45 @@ fn f() -> i32 {
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'x'},
 // CHECK:STDOUT:           {kind: 'MatchCondition', text: ')', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'MatchStatementStart', text: '{', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'MatchCaseIntroducer', text: 'case'},
-// CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeSignature', text: 'y'},
-// CHECK:STDOUT:               {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardIntroducer', text: 'if'},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardStart', text: '('},
-// CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'y'},
-// CHECK:STDOUT:                 {kind: 'IntLiteral', text: '2'},
-// CHECK:STDOUT:               {kind: 'InfixOperatorGreater', text: '>', subtree_size: 3},
-// CHECK:STDOUT:             {kind: 'MatchCaseGuard', text: ')', subtree_size: 6},
-// CHECK:STDOUT:             {kind: 'MatchCaseEqualGreater', text: '=>'},
-// CHECK:STDOUT:           {kind: 'MatchCaseStart', text: '{', subtree_size: 12},
+// CHECK:STDOUT:               {kind: 'MatchCaseIntroducer', text: 'case'},
+// CHECK:STDOUT:                 {kind: 'IdentifierNameNotBeforeSignature', text: 'y'},
+// CHECK:STDOUT:                 {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:               {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardIntroducer', text: 'if'},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardStart', text: '('},
+// CHECK:STDOUT:                   {kind: 'IdentifierNameExpr', text: 'y'},
+// CHECK:STDOUT:                   {kind: 'IntLiteral', text: '2'},
+// CHECK:STDOUT:                 {kind: 'InfixOperatorGreater', text: '>', subtree_size: 3},
+// CHECK:STDOUT:               {kind: 'MatchCaseGuard', text: ')', subtree_size: 6},
+// CHECK:STDOUT:             {kind: 'MatchCase', text: '=>', subtree_size: 11},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '{', subtree_size: 12},
 // CHECK:STDOUT:             {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '1'},
 // CHECK:STDOUT:           {kind: 'ReturnStatement', text: ';', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'MatchCase', text: '}', subtree_size: 16},
-// CHECK:STDOUT:             {kind: 'MatchCaseIntroducer', text: 'case'},
-// CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeSignature', text: 'z'},
-// CHECK:STDOUT:               {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardIntroducer', text: 'if'},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardStart', text: '('},
-// CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'z'},
-// CHECK:STDOUT:                 {kind: 'IntLiteral', text: '3'},
-// CHECK:STDOUT:               {kind: 'InfixOperatorEqualEqual', text: '==', subtree_size: 3},
-// CHECK:STDOUT:             {kind: 'MatchCaseGuard', text: ')', subtree_size: 6},
-// CHECK:STDOUT:             {kind: 'MatchCaseEqualGreater', text: '=>'},
-// CHECK:STDOUT:           {kind: 'MatchCaseStart', text: '{', subtree_size: 12},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '}', subtree_size: 16},
+// CHECK:STDOUT:               {kind: 'MatchCaseIntroducer', text: 'case'},
+// CHECK:STDOUT:                 {kind: 'IdentifierNameNotBeforeSignature', text: 'z'},
+// CHECK:STDOUT:                 {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:               {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardIntroducer', text: 'if'},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardStart', text: '('},
+// CHECK:STDOUT:                   {kind: 'IdentifierNameExpr', text: 'z'},
+// CHECK:STDOUT:                   {kind: 'IntLiteral', text: '3'},
+// CHECK:STDOUT:                 {kind: 'InfixOperatorEqualEqual', text: '==', subtree_size: 3},
+// CHECK:STDOUT:               {kind: 'MatchCaseGuard', text: ')', subtree_size: 6},
+// CHECK:STDOUT:             {kind: 'MatchCase', text: '=>', subtree_size: 11},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '{', subtree_size: 12},
 // CHECK:STDOUT:             {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '3'},
 // CHECK:STDOUT:           {kind: 'ReturnStatement', text: ';', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'MatchCase', text: '}', subtree_size: 16},
-// CHECK:STDOUT:             {kind: 'MatchDefaultIntroducer', text: 'default'},
-// CHECK:STDOUT:             {kind: 'MatchDefaultEqualGreater', text: '=>'},
-// CHECK:STDOUT:           {kind: 'MatchDefaultStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '}', subtree_size: 16},
+// CHECK:STDOUT:               {kind: 'MatchDefaultIntroducer', text: 'default'},
+// CHECK:STDOUT:             {kind: 'MatchDefault', text: '=>', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '{', subtree_size: 3},
 // CHECK:STDOUT:             {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '3'},
 // CHECK:STDOUT:           {kind: 'ReturnStatement', text: ';', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'MatchDefault', text: '}', subtree_size: 7},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '}', subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'MatchStatement', text: '}', has_error: yes, subtree_size: 45},
 // CHECK:STDOUT:         {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:         {kind: 'IntLiteral', text: '0'},

+ 45 - 45
toolchain/parse/testdata/match/match.carbon

@@ -45,63 +45,63 @@ fn f() -> i32 {
 // CHECK:STDOUT:             {kind: 'CallExpr', text: ')', subtree_size: 4},
 // CHECK:STDOUT:           {kind: 'MatchCondition', text: ')', subtree_size: 6},
 // CHECK:STDOUT:         {kind: 'MatchStatementStart', text: '{', subtree_size: 8},
-// CHECK:STDOUT:             {kind: 'MatchCaseIntroducer', text: 'case'},
-// CHECK:STDOUT:               {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:                 {kind: 'IdentifierNameNotBeforeSignature', text: 'a'},
-// CHECK:STDOUT:                 {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:               {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:               {kind: 'PatternListComma', text: ','},
-// CHECK:STDOUT:                 {kind: 'IdentifierNameNotBeforeSignature', text: 'b'},
-// CHECK:STDOUT:                 {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:               {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:             {kind: 'TuplePattern', text: ')', subtree_size: 9},
-// CHECK:STDOUT:             {kind: 'MatchCaseEqualGreater', text: '=>'},
-// CHECK:STDOUT:           {kind: 'MatchCaseStart', text: '{', subtree_size: 12},
+// CHECK:STDOUT:               {kind: 'MatchCaseIntroducer', text: 'case'},
+// CHECK:STDOUT:                 {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:                   {kind: 'IdentifierNameNotBeforeSignature', text: 'a'},
+// CHECK:STDOUT:                   {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:                 {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:                 {kind: 'PatternListComma', text: ','},
+// CHECK:STDOUT:                   {kind: 'IdentifierNameNotBeforeSignature', text: 'b'},
+// CHECK:STDOUT:                   {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:                 {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:               {kind: 'TuplePattern', text: ')', subtree_size: 9},
+// CHECK:STDOUT:             {kind: 'MatchCase', text: '=>', subtree_size: 11},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '{', subtree_size: 12},
 // CHECK:STDOUT:             {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '0'},
 // CHECK:STDOUT:           {kind: 'ReturnStatement', text: ';', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'MatchCase', text: '}', subtree_size: 16},
-// CHECK:STDOUT:             {kind: 'MatchCaseIntroducer', text: 'case'},
-// CHECK:STDOUT:               {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:                 {kind: 'IdentifierNameNotBeforeSignature', text: 'a'},
-// CHECK:STDOUT:                 {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:               {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:             {kind: 'TuplePattern', text: ')', subtree_size: 5},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardIntroducer', text: 'if'},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardStart', text: '('},
-// CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'a'},
-// CHECK:STDOUT:                 {kind: 'IntLiteral', text: '0'},
-// CHECK:STDOUT:               {kind: 'InfixOperatorLess', text: '<', subtree_size: 3},
-// CHECK:STDOUT:             {kind: 'MatchCaseGuard', text: ')', subtree_size: 6},
-// CHECK:STDOUT:             {kind: 'MatchCaseEqualGreater', text: '=>'},
-// CHECK:STDOUT:           {kind: 'MatchCaseStart', text: '{', subtree_size: 14},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '}', subtree_size: 16},
+// CHECK:STDOUT:               {kind: 'MatchCaseIntroducer', text: 'case'},
+// CHECK:STDOUT:                 {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:                   {kind: 'IdentifierNameNotBeforeSignature', text: 'a'},
+// CHECK:STDOUT:                   {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:                 {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:               {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardIntroducer', text: 'if'},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardStart', text: '('},
+// CHECK:STDOUT:                   {kind: 'IdentifierNameExpr', text: 'a'},
+// CHECK:STDOUT:                   {kind: 'IntLiteral', text: '0'},
+// CHECK:STDOUT:                 {kind: 'InfixOperatorLess', text: '<', subtree_size: 3},
+// CHECK:STDOUT:               {kind: 'MatchCaseGuard', text: ')', subtree_size: 6},
+// CHECK:STDOUT:             {kind: 'MatchCase', text: '=>', subtree_size: 13},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '{', subtree_size: 14},
 // CHECK:STDOUT:             {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '2'},
 // CHECK:STDOUT:           {kind: 'ReturnStatement', text: ';', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'MatchCase', text: '}', subtree_size: 18},
-// CHECK:STDOUT:             {kind: 'MatchCaseIntroducer', text: 'case'},
-// CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeSignature', text: 'a'},
-// CHECK:STDOUT:               {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardIntroducer', text: 'if'},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardStart', text: '('},
-// CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'a'},
-// CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'x'},
-// CHECK:STDOUT:               {kind: 'InfixOperatorExclaimEqual', text: '!=', subtree_size: 3},
-// CHECK:STDOUT:             {kind: 'MatchCaseGuard', text: ')', subtree_size: 6},
-// CHECK:STDOUT:             {kind: 'MatchCaseEqualGreater', text: '=>'},
-// CHECK:STDOUT:           {kind: 'MatchCaseStart', text: '{', subtree_size: 12},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '}', subtree_size: 18},
+// CHECK:STDOUT:               {kind: 'MatchCaseIntroducer', text: 'case'},
+// CHECK:STDOUT:                 {kind: 'IdentifierNameNotBeforeSignature', text: 'a'},
+// CHECK:STDOUT:                 {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:               {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardIntroducer', text: 'if'},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardStart', text: '('},
+// CHECK:STDOUT:                   {kind: 'IdentifierNameExpr', text: 'a'},
+// CHECK:STDOUT:                   {kind: 'IdentifierNameExpr', text: 'x'},
+// CHECK:STDOUT:                 {kind: 'InfixOperatorExclaimEqual', text: '!=', subtree_size: 3},
+// CHECK:STDOUT:               {kind: 'MatchCaseGuard', text: ')', subtree_size: 6},
+// CHECK:STDOUT:             {kind: 'MatchCase', text: '=>', subtree_size: 11},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '{', subtree_size: 12},
 // CHECK:STDOUT:             {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '3'},
 // CHECK:STDOUT:           {kind: 'ReturnStatement', text: ';', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'MatchCase', text: '}', subtree_size: 16},
-// CHECK:STDOUT:             {kind: 'MatchDefaultIntroducer', text: 'default'},
-// CHECK:STDOUT:             {kind: 'MatchDefaultEqualGreater', text: '=>'},
-// CHECK:STDOUT:           {kind: 'MatchDefaultStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '}', subtree_size: 16},
+// CHECK:STDOUT:               {kind: 'MatchDefaultIntroducer', text: 'default'},
+// CHECK:STDOUT:             {kind: 'MatchDefault', text: '=>', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '{', subtree_size: 3},
 // CHECK:STDOUT:             {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '4'},
 // CHECK:STDOUT:           {kind: 'ReturnStatement', text: ';', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'MatchDefault', text: '}', subtree_size: 7},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '}', subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'MatchStatement', text: '}', subtree_size: 66},
 // CHECK:STDOUT:         {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:         {kind: 'IntLiteral', text: '0'},

+ 49 - 49
toolchain/parse/testdata/var/unused.carbon

@@ -331,67 +331,67 @@ var unused (unused y: i32) = (0,);
 // CHECK:STDOUT:             {kind: 'CallExpr', text: ')', subtree_size: 4},
 // CHECK:STDOUT:           {kind: 'MatchCondition', text: ')', subtree_size: 6},
 // CHECK:STDOUT:         {kind: 'MatchStatementStart', text: '{', subtree_size: 8},
-// CHECK:STDOUT:             {kind: 'MatchCaseIntroducer', text: 'case'},
-// CHECK:STDOUT:                   {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:                     {kind: 'IdentifierNameNotBeforeSignature', text: 'a'},
-// CHECK:STDOUT:                     {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:                   {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:                   {kind: 'PatternListComma', text: ','},
-// CHECK:STDOUT:                     {kind: 'IdentifierNameNotBeforeSignature', text: 'b'},
-// CHECK:STDOUT:                     {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:                   {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:                 {kind: 'TuplePattern', text: ')', subtree_size: 9},
-// CHECK:STDOUT:               {kind: 'VariablePattern', text: 'var', subtree_size: 10},
-// CHECK:STDOUT:             {kind: 'UnusedPattern', text: 'unused', subtree_size: 11},
-// CHECK:STDOUT:             {kind: 'MatchCaseEqualGreater', text: '=>'},
-// CHECK:STDOUT:           {kind: 'MatchCaseStart', text: '{', subtree_size: 14},
+// CHECK:STDOUT:               {kind: 'MatchCaseIntroducer', text: 'case'},
+// CHECK:STDOUT:                     {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:                       {kind: 'IdentifierNameNotBeforeSignature', text: 'a'},
+// CHECK:STDOUT:                       {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:                     {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:                     {kind: 'PatternListComma', text: ','},
+// CHECK:STDOUT:                       {kind: 'IdentifierNameNotBeforeSignature', text: 'b'},
+// CHECK:STDOUT:                       {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:                     {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:                   {kind: 'TuplePattern', text: ')', subtree_size: 9},
+// CHECK:STDOUT:                 {kind: 'VariablePattern', text: 'var', subtree_size: 10},
+// CHECK:STDOUT:               {kind: 'UnusedPattern', text: 'unused', subtree_size: 11},
+// CHECK:STDOUT:             {kind: 'MatchCase', text: '=>', subtree_size: 13},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '{', subtree_size: 14},
 // CHECK:STDOUT:             {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '0'},
 // CHECK:STDOUT:           {kind: 'ReturnStatement', text: ';', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'MatchCase', text: '}', subtree_size: 18},
-// CHECK:STDOUT:             {kind: 'MatchCaseIntroducer', text: 'case'},
-// CHECK:STDOUT:               {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:                 {kind: 'IdentifierNameNotBeforeSignature', text: 'a'},
-// CHECK:STDOUT:                 {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:               {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:             {kind: 'TuplePattern', text: ')', subtree_size: 5},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardIntroducer', text: 'if'},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardStart', text: '('},
-// CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'a'},
-// CHECK:STDOUT:                 {kind: 'IntLiteral', text: '0'},
-// CHECK:STDOUT:               {kind: 'InfixOperatorLess', text: '<', subtree_size: 3},
-// CHECK:STDOUT:             {kind: 'MatchCaseGuard', text: ')', subtree_size: 6},
-// CHECK:STDOUT:             {kind: 'MatchCaseEqualGreater', text: '=>'},
-// CHECK:STDOUT:           {kind: 'MatchCaseStart', text: '{', subtree_size: 14},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '}', subtree_size: 18},
+// CHECK:STDOUT:               {kind: 'MatchCaseIntroducer', text: 'case'},
+// CHECK:STDOUT:                 {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:                   {kind: 'IdentifierNameNotBeforeSignature', text: 'a'},
+// CHECK:STDOUT:                   {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:                 {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:               {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardIntroducer', text: 'if'},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardStart', text: '('},
+// CHECK:STDOUT:                   {kind: 'IdentifierNameExpr', text: 'a'},
+// CHECK:STDOUT:                   {kind: 'IntLiteral', text: '0'},
+// CHECK:STDOUT:                 {kind: 'InfixOperatorLess', text: '<', subtree_size: 3},
+// CHECK:STDOUT:               {kind: 'MatchCaseGuard', text: ')', subtree_size: 6},
+// CHECK:STDOUT:             {kind: 'MatchCase', text: '=>', subtree_size: 13},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '{', subtree_size: 14},
 // CHECK:STDOUT:             {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '2'},
 // CHECK:STDOUT:           {kind: 'ReturnStatement', text: ';', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'MatchCase', text: '}', subtree_size: 18},
-// CHECK:STDOUT:             {kind: 'MatchCaseIntroducer', text: 'case'},
-// CHECK:STDOUT:                   {kind: 'IdentifierNameNotBeforeSignature', text: 'a'},
-// CHECK:STDOUT:                   {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:                 {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:               {kind: 'UnusedPattern', text: 'unused', subtree_size: 4},
-// CHECK:STDOUT:             {kind: 'VariablePattern', text: 'var', subtree_size: 5},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardIntroducer', text: 'if'},
-// CHECK:STDOUT:               {kind: 'MatchCaseGuardStart', text: '('},
-// CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'a'},
-// CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'x'},
-// CHECK:STDOUT:               {kind: 'InfixOperatorExclaimEqual', text: '!=', subtree_size: 3},
-// CHECK:STDOUT:             {kind: 'MatchCaseGuard', text: ')', subtree_size: 6},
-// CHECK:STDOUT:             {kind: 'MatchCaseEqualGreater', text: '=>'},
-// CHECK:STDOUT:           {kind: 'MatchCaseStart', text: '{', subtree_size: 14},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '}', subtree_size: 18},
+// CHECK:STDOUT:               {kind: 'MatchCaseIntroducer', text: 'case'},
+// CHECK:STDOUT:                     {kind: 'IdentifierNameNotBeforeSignature', text: 'a'},
+// CHECK:STDOUT:                     {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:                   {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:                 {kind: 'UnusedPattern', text: 'unused', subtree_size: 4},
+// CHECK:STDOUT:               {kind: 'VariablePattern', text: 'var', subtree_size: 5},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardIntroducer', text: 'if'},
+// CHECK:STDOUT:                 {kind: 'MatchCaseGuardStart', text: '('},
+// CHECK:STDOUT:                   {kind: 'IdentifierNameExpr', text: 'a'},
+// CHECK:STDOUT:                   {kind: 'IdentifierNameExpr', text: 'x'},
+// CHECK:STDOUT:                 {kind: 'InfixOperatorExclaimEqual', text: '!=', subtree_size: 3},
+// CHECK:STDOUT:               {kind: 'MatchCaseGuard', text: ')', subtree_size: 6},
+// CHECK:STDOUT:             {kind: 'MatchCase', text: '=>', subtree_size: 13},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '{', subtree_size: 14},
 // CHECK:STDOUT:             {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '3'},
 // CHECK:STDOUT:           {kind: 'ReturnStatement', text: ';', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'MatchCase', text: '}', subtree_size: 18},
-// CHECK:STDOUT:             {kind: 'MatchDefaultIntroducer', text: 'default'},
-// CHECK:STDOUT:             {kind: 'MatchDefaultEqualGreater', text: '=>'},
-// CHECK:STDOUT:           {kind: 'MatchDefaultStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '}', subtree_size: 18},
+// CHECK:STDOUT:               {kind: 'MatchDefaultIntroducer', text: 'default'},
+// CHECK:STDOUT:             {kind: 'MatchDefault', text: '=>', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'MatchHandlerStart', text: '{', subtree_size: 3},
 // CHECK:STDOUT:             {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '4'},
 // CHECK:STDOUT:           {kind: 'ReturnStatement', text: ';', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'MatchDefault', text: '}', subtree_size: 7},
+// CHECK:STDOUT:         {kind: 'MatchHandler', text: '}', subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'MatchStatement', text: '}', subtree_size: 70},
 // CHECK:STDOUT:         {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:         {kind: 'IntLiteral', text: '0'},

+ 19 - 28
toolchain/parse/typed_nodes.h

@@ -904,48 +904,40 @@ struct MatchCaseGuard {
   Lex::CloseParenTokenIndex token;
 };
 
-using MatchCaseEqualGreater =
-    LeafNode<NodeKind::MatchCaseEqualGreater, Lex::EqualGreaterTokenIndex>;
-
-struct MatchCaseStart {
-  static constexpr auto Kind = NodeKind::MatchCaseStart.Define(
-      {.bracketed_by = MatchCaseIntroducer::Kind});
+struct MatchCase {
+  static constexpr auto Kind =
+      NodeKind::MatchCase.Define({.bracketed_by = MatchCaseIntroducer::Kind});
 
   MatchCaseIntroducerId introducer;
   AnyPatternId pattern;
   std::optional<MatchCaseGuardId> guard;
-  MatchCaseEqualGreaterId equal_greater_token;
-  Lex::OpenCurlyBraceTokenIndex token;
-};
-
-struct MatchCase {
-  static constexpr auto Kind =
-      NodeKind::MatchCase.Define({.bracketed_by = MatchCaseStart::Kind});
-
-  MatchCaseStartId head;
-  llvm::SmallVector<AnyStatementId> statements;
-  Lex::CloseCurlyBraceTokenIndex token;
+  Lex::EqualGreaterTokenIndex token;
 };
 
 using MatchDefaultIntroducer =
     LeafNode<NodeKind::MatchDefaultIntroducer, Lex::DefaultTokenIndex>;
-using MatchDefaultEqualGreater =
-    LeafNode<NodeKind::MatchDefaultEqualGreater, Lex::EqualGreaterTokenIndex>;
 
-struct MatchDefaultStart {
-  static constexpr auto Kind = NodeKind::MatchDefaultStart.Define(
-      {.bracketed_by = MatchDefaultIntroducer::Kind, .child_count = 2});
+struct MatchDefault {
+  static constexpr auto Kind = NodeKind::MatchDefault.Define(
+      {.bracketed_by = MatchDefaultIntroducer::Kind, .child_count = 1});
 
   MatchDefaultIntroducerId introducer;
-  MatchDefaultEqualGreaterId equal_greater_token;
+  Lex::EqualGreaterTokenIndex token;
+};
+
+struct MatchHandlerStart {
+  static constexpr auto Kind =
+      NodeKind::MatchHandlerStart.Define({.child_count = 1});
+
+  NodeIdOneOf<MatchCase, MatchDefault> label;
   Lex::OpenCurlyBraceTokenIndex token;
 };
 
-struct MatchDefault {
+struct MatchHandler {
   static constexpr auto Kind =
-      NodeKind::MatchDefault.Define({.bracketed_by = MatchDefaultStart::Kind});
+      NodeKind::MatchHandler.Define({.bracketed_by = MatchHandlerStart::Kind});
 
-  MatchDefaultStartId introducer;
+  MatchHandlerStartId head;
   llvm::SmallVector<AnyStatementId> statements;
   Lex::CloseCurlyBraceTokenIndex token;
 };
@@ -958,8 +950,7 @@ struct MatchStatement {
 
   MatchStatementStartId head;
 
-  llvm::SmallVector<MatchCaseId> cases;
-  std::optional<MatchDefaultId> default_case;
+  llvm::SmallVector<MatchHandlerId> handlers;
   Lex::CloseCurlyBraceTokenIndex token;
 };