Skip to content

Commit 90c36f5

Browse files
authored
Unrolled build for #147004
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.
2 parents 4b9c62b + bb48c16 commit 90c36f5

File tree

6 files changed

+95
-123
lines changed

6 files changed

+95
-123
lines changed

compiler/rustc_parse/src/parser/diagnostics.rs

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2748,28 +2748,7 @@ impl<'a> Parser<'a> {
27482748
if token::Colon != self.token.kind {
27492749
return first_pat;
27502750
}
2751-
if !matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..))
2752-
|| !self.look_ahead(1, |token| token.is_non_reserved_ident())
2753-
{
2754-
let mut snapshot_type = self.create_snapshot_for_diagnostic();
2755-
snapshot_type.bump(); // `:`
2756-
match snapshot_type.parse_ty() {
2757-
Err(inner_err) => {
2758-
inner_err.cancel();
2759-
}
2760-
Ok(ty) => {
2761-
let Err(mut err) = self.expected_one_of_not_found(&[], &[]) else {
2762-
return first_pat;
2763-
};
2764-
err.span_label(ty.span, "specifying the type of a pattern isn't supported");
2765-
self.restore_snapshot(snapshot_type);
2766-
let span = first_pat.span.to(ty.span);
2767-
first_pat = self.mk_pat(span, PatKind::Wild);
2768-
err.emit();
2769-
}
2770-
}
2771-
return first_pat;
2772-
}
2751+
27732752
// The pattern looks like it might be a path with a `::` -> `:` typo:
27742753
// `match foo { bar:baz => {} }`
27752754
let colon_span = self.token.span;
@@ -2857,7 +2836,13 @@ impl<'a> Parser<'a> {
28572836
Applicability::MaybeIncorrect,
28582837
);
28592838
} else {
2860-
first_pat = self.mk_pat(new_span, PatKind::Wild);
2839+
first_pat = self.mk_pat(
2840+
new_span,
2841+
PatKind::Err(
2842+
self.dcx()
2843+
.span_delayed_bug(colon_span, "recovered bad path pattern"),
2844+
),
2845+
);
28612846
}
28622847
self.restore_snapshot(snapshot_pat);
28632848
}
@@ -2870,7 +2855,14 @@ impl<'a> Parser<'a> {
28702855
err.span_label(ty.span, "specifying the type of a pattern isn't supported");
28712856
self.restore_snapshot(snapshot_type);
28722857
let new_span = first_pat.span.to(ty.span);
2873-
first_pat = self.mk_pat(new_span, PatKind::Wild);
2858+
first_pat =
2859+
self.mk_pat(
2860+
new_span,
2861+
PatKind::Err(self.dcx().span_delayed_bug(
2862+
colon_span,
2863+
"recovered bad pattern with type",
2864+
)),
2865+
);
28742866
}
28752867
}
28762868
err.emit();

compiler/rustc_parse/src/parser/expr.rs

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3612,7 +3612,7 @@ impl<'a> Parser<'a> {
36123612
self.token.is_keyword(kw::Async) && self.is_gen_block(kw::Gen, 1)
36133613
}
36143614

