From 085e45093e522da40fc174001fe39100afddf77d Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Mon, 20 Jul 2026 15:09:16 -0400 Subject: [PATCH] Avoid consuming a token after an invalid match_first opening (#7538) If the `match_first` is not followed by `{` avoid consuming whatever comes after it. Recover by leaving whatever comes next alone. It could even be the `FileEnd` token, and then we would crash when we read off the end of the token stream looking for `FileEnd`. --- toolchain/parse/handle_match_first.cpp | 7 ++++++- .../testdata/generics/impl/match_first.carbon | 19 ++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/toolchain/parse/handle_match_first.cpp b/toolchain/parse/handle_match_first.cpp index 7a9cfd470706..d7ca84fc3812 100644 --- a/toolchain/parse/handle_match_first.cpp +++ b/toolchain/parse/handle_match_first.cpp @@ -26,7 +26,12 @@ auto HandleMatchFirst(Context& context) -> void { auto HandleMatchFirstFinish(Context& context) -> void { auto state = context.PopState(); - context.AddNode(NodeKind::MatchFirst, context.Consume(), state.has_error); + if (auto closing_token = context.ConsumeIf(Lex::TokenKind::CloseCurlyBrace)) { + context.AddNode(NodeKind::MatchFirst, *closing_token, state.has_error); + } else { + // Recover from error by just moving on. + context.AddNode(NodeKind::MatchFirst, state.token, state.has_error); + } } } // namespace Carbon::Parse diff --git a/toolchain/parse/testdata/generics/impl/match_first.carbon b/toolchain/parse/testdata/generics/impl/match_first.carbon index 7c3135ef455c..125358a9c3d2 100644 --- a/toolchain/parse/testdata/generics/impl/match_first.carbon +++ b/toolchain/parse/testdata/generics/impl/match_first.carbon @@ -20,6 +20,14 @@ match_first {} // CHECK:STDERR: match_first; +// --- fail_eof.carbon + +match_first +// CHECK:STDERR: fail_eof.carbon:[[@LINE+4]]:1: error: expected `{` after `match_first` [ExpectedCurlyBraceAfter] +// CHECK:STDERR: +// CHECK:STDERR: ^ +// CHECK:STDERR: + // --- with_impls.carbon match_first { @@ -47,7 +55,16 @@ impl forall [T: J] T as Interface { // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'MatchFirstIntroducer', text: 'match_first'}, // CHECK:STDOUT: {kind: 'MatchFirstDefinitionStart', text: 'match_first', has_error: yes, subtree_size: 2}, -// CHECK:STDOUT: {kind: 'MatchFirst', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'MatchFirst', text: 'match_first', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'EmptyDecl', text: ';'}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_eof.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'MatchFirstIntroducer', text: 'match_first'}, +// CHECK:STDOUT: {kind: 'MatchFirstDefinitionStart', text: 'match_first', has_error: yes, subtree_size: 2}, +// CHECK:STDOUT: {kind: 'MatchFirst', text: 'match_first', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] // CHECK:STDOUT: - filename: with_impls.carbon