diff --git a/toolchain/check/handle_match.cpp b/toolchain/check/handle_match.cpp new file mode 100644 index 000000000000..ff68fb835b44 --- /dev/null +++ b/toolchain/check/handle_match.cpp @@ -0,0 +1,95 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "toolchain/check/context.h" +#include "toolchain/check/convert.h" + +namespace Carbon::Check { + +auto HandleMatchConditionStart(Context& context, + Parse::MatchConditionStartId parse_node) + -> bool { + return context.TODO(parse_node, "HandleMatchConditionStart"); +} + +auto HandleMatchCondition(Context& context, Parse::MatchConditionId parse_node) + -> bool { + return context.TODO(parse_node, "HandleMatchCondition"); +} + +auto HandleMatchIntroducer(Context& context, + Parse::MatchIntroducerId parse_node) -> bool { + return context.TODO(parse_node, "HandleMatchIntroducer"); +} + +auto HandleMatchStatementStart(Context& context, + Parse::MatchStatementStartId parse_node) + -> bool { + return context.TODO(parse_node, "HandleMatchStatementStart"); +} + +auto HandleMatchCaseIntroducer(Context& context, + Parse::MatchCaseIntroducerId parse_node) + -> bool { + return context.TODO(parse_node, "HandleMatchCaseIntroducer"); +} + +auto HandleMatchCaseGuardIntroducer( + Context& context, Parse::MatchCaseGuardIntroducerId parse_node) -> bool { + return context.TODO(parse_node, "HandleMatchCaseGuardIntroducer"); +} + +auto HandleMatchCaseGuardStart(Context& context, + Parse::MatchCaseGuardStartId parse_node) + -> bool { + return context.TODO(parse_node, "HandleMatchCaseGuardStart"); +} + +auto HandleMatchCaseGuard(Context& context, Parse::MatchCaseGuardId parse_node) + -> bool { + return context.TODO(parse_node, "HandleMatchCaseGuard"); +} + +auto HandleMatchCaseEqualGreater(Context& context, + Parse::MatchCaseEqualGreaterId parse_node) + -> bool { + return context.TODO(parse_node, "HandleMatchCaseEqualGreater"); +} + +auto HandleMatchCaseStart(Context& context, Parse::MatchCaseStartId parse_node) + -> bool { + return context.TODO(parse_node, "HandleMatchCaseStart"); +} + +auto HandleMatchCase(Context& context, Parse::MatchCaseId parse_node) -> bool { + return context.TODO(parse_node, "HandleMatchCase"); +} + +auto HandleMatchDefaultIntroducer(Context& context, + Parse::MatchDefaultIntroducerId parse_node) + -> bool { + return context.TODO(parse_node, "MatchDefaultIntroducer"); +} + +auto HandleMatchDefaultEqualGreater( + Context& context, Parse::MatchDefaultEqualGreaterId parse_node) -> bool { + return context.TODO(parse_node, "MatchDefaultEqualGreater"); +} + +auto HandleMatchDefaultStart(Context& context, + Parse::MatchDefaultStartId parse_node) -> bool { + return context.TODO(parse_node, "HandleMatchDefaultStart"); +} + +auto HandleMatchDefault(Context& context, Parse::MatchDefaultId parse_node) + -> bool { + return context.TODO(parse_node, "HandleMatchDefault"); +} + +auto HandleMatchStatement(Context& context, Parse::MatchStatementId parse_node) + -> bool { + return context.TODO(parse_node, "HandleMatchStatement"); +} + +} // namespace Carbon::Check diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 376ffca1f9b0..05ce76425e1e 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -73,6 +73,12 @@ CARBON_DIAGNOSTIC_KIND(ExpectedVarAfterReturned) CARBON_DIAGNOSTIC_KIND(ExpectedVariableDecl) CARBON_DIAGNOSTIC_KIND(ExpectedChoiceDefinition) CARBON_DIAGNOSTIC_KIND(ExpectedChoiceAlternativeName) +CARBON_DIAGNOSTIC_KIND(ExpectedMatchCases) +CARBON_DIAGNOSTIC_KIND(ExpectedMatchCasesBlock) +CARBON_DIAGNOSTIC_KIND(ExpectedMatchCaseArrow) +CARBON_DIAGNOSTIC_KIND(ExpectedMatchCaseBlock) +CARBON_DIAGNOSTIC_KIND(UnexpectedTokenInMatchCasesBlock) +CARBON_DIAGNOSTIC_KIND(UnreachableMatchCase) CARBON_DIAGNOSTIC_KIND(OperatorRequiresParentheses) CARBON_DIAGNOSTIC_KIND(StatementOperatorAsSubExpr) CARBON_DIAGNOSTIC_KIND(UnaryOperatorRequiresParentheses) diff --git a/toolchain/parse/handle_match.cpp b/toolchain/parse/handle_match.cpp new file mode 100644 index 000000000000..0136df85a4ed --- /dev/null +++ b/toolchain/parse/handle_match.cpp @@ -0,0 +1,232 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "toolchain/parse/context.h" + +namespace Carbon::Parse { + +static auto HandleStatementsBlockStart(Context& context, State finish, + NodeKind equal_greater, NodeKind starter, + NodeKind complete) -> void { + auto state = context.PopState(); + + if (!context.PositionIs(Lex::TokenKind::EqualGreater)) { + if (!state.has_error) { + CARBON_DIAGNOSTIC(ExpectedMatchCaseArrow, Error, + "Expected `=>` introducing statement block."); + context.emitter().Emit(*context.position(), ExpectedMatchCaseArrow); + } + + context.AddLeafNode(equal_greater, *context.position(), true); + context.AddNode(starter, *context.position(), state.subtree_start, true); + context.AddNode(complete, *context.position(), state.subtree_start, true); + context.SkipPastLikelyEnd(*context.position()); + return; + } + + context.AddLeafNode(equal_greater, context.Consume()); + + if (!context.PositionIs(Lex::TokenKind::OpenCurlyBrace)) { + if (!state.has_error) { + CARBON_DIAGNOSTIC(ExpectedMatchCaseBlock, Error, + "Expected `{{` after `=>`."); + context.emitter().Emit(*context.position(), ExpectedMatchCaseBlock); + } + + context.AddNode(starter, *context.position(), state.subtree_start, true); + context.AddNode(complete, *context.position(), state.subtree_start, true); + context.SkipPastLikelyEnd(*context.position()); + return; + } + + context.AddNode(starter, context.Consume(), state.subtree_start, + state.has_error); + context.PushState(state, finish); + context.PushState(State::StatementScopeLoop); +} + +static auto EmitUnexpectedTokenAndRecover(Context& context) -> void { + CARBON_DIAGNOSTIC(UnexpectedTokenInMatchCasesBlock, Error, + "Unexpected `{0}`; expected `case`, `default` or `}`.", + Lex::TokenKind); + context.emitter().Emit(*context.position(), UnexpectedTokenInMatchCasesBlock, + context.PositionKind()); + context.ReturnErrorOnState(); + context.SkipPastLikelyEnd(*context.position()); +} + +auto HandleMatchIntroducer(Context& context) -> void { + auto state = context.PopState(); + context.AddLeafNode(NodeKind::Placeholder, *context.position()); + context.PushState(state, State::MatchConditionFinish); + context.PushState(State::ParenConditionAsMatch); + context.ConsumeAndDiscard(); +} + +auto HandleMatchConditionFinish(Context& context) -> void { + auto state = context.PopState(); + context.ReplacePlaceholderNode(state.subtree_start, NodeKind::MatchIntroducer, + state.token); + + if (!context.PositionIs(Lex::TokenKind::OpenCurlyBrace)) { + if (!state.has_error) { + CARBON_DIAGNOSTIC(ExpectedMatchCasesBlock, Error, + "Expected `{{` starting block with cases."); + context.emitter().Emit(*context.position(), ExpectedMatchCasesBlock); + } + + context.AddNode(NodeKind::MatchStatementStart, *context.position(), + state.subtree_start, true); + context.AddNode(NodeKind::MatchStatement, *context.position(), + state.subtree_start, true); + context.SkipPastLikelyEnd(*context.position()); + return; + } + + context.AddNode(NodeKind::MatchStatementStart, context.Consume(), + state.subtree_start, state.has_error); + + state.has_error = false; + if (context.PositionIs(Lex::TokenKind::CloseCurlyBrace)) { + CARBON_DIAGNOSTIC(ExpectedMatchCases, Error, "Expected cases."); + context.emitter().Emit(*context.position(), ExpectedMatchCases); + state.has_error = true; + } + + context.PushState(state, State::MatchStatementFinish); + context.PushState(State::MatchCaseLoop); +} + +auto HandleMatchCaseLoop(Context& context) -> void { + context.PopAndDiscardState(); + + if (context.PositionIs(Lex::TokenKind::Case)) { + context.PushState(State::MatchCaseLoop); + context.PushState(State::MatchCaseIntroducer); + } else if (context.PositionIs(Lex::TokenKind::Default)) { + context.PushState(State::MatchCaseLoopAfterDefault); + context.PushState(State::MatchDefaultIntroducer); + } else if (!context.PositionIs(Lex::TokenKind::CloseCurlyBrace)) { + EmitUnexpectedTokenAndRecover(context); + context.PushState(State::MatchCaseLoop); + } +} + +auto HandleMatchCaseLoopAfterDefault(Context& context) -> void { + context.PopAndDiscardState(); + + Lex::TokenKind kind = context.PositionKind(); + if (kind == Lex::TokenKind::Case or kind == Lex::TokenKind::Default) { + CARBON_DIAGNOSTIC(UnreachableMatchCase, Error, "Unreachable case; `{0}{1}", + Lex::TokenKind, std::string); + context.emitter().Emit(*context.position(), UnreachableMatchCase, kind, + "` occurs after the `default`"); + + context.ReturnErrorOnState(); + context.PushState(State::MatchCaseLoopAfterDefault); + context.SkipPastLikelyEnd(*context.position()); + return; + } else if (kind != Lex::TokenKind::CloseCurlyBrace) { + EmitUnexpectedTokenAndRecover(context); + context.PushState(State::MatchCaseLoopAfterDefault); + } +} + +auto HandleMatchCaseIntroducer(Context& context) -> void { + auto state = context.PopState(); + + context.AddLeafNode(NodeKind::MatchCaseIntroducer, context.Consume()); + context.PushState(state, State::MatchCaseAfterPattern); + context.PushState(State::Pattern); +} + +auto HandleMatchCaseAfterPattern(Context& context) -> void { + auto state = context.PopState(); + if (state.has_error) { + context.AddNode(NodeKind::MatchCaseStart, *context.position(), + state.subtree_start, true); + context.AddNode(NodeKind::MatchCase, *context.position(), + state.subtree_start, true); + context.SkipPastLikelyEnd(*context.position()); + return; + } + + context.PushState(state, State::MatchCaseStart); + if (context.PositionIs(Lex::TokenKind::If)) { + context.PushState(State::MatchCaseGuardFinish); + context.AddLeafNode(NodeKind::MatchCaseGuardIntroducer, context.Consume()); + auto open_paren = context.ConsumeIf(Lex::TokenKind::OpenParen); + if (open_paren) { + context.AddLeafNode(NodeKind::MatchCaseGuardStart, *open_paren); + context.PushState(State::Expr); + } else { + context.AddLeafNode(NodeKind::MatchCaseGuardStart, *context.position(), + true); + context.AddLeafNode(NodeKind::InvalidParse, *context.position(), true); + state = context.PopState(); + context.AddNode(NodeKind::MatchCaseGuard, *context.position(), + state.subtree_start, true); + state = context.PopState(); + context.AddNode(NodeKind::MatchCaseStart, *context.position(), + state.subtree_start, true); + context.AddNode(NodeKind::MatchCase, *context.position(), + state.subtree_start, true); + context.SkipPastLikelyEnd(*context.position()); + return; + } + } +} + +auto HandleMatchCaseGuardFinish(Context& context) -> void { + auto state = context.PopState(); + + auto close_paren = context.ConsumeIf(Lex::TokenKind::CloseParen); + if (close_paren) { + context.AddNode(NodeKind::MatchCaseGuard, *close_paren, state.subtree_start, + state.has_error); + } else { + context.AddNode(NodeKind::MatchCaseGuard, *context.position(), + state.subtree_start, true); + context.ReturnErrorOnState(); + context.SkipPastLikelyEnd(*context.position()); + return; + } +} + +auto HandleMatchCaseStart(Context& context) -> void { + HandleStatementsBlockStart(context, State::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.subtree_start, state.has_error); +} + +auto HandleMatchDefaultIntroducer(Context& context) -> void { + context.AddLeafNode(NodeKind::MatchDefaultIntroducer, context.Consume()); + + HandleStatementsBlockStart( + context, State::MatchDefaultFinish, NodeKind::MatchDefaultEqualGreater, + NodeKind::MatchDefaultStart, NodeKind::MatchDefault); +} + +auto HandleMatchDefaultFinish(Context& context) -> void { + auto state = context.PopState(); + context.AddNode(NodeKind::MatchDefault, + context.ConsumeChecked(Lex::TokenKind::CloseCurlyBrace), + state.subtree_start, state.has_error); +} + +auto HandleMatchStatementFinish(Context& context) -> void { + auto state = context.PopState(); + context.AddNode(NodeKind::MatchStatement, + context.ConsumeChecked(Lex::TokenKind::CloseCurlyBrace), + state.subtree_start, state.has_error); +} + +} // namespace Carbon::Parse diff --git a/toolchain/parse/handle_paren_condition.cpp b/toolchain/parse/handle_paren_condition.cpp index e4a587f4af37..c75805f0e05d 100644 --- a/toolchain/parse/handle_paren_condition.cpp +++ b/toolchain/parse/handle_paren_condition.cpp @@ -6,7 +6,7 @@ namespace Carbon::Parse { -// Handles ParenConditionAs(If|While). +// Handles ParenConditionAs(If|While|Match). static auto HandleParenCondition(Context& context, NodeKind start_kind, State finish_state) -> void { auto state = context.PopState(); @@ -39,6 +39,11 @@ auto HandleParenConditionAsWhile(Context& context) -> void { State::ParenConditionFinishAsWhile); } +auto HandleParenConditionAsMatch(Context& context) -> void { + HandleParenCondition(context, NodeKind::MatchConditionStart, + State::ParenConditionFinishAsMatch); +} + auto HandleParenConditionFinishAsIf(Context& context) -> void { auto state = context.PopState(); @@ -52,4 +57,11 @@ auto HandleParenConditionFinishAsWhile(Context& context) -> void { NodeKind::WhileCondition); } +auto HandleParenConditionFinishAsMatch(Context& context) -> void { + auto state = context.PopState(); + + context.ConsumeAndAddCloseSymbol(state.token, state, + NodeKind::MatchCondition); +} + } // namespace Carbon::Parse diff --git a/toolchain/parse/handle_statement.cpp b/toolchain/parse/handle_statement.cpp index 2a3acb766dc2..5b1e53f1eced 100644 --- a/toolchain/parse/handle_statement.cpp +++ b/toolchain/parse/handle_statement.cpp @@ -52,6 +52,10 @@ auto HandleStatement(Context& context) -> void { context.PushState(State::StatementWhile); break; } + case Lex::TokenKind::Match: { + context.PushState(State::MatchIntroducer); + break; + } default: { context.PushState(State::ExprStatementFinish); context.PushStateForExpr(PrecedenceGroup::ForExprStatement()); diff --git a/toolchain/parse/node_kind.def b/toolchain/parse/node_kind.def index 8069f0fd7fe8..ea7690bae333 100644 --- a/toolchain/parse/node_kind.def +++ b/toolchain/parse/node_kind.def @@ -737,6 +737,64 @@ CARBON_PARSE_NODE_KIND_BRACKET(ChoiceDefinition, ChoiceDefinitionStart, // ChoiceAlternativeList CARBON_PARSE_NODE_KIND_CHILD_COUNT(ChoiceAlternativeListComma, 0, Comma) +// `match`: +// MatchIntroducer +// MatchConditionStart +// _external_: expression +// MatchCondition +// MatchStatementStart +// _repeated_ _external_: MatchCase +// _optional_ _external_: MatchStatementDefault +// MatchStatement +CARBON_PARSE_NODE_KIND_CHILD_COUNT(MatchIntroducer, 0, Match) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(MatchConditionStart, 0, + CARBON_IF_VALID(OpenParen)) +CARBON_PARSE_NODE_KIND_BRACKET(MatchCondition, MatchConditionStart, + CARBON_IF_VALID(CloseParen)) +CARBON_PARSE_NODE_KIND_BRACKET(MatchStatementStart, MatchIntroducer, + CARBON_IF_VALID(OpenCurlyBrace)) +CARBON_PARSE_NODE_KIND_BRACKET(MatchStatement, MatchStatementStart, + CARBON_IF_VALID(CloseCurlyBrace)) + +// `case`: +// MatchCaseIntroducer +// _external_: Pattern +// MatchCaseGuardIntroducer +// MatchCaseGuardStart +// _external_: expression +// MatchCaseGuard +// MatchCaseEqualGreater +// MatchCaseStart +// _repeated_ _external_: statement +// MatchCase +CARBON_PARSE_NODE_KIND_CHILD_COUNT(MatchCaseIntroducer, 0, Case) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(MatchCaseGuardIntroducer, 0, If) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(MatchCaseGuardStart, 0, + CARBON_IF_VALID(OpenParen)) +CARBON_PARSE_NODE_KIND_BRACKET(MatchCaseGuard, + MatchCaseGuardIntroducer, + CARBON_IF_VALID(CloseParen)) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(MatchCaseEqualGreater, 0, + CARBON_IF_VALID(EqualGreater)) +CARBON_PARSE_NODE_KIND_BRACKET(MatchCaseStart, MatchCaseIntroducer, + CARBON_IF_VALID(OpenCurlyBrace)) +CARBON_PARSE_NODE_KIND_BRACKET(MatchCase, MatchCaseStart, + CARBON_IF_VALID(CloseCurlyBrace)) + +// `default`: +// MatchDefaultIntroducer +// MatchDefaultEqualGreater +// MatchDefaultStart +// _repeated_ _external_: statement +// MatchDefault +CARBON_PARSE_NODE_KIND_CHILD_COUNT(MatchDefaultIntroducer, 0, Default) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(MatchDefaultEqualGreater, 0, + CARBON_IF_VALID(EqualGreater)) +CARBON_PARSE_NODE_KIND_BRACKET(MatchDefaultStart, MatchDefaultIntroducer, + CARBON_IF_VALID(OpenCurlyBrace)) +CARBON_PARSE_NODE_KIND_BRACKET(MatchDefault, MatchDefaultStart, + CARBON_IF_VALID(CloseCurlyBrace)) + #undef CARBON_PARSE_NODE_KIND #undef CARBON_PARSE_NODE_KIND_BRACKET #undef CARBON_PARSE_NODE_KIND_CHILD_COUNT diff --git a/toolchain/parse/state.def b/toolchain/parse/state.def index ce3e064973c8..2e34d3f7ab0a 100644 --- a/toolchain/parse/state.def +++ b/toolchain/parse/state.def @@ -730,28 +730,28 @@ CARBON_PARSE_STATE_VARIANTS2(PatternListFinish, Implicit, Tuple) // Handles the processing of a `(condition)` up through the expression. // -// if/while { (invalid) -// ^ -// 1. ParenConditionAs(If|While)Finish +// if/while/match { (invalid) +// ^ +// 1. ParenConditionAs(If|While|Match)Finish // -// if/while ( ... ) -// ^ -// if/while ??? -// ^ +// if/while/match ( ... ) +// ^ +// if/while/match ??? +// ^ // 1. Expr -// 2. ParenConditionAs(If|While)Finish -CARBON_PARSE_STATE_VARIANTS2(ParenCondition, If, While) +// 2. ParenConditionAs(If|While|Match)Finish +CARBON_PARSE_STATE_VARIANTS3(ParenCondition, If, While, Match) // Finishes the processing of a `(condition)` after the expression. // -// if/while ( expr ) -// ^ -// if/while { -// ^ -// if/while ??? { -// ^ +// if/while/match ( expr ) +// ^ +// if/while/match { +// ^ +// if/while/match ??? { +// ^ // (state done) -CARBON_PARSE_STATE_VARIANTS2(ParenConditionFinish, If, While) +CARBON_PARSE_STATE_VARIANTS3(ParenConditionFinish, If, While, Match) // Handles the `(` of a parenthesized single expression // @@ -922,6 +922,10 @@ CARBON_PARSE_STATE_VARIANTS2(BindingPatternFinish, Generic, Regular) // ^ // 1. Expr // 2. ExprStatementFinish +// +// match ... +// ^ +// 1. MatchIntroducer CARBON_PARSE_STATE(Statement) // Handles `break` processing at the `;`. @@ -1304,4 +1308,131 @@ CARBON_PARSE_STATE(ChoiceAlternativeFinish) // (state done) CARBON_PARSE_STATE(ChoiceDefinitionFinish) +// Handles `match` introducer. +// +// match ... +// ^~~~~ +// 1. ParenConditionAsMatch +// 2. MatchConditionFinish +CARBON_PARSE_STATE(MatchIntroducer) + +// Handles `match` cases block start after the condition. +// +// match (...) { ... } +// ^ +// 1. MatchCaseLoop +// 2. MatchStatementFinish +// +// match (...) ??? +// ^ +// (state done) +CARBON_PARSE_STATE(MatchConditionFinish) + +// Handles `match` cases. +// +// match (...) { case ...} +// ^ +// 1. MatchCaseIntroducer +// 2. MatchCaseLoop +// match (...) { default ...} +// ^ +// 1. MatchDefaultIntroducer +// 2. MatchCaseLoopAfterDefault +CARBON_PARSE_STATE(MatchCaseLoop) + +// Handles `match` `case` introducer. +// +// match (...) { case ...} +// ^~~~ +// 1. Pattern +// 2. MatchCaseAfterPattern +CARBON_PARSE_STATE(MatchCaseIntroducer) + +// Handles `match` case after pattern. +// +// match (...) { case ... => ... } +// ^ +// 1. MatchCaseStart +// match (...) { case ... if (...) } +// ^~~~ +// 1. Expr +// 2. MatchCaseGuardFinish +// 3. MatchCaseStart +CARBON_PARSE_STATE(MatchCaseAfterPattern) + +// Handles `match` case guard closing parenthesis. +// +// match (...) { case ... if (...) => ... } +// ^ +// (state done) +CARBON_PARSE_STATE(MatchCaseGuardFinish) + +// Handles `match` case `=>` and `{` opening statements block. +// +// match (...) { case ... => {...} } +// ^~~~ +// 1. StatementScopeLoop +// 2. MatchCaseFinish +// +// match (...) { case ... ??? } +// ^ +// (state done) +// +// match (...) { case ... => ??? } +// ^ +// (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 +// +// match (...) { default ??? } +// ^ +// (state done) +// +// match (...) { default => ??? } +// ^ +// (state done) +CARBON_PARSE_STATE(MatchDefaultIntroducer) + +// Handles `match` default case statements block closing `}`. +// +// match (...) { default => {...} } +// ^ +// (state done) +CARBON_PARSE_STATE(MatchDefaultFinish) + +// Handles `match` cases after the `default` case. +// +// match (...) { default => {...} case ... } +// ^~~~ +// 1. MatchCaseLoopAfterDefault +// +// match (...) { default => {...} default ... } +// ^~~~~~~ +// 1. MatchCaseLoopAfterDefault +// +// match (...) { default => {...} } +// ^ +// (state done) +CARBON_PARSE_STATE(MatchCaseLoopAfterDefault) + +// Finishes `match` statement. +// +// match (...) {...} +// ^ +// (state done) +CARBON_PARSE_STATE(MatchStatementFinish) + #undef CARBON_PARSE_STATE diff --git a/toolchain/parse/testdata/match/fail_cases_after_default.carbon b/toolchain/parse/testdata/match/fail_cases_after_default.carbon new file mode 100644 index 000000000000..be4467c9c214 --- /dev/null +++ b/toolchain/parse/testdata/match/fail_cases_after_default.carbon @@ -0,0 +1,62 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn f() -> i32 { + var x: i32 = 3; + match (x) { + default => { return 1; } + // CHECK:STDERR: fail_cases_after_default.carbon:[[@LINE+3]]:5: ERROR: Unreachable case; `case` occurs after the `default` + // CHECK:STDERR: case a: i32 => { return 2; } + // CHECK:STDERR: ^~~~ + case a: i32 => { return 2; } + // CHECK:STDERR: fail_cases_after_default.carbon:[[@LINE+3]]:5: ERROR: Unreachable case; `default` occurs after the `default` + // CHECK:STDERR: default => { return 3; } + // CHECK:STDERR: ^~~~~~~ + default => { return 3; } + // CHECK:STDERR: fail_cases_after_default.carbon:[[@LINE+3]]:5: ERROR: Unreachable case; `case` occurs after the `default` + // CHECK:STDERR: case (a: i32) if (a == 4) => { return 4; } + // CHECK:STDERR: ^~~~ + case (a: i32) if (a == 4) => { return 4; } + } + return 0; +} + +// CHECK:STDOUT: - filename: fail_cases_after_default.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'f'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'x'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'IntLiteral', text: '3'}, +// CHECK:STDOUT: {kind: 'VariableInitializer', text: '=', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'MatchIntroducer', text: 'match'}, +// CHECK:STDOUT: {kind: 'MatchConditionStart', text: '('}, +// 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: '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: 'MatchStatement', text: '}', has_error: yes, 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: 31}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/match/fail_missing_case_arrow.carbon b/toolchain/parse/testdata/match/fail_missing_case_arrow.carbon new file mode 100644 index 000000000000..815aecfb92a2 --- /dev/null +++ b/toolchain/parse/testdata/match/fail_missing_case_arrow.carbon @@ -0,0 +1,62 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn f() -> i32 { + match (3) { + // CHECK:STDERR: fail_missing_case_arrow.carbon:[[@LINE+3]]:17: ERROR: Expected `=>` introducing statement block. + // CHECK:STDERR: case x: i32 { return 2; } + // CHECK:STDERR: ^ + case x: i32 { return 2; } + // CHECK:STDERR: fail_missing_case_arrow.carbon:[[@LINE+3]]:29: ERROR: Expected `=>` introducing statement block. + // CHECK:STDERR: case x: i32 if (x == 3) { return 3; } + // CHECK:STDERR: ^ + case x: i32 if (x == 3) { return 3; } + } + return 0; +} + +// CHECK:STDOUT: - filename: fail_missing_case_arrow.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'f'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'MatchIntroducer', text: 'match'}, +// CHECK:STDOUT: {kind: 'MatchConditionStart', text: '('}, +// 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: 'IdentifierName', text: 'x'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'BindingPattern', 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: 'IdentifierName', text: 'x'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'BindingPattern', 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: 'MatchStatement', text: '}', subtree_size: 26}, +// 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: 37}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/match/fail_missing_case_pattern.carbon b/toolchain/parse/testdata/match/fail_missing_case_pattern.carbon new file mode 100644 index 000000000000..017d04e13103 --- /dev/null +++ b/toolchain/parse/testdata/match/fail_missing_case_pattern.carbon @@ -0,0 +1,44 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn f() -> i32 { + match (3) { + // CHECK:STDERR: fail_missing_case_pattern.carbon:[[@LINE+3]]:10: ERROR: Expected binding pattern. + // CHECK:STDERR: case => { return 2; } + // CHECK:STDERR: ^~ + case => { return 2; } + } + return 0; +} + +// CHECK:STDOUT: - filename: fail_missing_case_pattern.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'f'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'MatchIntroducer', text: 'match'}, +// CHECK:STDOUT: {kind: 'MatchConditionStart', text: '('}, +// 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: 'IdentifierName', text: '=>', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '=>', has_error: yes}, +// CHECK:STDOUT: {kind: 'BindingPattern', 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: '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: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/match/fail_missing_case_statements_block.carbon b/toolchain/parse/testdata/match/fail_missing_case_statements_block.carbon new file mode 100644 index 000000000000..7c39d3f8a693 --- /dev/null +++ b/toolchain/parse/testdata/match/fail_missing_case_statements_block.carbon @@ -0,0 +1,46 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn f() -> i32 { + match (3) { + case x: i32 => + // CHECK:STDERR: fail_missing_case_statements_block.carbon:[[@LINE+3]]:5: ERROR: Expected `{` after `=>`. + // CHECK:STDERR: default => { return 3; } + // CHECK:STDERR: ^~~~~~~ + default => { return 3; } + } + return 0; +} + +// CHECK:STDOUT: - filename: fail_missing_case_statements_block.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'f'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'MatchIntroducer', text: 'match'}, +// CHECK:STDOUT: {kind: 'MatchConditionStart', text: '('}, +// 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: 'IdentifierName', text: 'x'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'BindingPattern', 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: '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: 24}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/match/fail_missing_cases.carbon b/toolchain/parse/testdata/match/fail_missing_cases.carbon new file mode 100644 index 000000000000..77172a4b7e2d --- /dev/null +++ b/toolchain/parse/testdata/match/fail_missing_cases.carbon @@ -0,0 +1,37 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + + +fn f() -> i32 { + // CHECK:STDERR: fail_missing_cases.carbon:[[@LINE+3]]:14: ERROR: Expected cases. + // CHECK:STDERR: match (3) {} + // CHECK:STDERR: ^ + match (3) {} + return 0; +} + +// CHECK:STDOUT: - filename: fail_missing_cases.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'f'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'MatchIntroducer', text: 'match'}, +// CHECK:STDOUT: {kind: 'MatchConditionStart', text: '('}, +// 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: 'MatchStatement', text: '}', has_error: yes, subtree_size: 6}, +// 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: 17}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/match/fail_missing_cases_block.carbon b/toolchain/parse/testdata/match/fail_missing_cases_block.carbon new file mode 100644 index 000000000000..08644819cb9d --- /dev/null +++ b/toolchain/parse/testdata/match/fail_missing_cases_block.carbon @@ -0,0 +1,33 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn f() -> i32 { + match (1) + // CHECK:STDERR: fail_missing_cases_block.carbon:[[@LINE+3]]:3: ERROR: Expected `{` starting block with cases. + // CHECK:STDERR: return 0; + // CHECK:STDERR: ^~~~~~ + return 0; +} + +// CHECK:STDOUT: - filename: fail_missing_cases_block.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'f'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'MatchIntroducer', text: 'match'}, +// CHECK:STDOUT: {kind: 'MatchConditionStart', text: '('}, +// CHECK:STDOUT: {kind: 'IntLiteral', text: '1'}, +// CHECK:STDOUT: {kind: 'MatchCondition', text: ')', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'MatchStatementStart', text: 'return', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'MatchStatement', text: 'return', has_error: yes, subtree_size: 6}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 14}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/match/fail_missing_default_arrow.carbon b/toolchain/parse/testdata/match/fail_missing_default_arrow.carbon new file mode 100644 index 000000000000..16ba3a599b6d --- /dev/null +++ b/toolchain/parse/testdata/match/fail_missing_default_arrow.carbon @@ -0,0 +1,42 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn f() -> i32 { + match (3) { + // CHECK:STDERR: fail_missing_default_arrow.carbon:[[@LINE+3]]:13: ERROR: Expected `=>` introducing statement block. + // CHECK:STDERR: default { return 1; } + // CHECK:STDERR: ^ + default { return 1; } + } + return 0; +} + +// CHECK:STDOUT: - filename: fail_missing_default_arrow.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'f'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'MatchIntroducer', text: 'match'}, +// CHECK:STDOUT: {kind: 'MatchConditionStart', text: '('}, +// 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: 'MatchStatement', text: '}', subtree_size: 10}, +// 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: 21}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/match/fail_missing_default_statements_block.carbon b/toolchain/parse/testdata/match/fail_missing_default_statements_block.carbon new file mode 100644 index 000000000000..b5aefcb22277 --- /dev/null +++ b/toolchain/parse/testdata/match/fail_missing_default_statements_block.carbon @@ -0,0 +1,42 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn f() -> i32 { + match (3) { + default => + // CHECK:STDERR: fail_missing_default_statements_block.carbon:[[@LINE+3]]:3: ERROR: Expected `{` after `=>`. + // CHECK:STDERR: } + // CHECK:STDERR: ^ + } + return 0; +} + +// CHECK:STDOUT: - filename: fail_missing_default_statements_block.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'f'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'MatchIntroducer', text: 'match'}, +// CHECK:STDOUT: {kind: 'MatchConditionStart', text: '('}, +// 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: 'MatchStatement', text: '}', subtree_size: 10}, +// 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: 21}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/match/fail_missing_guard_close_paren.carbon b/toolchain/parse/testdata/match/fail_missing_guard_close_paren.carbon new file mode 100644 index 000000000000..ff30ead5f8f2 --- /dev/null +++ b/toolchain/parse/testdata/match/fail_missing_guard_close_paren.carbon @@ -0,0 +1,49 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn f() -> i32 { + match (true) { + // CHECK:STDERR: fail_missing_guard_close_paren.carbon:[[@LINE+3]]:21: ERROR: Opening symbol without a corresponding closing symbol. + // CHECK:STDERR: case x: bool if (false => { return 1; } + // CHECK:STDERR: ^ + case x: bool if (false => { return 1; } + } + return 0; +} + +// CHECK:STDOUT: - filename: fail_missing_guard_close_paren.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'f'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'MatchIntroducer', text: 'match'}, +// CHECK:STDOUT: {kind: 'MatchConditionStart', text: '('}, +// 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: 'IdentifierName', text: 'x'}, +// CHECK:STDOUT: {kind: 'BoolTypeLiteral', text: 'bool'}, +// CHECK:STDOUT: {kind: 'BindingPattern', 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: '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: 28}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/match/fail_missing_guard_open_paren.carbon b/toolchain/parse/testdata/match/fail_missing_guard_open_paren.carbon new file mode 100644 index 000000000000..45311657b0bc --- /dev/null +++ b/toolchain/parse/testdata/match/fail_missing_guard_open_paren.carbon @@ -0,0 +1,48 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn f() -> i32 { + match (true) { + // CHECK:STDERR: fail_missing_guard_open_paren.carbon:[[@LINE+3]]:26: ERROR: Closing symbol without a corresponding opening symbol. + // CHECK:STDERR: case x: bool if false) => { return 1; } + // CHECK:STDERR: ^ + case x: bool if false) => { return 1; } + } + return 0; +} + +// CHECK:STDOUT: - filename: fail_missing_guard_open_paren.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'f'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'MatchIntroducer', text: 'match'}, +// CHECK:STDOUT: {kind: 'MatchConditionStart', text: '('}, +// 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: 'IdentifierName', text: 'x'}, +// CHECK:STDOUT: {kind: 'BoolTypeLiteral', text: 'bool'}, +// CHECK:STDOUT: {kind: 'BindingPattern', 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: '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: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/match/fail_missing_matched_expr.carbon b/toolchain/parse/testdata/match/fail_missing_matched_expr.carbon new file mode 100644 index 000000000000..a23de8ee5555 --- /dev/null +++ b/toolchain/parse/testdata/match/fail_missing_matched_expr.carbon @@ -0,0 +1,45 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn f() -> i32 { + // CHECK:STDERR: fail_missing_matched_expr.carbon:[[@LINE+3]]:9: ERROR: Expected `(` after `match`. + // CHECK:STDERR: match { + // CHECK:STDERR: ^ + match { + default => { return 1; } + } + return 0; +} + +// CHECK:STDOUT: - filename: fail_missing_matched_expr.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'f'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'MatchIntroducer', text: 'match'}, +// CHECK:STDOUT: {kind: 'MatchConditionStart', text: 'match', has_error: yes}, +// 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: '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: '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: 24}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/match/fail_unexpected_tokens_in_cases_block.carbon b/toolchain/parse/testdata/match/fail_unexpected_tokens_in_cases_block.carbon new file mode 100644 index 000000000000..8c9c53398acd --- /dev/null +++ b/toolchain/parse/testdata/match/fail_unexpected_tokens_in_cases_block.carbon @@ -0,0 +1,112 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn f() -> i32 { + var x: i32 = 3; + match (x) { + // CHECK:STDERR: fail_unexpected_tokens_in_cases_block.carbon:[[@LINE+3]]:5: ERROR: Unexpected `let`; expected `case`, `default` or `}`. + // CHECK:STDERR: let u: i32 = + // CHECK:STDERR: ^~~ + let u: i32 = + case y: i32 if (y > 2) => { return 1; } + // CHECK:STDERR: fail_unexpected_tokens_in_cases_block.carbon:[[@LINE+3]]:5: ERROR: Unexpected `if`; expected `case`, `default` or `}`. + // CHECK:STDERR: if (true) { + // CHECK:STDERR: ^~ + if (true) { + return 2; + } + // CHECK:STDERR: fail_unexpected_tokens_in_cases_block.carbon:[[@LINE+3]]:5: ERROR: Unexpected `var`; expected `case`, `default` or `}`. + // CHECK:STDERR: var w + // CHECK:STDERR: ^~~ + var w + case z: i32 if (z == 3) => { return 3; } + // CHECK:STDERR: fail_unexpected_tokens_in_cases_block.carbon:[[@LINE+3]]:5: ERROR: Unexpected `{`; expected `case`, `default` or `}`. + // CHECK:STDERR: { + // CHECK:STDERR: ^ + { + break; + } + default => { return 3; } + // CHECK:STDERR: fail_unexpected_tokens_in_cases_block.carbon:[[@LINE+3]]:5: ERROR: Unexpected `return`; expected `case`, `default` or `}`. + // CHECK:STDERR: return 1; + // CHECK:STDERR: ^~~~~~ + return 1; + // CHECK:STDERR: fail_unexpected_tokens_in_cases_block.carbon:[[@LINE+3]]:5: ERROR: Unexpected `(`; expected `case`, `default` or `}`. + // CHECK:STDERR: ((), (), ()) + // CHECK:STDERR: ^ + ((), (), ()) + } + return 0; +} + +// CHECK:STDOUT: - filename: fail_unexpected_tokens_in_cases_block.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'f'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'x'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'IntLiteral', text: '3'}, +// CHECK:STDOUT: {kind: 'VariableInitializer', text: '=', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'MatchIntroducer', text: 'match'}, +// CHECK:STDOUT: {kind: 'MatchConditionStart', text: '('}, +// 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: 'IdentifierName', text: 'y'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'BindingPattern', 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: '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: 'IdentifierName', text: 'z'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'BindingPattern', 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: '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: '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: 'MatchStatement', text: '}', has_error: yes, subtree_size: 45}, +// 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: 63}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/match/match.carbon b/toolchain/parse/testdata/match/match.carbon new file mode 100644 index 000000000000..c5f555bed24b --- /dev/null +++ b/toolchain/parse/testdata/match/match.carbon @@ -0,0 +1,106 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn f() -> i32 { + var x: i32 = 3; + match (f(x)) { + case (a: i32, b: i32) => { return 0; } + case (a: i32) if (a < 0) => { return 2; } + case a: i32 if (a != x) => { return 3; } + default => { return 4; } + } + return 0; +} + +// CHECK:STDOUT: - filename: match.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'f'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'x'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'IntLiteral', text: '3'}, +// CHECK:STDOUT: {kind: 'VariableInitializer', text: '=', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'MatchIntroducer', text: 'match'}, +// CHECK:STDOUT: {kind: 'MatchConditionStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'f'}, +// CHECK:STDOUT: {kind: 'CallExprStart', text: '(', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'}, +// 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: 'IdentifierName', text: 'a'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'PatternListComma', text: ','}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'b'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'BindingPattern', 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: '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: 'IdentifierName', text: 'a'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'BindingPattern', 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: '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: 'IdentifierName', text: 'a'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'BindingPattern', 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: '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: '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: 'MatchStatement', text: '}', subtree_size: 66}, +// 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: 84}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/typed_nodes.h b/toolchain/parse/typed_nodes.h index 429a8544b23e..d5313dc462f6 100644 --- a/toolchain/parse/typed_nodes.h +++ b/toolchain/parse/typed_nodes.h @@ -5,6 +5,8 @@ #ifndef CARBON_TOOLCHAIN_PARSE_TYPED_NODES_H_ #define CARBON_TOOLCHAIN_PARSE_TYPED_NODES_H_ +#include + #include "toolchain/parse/node_ids.h" #include "toolchain/parse/node_kind.h" @@ -482,6 +484,77 @@ struct WhileStatement { CodeBlockId body; }; +using MatchConditionStart = LeafNode; + +struct MatchCondition { + static constexpr auto Kind = NodeKind::MatchCondition.Define(); + + MatchConditionStartId left_paren; + AnyExprId condition; +}; + +using MatchIntroducer = LeafNode; +struct MatchStatementStart { + static constexpr auto Kind = NodeKind::MatchStatementStart.Define(); + + MatchIntroducerId introducer; + MatchConditionId left_brace; +}; + +using MatchCaseIntroducer = LeafNode; +using MatchCaseGuardIntroducer = LeafNode; +using MatchCaseGuardStart = LeafNode; + +struct MatchCaseGuard { + static constexpr auto Kind = NodeKind::MatchCaseGuard.Define(); + MatchCaseGuardIntroducerId introducer; + MatchCaseGuardStartId left_paren; + AnyExprId condition; +}; + +using MatchCaseEqualGreater = LeafNode; + +struct MatchCaseStart { + static constexpr auto Kind = NodeKind::MatchCaseStart.Define(); + MatchCaseIntroducerId introducer; + AnyPatternId pattern; + std::optional guard; + MatchCaseEqualGreaterId equal_greater_token; +}; + +struct MatchCase { + static constexpr auto Kind = NodeKind::MatchCase.Define(); + MatchCaseStartId head; + llvm::SmallVector statements; +}; + +using MatchDefaultIntroducer = LeafNode; +using MatchDefaultEqualGreater = LeafNode; + +struct MatchDefaultStart { + static constexpr auto Kind = NodeKind::MatchDefaultStart.Define(); + MatchDefaultIntroducerId introducer; + MatchDefaultEqualGreaterId equal_greater_token; +}; + +struct MatchDefault { + static constexpr auto Kind = NodeKind::MatchDefault.Define(); + + MatchDefaultStartId introducer; + llvm::SmallVector statements; +}; + +// A `match` statement: `match (expr) { case (...) => {...} default => {...}}`. +struct MatchStatement { + static constexpr auto Kind = + NodeKind::MatchStatement.Define(NodeCategory::Statement); + + MatchStatementStartId head; + + llvm::SmallVector cases; + std::optional default_case; +}; + // Expression nodes // ----------------