Skip to content

Commit 4305769

Browse files
committed
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 successfuly parsed as such.
1 parent 15283f6 commit 4305769

File tree

6 files changed

+93
-128
lines changed

6 files changed

+93
-128
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: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3612,36 +3612,55 @@ 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 {
3616-
// `{ ident, ` and `{ ident: ` cannot start a block.
3617-
self.look_ahead(1, |t| t.is_ident())
3618-
&& self.look_ahead(2, |t| t == &token::Comma || t == &token::Colon)
3619-
}
3620-
36213615
fn maybe_parse_struct_expr(
36223616
&mut self,
36233617
qself: &Option<Box<ast::QSelf>>,
36243618
path: &ast::Path,
36253619
) -> Option<PResult<'a, Box<Expr>>> {
36263620
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));
3621+
let is_ident = self.look_ahead(1, |t| t.is_ident());
3622+
let is_comma = self.look_ahead(2, |t| t == &token::Comma);
3623+
let is_colon = self.look_ahead(2, |t| t == &token::Colon);
3624+
match (struct_allowed, is_ident, is_comma, is_colon) {
3625+
(false, true, true, _) | (false, true, _, true) => {
3626+
// We have something like `match foo { bar,` or `match foo { bar:`, which means the
3627+
// user might have meant to write a struct literal as part of the `match`
3628+
// discriminant.
3629+
let snapshot = self.create_snapshot_for_diagnostic();
3630+
if let Err(err) = self.expect(exp!(OpenBrace)) {
3631+
return Some(Err(err));
3632+
}
3633+
match self.parse_expr_struct(qself.clone(), path.clone(), false) {
3634+
Ok(expr) => {
3635+
// This is a struct literal, but we don't accept them here.
3636+
self.dcx().emit_err(errors::StructLiteralNotAllowedHere {
3637+
span: expr.span,
3638+
sub: errors::StructLiteralNotAllowedHereSugg {
3639+
left: path.span.shrink_to_lo(),
3640+
right: expr.span.shrink_to_hi(),
3641+
},
3642+
});
3643+
Some(Ok(expr))
3644+
}
3645+
Err(err) => {
3646+
// We couldn't parse a valid struct, rollback and let the parser emit an
3647+
// error elsewhere.
3648+
err.cancel();
3649+
self.restore_snapshot(snapshot);
3650+
None
3651+
}
3652+
}
36303653
}
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-
});
3654+
(true, _, _, _) => {
3655+
// A struct is accepted here, try to parse it and rely on `parse_expr_struct` for
3656+
// any kind of recovery.
3657+
if let Err(err) = self.expect(exp!(OpenBrace)) {
3658+
return Some(Err(err));
3659+
}
3660+
Some(self.parse_expr_struct(qself.clone(), path.clone(), true))
36413661
}
3642-
return Some(expr);
3662+
(false, _, _, _) => None,
36433663
}
3644-
None
36453664
}
36463665

36473666
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)