3615-
fn is_certainly_not_a_block(&self) -> bool {
3615+
fn is_likely_struct_lit(&self) -> bool {
36163616
// `{ ident, ` and `{ ident: ` cannot start a block.
36173617
self.look_ahead(1, |t| t.is_ident())
36183618
&& self.look_ahead(2, |t| t == &token::Comma || t == &token::Colon)
@@ -3624,24 +3624,50 @@ impl<'a> Parser<'a> {
36243624
path: &ast::Path,
36253625
) -> Option<PResult<'a, Box<Expr>>> {
36263626
let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL);
3627-
if struct_allowed || self.is_certainly_not_a_block() {
3628-
if let Err(err) = self.expect(exp!(OpenBrace)) {
3629-
return Some(Err(err));
3627+
match (struct_allowed, self.is_likely_struct_lit()) {
3628+
// A struct literal isn't expected and one is pretty much assured not to be present. The
3629+
// only situation that isn't detected is when a struct with a single field was attempted
3630+
// in a place where a struct literal wasn't expected, but regular parser errors apply.
3631+
// Happy path.
3632+
(false, false) => None,
3633+
(true, _) => {
3634+
// A struct is accepted here, try to parse it and rely on `parse_expr_struct` for
3635+
// any kind of recovery. Happy path.
3636+
if let Err(err) = self.expect(exp!(OpenBrace)) {
3637+
return Some(Err(err));
3638+
}
3639+
Some(self.parse_expr_struct(qself.clone(), path.clone(), true))
36303640
}
3631-
let expr = self.parse_expr_struct(qself.clone(), path.clone(), true);
3632-
if let (Ok(expr), false) = (&expr, struct_allowed) {
3633-
// This is a struct literal, but we don't can't accept them here.
3634-
self.dcx().emit_err(errors::StructLiteralNotAllowedHere {
3635-
span: expr.span,
3636-
sub: errors::StructLiteralNotAllowedHereSugg {
3637-
left: path.span.shrink_to_lo(),
3638-
right: expr.span.shrink_to_hi(),
3639-
},
3640-
});
3641+
(false, true) => {
3642+
// We have something like `match foo { bar,` or `match foo { bar:`, which means the
3643+
// user might have meant to write a struct literal as part of the `match`
3644+
// discriminant. This is done purely for error recovery.
3645+
let snapshot = self.create_snapshot_for_diagnostic();
3646+
if let Err(err) = self.expect(exp!(OpenBrace)) {
3647+
return Some(Err(err));
3648+
}
3649+
match self.parse_expr_struct(qself.clone(), path.clone(), false) {
3650+
Ok(expr) => {
3651+
// This is a struct literal, but we don't accept them here.
3652+
self.dcx().emit_err(errors::StructLiteralNotAllowedHere {
3653+
span: expr.span,
3654+
sub: errors::StructLiteralNotAllowedHereSugg {
3655+
left: path.span.shrink_to_lo(),
3656+
right: expr.span.shrink_to_hi(),
3657+
},
3658+
});
3659+
Some(Ok(expr))
3660+
}
3661+
Err(err) => {
3662+
// We couldn't parse a valid struct, rollback and let the parser emit an
3663+
// error elsewhere.
3664+
err.cancel();
3665+
self.restore_snapshot(snapshot);
3666+
None
3667+
}
3668+
}
36413669
}
3642-
return Some(expr);
36433670
}
3644-
None
36453671
}
36463672

36473673
pub(super) fn parse_struct_fields(

tests/ui/parser/issues/issue-87086-colon-path-sep.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,9 @@ fn g1() {
3737
//~| HELP: maybe write a path separator here
3838
_ => {}
3939
}
40-
if let Foo:Bar = f() { //~ WARN: irrefutable `if let` pattern
40+
if let Foo:Bar = f() {
4141
//~^ ERROR: expected one of
4242
//~| HELP: maybe write a path separator here
43-
//~| HELP: consider replacing the `if let` with a `let`
4443
}
4544
}
4645

tests/ui/parser/issues/issue-87086-colon-path-sep.stderr

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ LL | if let Foo::Bar = f() {
6464
| +
6565

6666
error: expected one of `@` or `|`, found `:`
67-
--> $DIR/issue-87086-colon-path-sep.rs:49:16
67+
--> $DIR/issue-87086-colon-path-sep.rs:48:16
6868
|
6969
LL | ref qux: Foo::Baz => {}
7070
| ^ -------- specifying the type of a pattern isn't supported
@@ -77,7 +77,7 @@ LL | ref qux::Foo::Baz => {}
7777
| ~~
7878

7979
error: expected one of `@` or `|`, found `:`
80-
--> $DIR/issue-87086-colon-path-sep.rs:58:16
80+
--> $DIR/issue-87086-colon-path-sep.rs:57:16
8181
|
8282
LL | mut qux: Foo::Baz => {}
8383
| ^ -------- specifying the type of a pattern isn't supported
@@ -90,7 +90,7 @@ LL | mut qux::Foo::Baz => {}
9090
| ~~
9191

9292
error: expected one of `@` or `|`, found `:`
93-
--> $DIR/issue-87086-colon-path-sep.rs:69:12
93+
--> $DIR/issue-87086-colon-path-sep.rs:68:12
9494
|
9595
LL | Foo:Bar::Baz => {}
9696
| ^-------- specifying the type of a pattern isn't supported
@@ -103,7 +103,7 @@ LL | Foo::Bar::Baz => {}
103103
| +
104104

105105
error: expected one of `@` or `|`, found `:`
106-
--> $DIR/issue-87086-colon-path-sep.rs:75:12
106+
--> $DIR/issue-87086-colon-path-sep.rs:74:12
107107
|
108108
LL | Foo:Bar => {}
109109
| ^--- specifying the type of a pattern isn't supported
@@ -115,15 +115,5 @@ help: maybe write a path separator here
115115
LL | Foo::Bar => {}
116116
| +
117117

118-
warning: irrefutable `if let` pattern
119-
--> $DIR/issue-87086-colon-path-sep.rs:40:8
120-
|
121-
LL | if let Foo:Bar = f() {
122-
| ^^^^^^^^^^^^^^^^^
123-
|
124-
= note: this pattern will always match, so the `if let` is useless
125-
= help: consider replacing the `if let` with a `let`
126-
= note: `#[warn(irrefutable_let_patterns)]` on by default
127-
128-
error: aborting due to 9 previous errors; 1 warning emitted
118+
error: aborting due to 9 previous errors
129119

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
fn foo(x: bool) -> i32 {
2-
match x { //~ ERROR struct literals are not allowed here
3-
x: i32 => x, //~ ERROR expected
4-
true => 42., //~ ERROR expected identifier
5-
false => 0.333, //~ ERROR expected identifier
2+
match x {
3+
x: i32 => x, //~ ERROR: expected
4+
//~^ ERROR: mismatched types
5+
true => 42.,
6+
false => 0.333,
67
}
7-
} //~ ERROR expected one of
8+
}
89

910
fn main() {
1011
match foo(true) {
11-
42: i32 => (), //~ ERROR expected
12-
_: f64 => (), //~ ERROR expected
13-
x: i32 => (), //~ ERROR expected
12+
42: i32 => (), //~ ERROR: expected
13+
_: f64 => (), //~ ERROR: expected
14+
x: i32 => (), //~ ERROR: expected
1415
}
1516
}
Lines changed: 22 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,34 @@
1-
error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, `}`, or an operator, found `=>`
2-
--> $DIR/type-ascription-in-pattern.rs:3:16
3-
|
4-
LL | match x {
5-
| - while parsing this struct
6-
LL | x: i32 => x,
7-
| -^^ expected one of 8 possible tokens
8-
| |
9-
| help: try adding a comma: `,`
10-
11-
error: expected identifier, found keyword `true`
12-
--> $DIR/type-ascription-in-pattern.rs:4:9
13-
|
14-
LL | match x {
15-
| - while parsing this struct
16-
LL | x: i32 => x,
17-
LL | true => 42.,
18-
| ^^^^ expected identifier, found keyword
19-
20-
error: expected identifier, found keyword `false`
21-
--> $DIR/type-ascription-in-pattern.rs:5:9
22-
|
23-
LL | match x {
24-
| - while parsing this struct
25-
...
26-
LL | false => 0.333,
27-
| ^^^^^ expected identifier, found keyword
28-
29-
error: struct literals are not allowed here
30-
--> $DIR/type-ascription-in-pattern.rs:2:11
31-
|
32-
LL | match x {
33-
| ___________^
34-
LL | | x: i32 => x,
35-
LL | | true => 42.,
36-
LL | | false => 0.333,
37-
LL | | }
38-
| |_____^
39-
|
40-
help: surround the struct literal with parentheses
1+
error: expected one of `@` or `|`, found `:`
2+
--> $DIR/type-ascription-in-pattern.rs:3:10
413
|
42-
LL ~ match (x {
434
LL | x: i32 => x,
44-
LL | true => 42.,
45-
LL | false => 0.333,
46-
LL ~ })
5+
| ^ --- specifying the type of a pattern isn't supported
6+
| |
7+
| expected one of `@` or `|`
478
|
48-
49-
error: expected one of `.`, `?`, `{`, or an operator, found `}`
50-
--> $DIR/type-ascription-in-pattern.rs:7:1
9+
help: maybe write a path separator here
5110
|
52-
LL | match x {
53-
| ----- while parsing this `match` expression
54-
...
55-
LL | }
56-
| - expected one of `.`, `?`, `{`, or an operator
57-
LL | }
58-
| ^ unexpected token
11+
LL | x::i32 => x,
12+
| ~~
5913

6014
error: expected one of `...`, `..=`, `..`, or `|`, found `:`
61-
--> $DIR/type-ascription-in-pattern.rs:11:11
15+
--> $DIR/type-ascription-in-pattern.rs:12:11
6216
|
6317
LL | 42: i32 => (),
6418
| ^ --- specifying the type of a pattern isn't supported
6519
| |
6620
| expected one of `...`, `..=`, `..`, or `|`
6721

6822
error: expected `|`, found `:`
69-
--> $DIR/type-ascription-in-pattern.rs:12:10
23+
--> $DIR/type-ascription-in-pattern.rs:13:10
7024
|
7125
LL | _: f64 => (),
7226
| ^ --- specifying the type of a pattern isn't supported
7327
| |
7428
| expected `|`
7529

7630
error: expected one of `@` or `|`, found `:`
77-
--> $DIR/type-ascription-in-pattern.rs:13:10
31+
--> $DIR/type-ascription-in-pattern.rs:14:10
7832
|
7933
LL | x: i32 => (),
8034
| ^ --- specifying the type of a pattern isn't supported
@@ -86,5 +40,15 @@ help: maybe write a path separator here
8640
LL | x::i32 => (),
8741
| ~~
8842

89-
error: aborting due to 8 previous errors
43+
error[E0308]: mismatched types
44+
--> $DIR/type-ascription-in-pattern.rs:3:19
45+
|
46+
LL | fn foo(x: bool) -> i32 {
47+
| --- expected `i32` because of return type
48+
LL | match x {
49+
LL | x: i32 => x,
50+
| ^ expected `i32`, found `bool`
51+
52+
error: aborting due to 5 previous errors
9053

54+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)