diff options
| author | Matthias Krüger <476013+matthiaskrgr@users.noreply.github.com> | 2025-10-02 10:27:50 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-10-02 10:27:50 +0200 |
| commit | e041b2c07830a561d3224338aec4c9e520791e67 (patch) | |
| tree | fbf2c0da10896023abd348152ceb0ec2fc51fee2 /compiler | |
| parent | 7320d3ed6104280b4af5f99695544b861c0a2b86 (diff) | |
| parent | bb48c162b62ce20c5f51b6a435ca2ba7bb918d7e (diff) | |
| download | rust-e041b2c07830a561d3224338aec4c9e520791e67.tar.gz rust-e041b2c07830a561d3224338aec4c9e520791e67.zip | |
Rollup merge of #147004 - estebank:ascription-in-pat, r=fee1-dead
Tweak handling of "struct like start" where a struct isn't supported
This improves the case where someone tries to write a `match` expr where the patterns have type ascription syntax. Makes them less verbose, by giving up on the first encounter in the block, and makes them more accurate by only treating them as a struct literal if successfully parsed as such.
Before, encountering something like `match a { b:` would confuse the parser and think everything after `match` *must* be a struct, and if it wasn't it would generate a cascade of unnecessary diagnostics.
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_parse/src/parser/diagnostics.rs | 40 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 58 |
2 files changed, 58 insertions, 40 deletions
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index a28af7833c3..a9fbd0fa33d 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2748,28 +2748,7 @@ impl<'a> Parser<'a> { if token::Colon != self.token.kind { return first_pat; } - if !matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..)) - || !self.look_ahead(1, |token| token.is_non_reserved_ident()) - { - let mut snapshot_type = self.create_snapshot_for_diagnostic(); - snapshot_type.bump(); // `:` - match snapshot_type.parse_ty() { - Err(inner_err) => { - inner_err.cancel(); - } - Ok(ty) => { - let Err(mut err) = self.expected_one_of_not_found(&[], &[]) else { - return first_pat; - }; - err.span_label(ty.span, "specifying the type of a pattern isn't supported"); - self.restore_snapshot(snapshot_type); - let span = first_pat.span.to(ty.span); - first_pat = self.mk_pat(span, PatKind::Wild); - err.emit(); - } - } - return first_pat; - } + // The pattern looks like it might be a path with a `::` -> `:` typo: // `match foo { bar:baz => {} }` let colon_span = self.token.span; @@ -2857,7 +2836,13 @@ impl<'a> Parser<'a> { Applicability::MaybeIncorrect, ); } else { - first_pat = self.mk_pat(new_span, PatKind::Wild); + first_pat = self.mk_pat( + new_span, + PatKind::Err( + self.dcx() + .span_delayed_bug(colon_span, "recovered bad path pattern"), + ), + ); } self.restore_snapshot(snapshot_pat); } @@ -2870,7 +2855,14 @@ impl<'a> Parser<'a> { err.span_label(ty.span, "specifying the type of a pattern isn't supported"); self.restore_snapshot(snapshot_type); let new_span = first_pat.span.to(ty.span); - first_pat = self.mk_pat(new_span, PatKind::Wild); + first_pat = + self.mk_pat( + new_span, + PatKind::Err(self.dcx().span_delayed_bug( + colon_span, + "recovered bad pattern with type", + )), + ); } } err.emit(); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 81a5d48d94e..8046abcd70b 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3612,7 +3612,7 @@ impl<'a> Parser<'a> { self.token.is_keyword(kw::Async) && self.is_gen_block(kw::Gen, 1) } - fn is_certainly_not_a_block(&self) -> bool { + fn is_likely_struct_lit(&self) -> bool { // `{ ident, ` and `{ ident: ` cannot start a block. self.look_ahead(1, |t| t.is_ident()) && self.look_ahead(2, |t| t == &token::Comma || t == &token::Colon) @@ -3624,24 +3624,50 @@ impl<'a> Parser<'a> { path: &ast::Path, ) -> Option<PResult<'a, Box<Expr>>> { let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL); - if struct_allowed || self.is_certainly_not_a_block() { - if let Err(err) = self.expect(exp!(OpenBrace)) { - return Some(Err(err)); + match (struct_allowed, self.is_likely_struct_lit()) { + // A struct literal isn't expected and one is pretty much assured not to be present. The + // only situation that isn't detected is when a struct with a single field was attempted + // in a place where a struct literal wasn't expected, but regular parser errors apply. + // Happy path. + (false, false) => None, + (true, _) => { + // A struct is accepted here, try to parse it and rely on `parse_expr_struct` for + // any kind of recovery. Happy path. + if let Err(err) = self.expect(exp!(OpenBrace)) { + return Some(Err(err)); + } + Some(self.parse_expr_struct(qself.clone(), path.clone(), true)) } - let expr = self.parse_expr_struct(qself.clone(), path.clone(), true); - if let (Ok(expr), false) = (&expr, struct_allowed) { - // This is a struct literal, but we don't can't accept them here. - self.dcx().emit_err(errors::StructLiteralNotAllowedHere { - span: expr.span, - sub: errors::StructLiteralNotAllowedHereSugg { - left: path.span.shrink_to_lo(), - right: expr.span.shrink_to_hi(), - }, - }); + (false, true) => { + // We have something like `match foo { bar,` or `match foo { bar:`, which means the + // user might have meant to write a struct literal as part of the `match` + // discriminant. This is done purely for error recovery. + let snapshot = self.create_snapshot_for_diagnostic(); + if let Err(err) = self.expect(exp!(OpenBrace)) { + return Some(Err(err)); + } + match self.parse_expr_struct(qself.clone(), path.clone(), false) { + Ok(expr) => { + // This is a struct literal, but we don't accept them here. + self.dcx().emit_err(errors::StructLiteralNotAllowedHere { + span: expr.span, + sub: errors::StructLiteralNotAllowedHereSugg { + left: path.span.shrink_to_lo(), + right: expr.span.shrink_to_hi(), + }, + }); + Some(Ok(expr)) + } + Err(err) => { + // We couldn't parse a valid struct, rollback and let the parser emit an + // error elsewhere. + err.cancel(); + self.restore_snapshot(snapshot); + None + } + } } - return Some(expr); } - None } pub(super) fn parse_struct_fields( |
