Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 16 additions & 24 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand All @@ -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();
Expand Down
58 changes: 42 additions & 16 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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(
Expand Down
3 changes: 1 addition & 2 deletions tests/ui/parser/issues/issue-87086-colon-path-sep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,9 @@ fn g1() {
//~| HELP: maybe write a path separator here
_ => {}
}
if let Foo:Bar = f() { //~ WARN: irrefutable `if let` pattern
if let Foo:Bar = f() {
//~^ ERROR: expected one of
//~| HELP: maybe write a path separator here
//~| HELP: consider replacing the `if let` with a `let`
}
}

Expand Down
20 changes: 5 additions & 15 deletions tests/ui/parser/issues/issue-87086-colon-path-sep.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ LL | if let Foo::Bar = f() {
| +

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

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

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

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

warning: irrefutable `if let` pattern
--> $DIR/issue-87086-colon-path-sep.rs:40:8
|
LL | if let Foo:Bar = f() {
| ^^^^^^^^^^^^^^^^^
|
= note: this pattern will always match, so the `if let` is useless
= help: consider replacing the `if let` with a `let`
= note: `#[warn(irrefutable_let_patterns)]` on by default

error: aborting due to 9 previous errors; 1 warning emitted
error: aborting due to 9 previous errors

17 changes: 9 additions & 8 deletions tests/ui/parser/type-ascription-in-pattern.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
fn foo(x: bool) -> i32 {
match x { //~ ERROR struct literals are not allowed here
x: i32 => x, //~ ERROR expected
true => 42., //~ ERROR expected identifier
false => 0.333, //~ ERROR expected identifier
match x {
x: i32 => x, //~ ERROR: expected
//~^ ERROR: mismatched types
true => 42.,
false => 0.333,
}
} //~ ERROR expected one of
}

fn main() {
match foo(true) {
42: i32 => (), //~ ERROR expected
_: f64 => (), //~ ERROR expected
x: i32 => (), //~ ERROR expected
42: i32 => (), //~ ERROR: expected
_: f64 => (), //~ ERROR: expected
x: i32 => (), //~ ERROR: expected
}
}
80 changes: 22 additions & 58 deletions tests/ui/parser/type-ascription-in-pattern.stderr
Original file line number Diff line number Diff line change
@@ -1,80 +1,34 @@
error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, `}`, or an operator, found `=>`
--> $DIR/type-ascription-in-pattern.rs:3:16
|
LL | match x {
| - while parsing this struct
LL | x: i32 => x,
| -^^ expected one of 8 possible tokens
| |
| help: try adding a comma: `,`

error: expected identifier, found keyword `true`
--> $DIR/type-ascription-in-pattern.rs:4:9
|
LL | match x {
| - while parsing this struct
LL | x: i32 => x,
LL | true => 42.,
| ^^^^ expected identifier, found keyword

error: expected identifier, found keyword `false`
--> $DIR/type-ascription-in-pattern.rs:5:9
|
LL | match x {
| - while parsing this struct
...
LL | false => 0.333,
| ^^^^^ expected identifier, found keyword

error: struct literals are not allowed here
--> $DIR/type-ascription-in-pattern.rs:2:11
|
LL | match x {
| ___________^
LL | | x: i32 => x,
LL | | true => 42.,
LL | | false => 0.333,
LL | | }
| |_____^
|
help: surround the struct literal with parentheses
error: expected one of `@` or `|`, found `:`
--> $DIR/type-ascription-in-pattern.rs:3:10
|
LL ~ match (x {
LL | x: i32 => x,
LL | true => 42.,
LL | false => 0.333,
LL ~ })
| ^ --- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
|

error: expected one of `.`, `?`, `{`, or an operator, found `}`
--> $DIR/type-ascription-in-pattern.rs:7:1
help: maybe write a path separator here
|
LL | match x {
| ----- while parsing this `match` expression
...
LL | }
| - expected one of `.`, `?`, `{`, or an operator
LL | }
| ^ unexpected token
LL | x::i32 => x,
| ~~

error: expected one of `...`, `..=`, `..`, or `|`, found `:`
--> $DIR/type-ascription-in-pattern.rs:11:11
--> $DIR/type-ascription-in-pattern.rs:12:11
|
LL | 42: i32 => (),
| ^ --- specifying the type of a pattern isn't supported
| |
| expected one of `...`, `..=`, `..`, or `|`

error: expected `|`, found `:`
--> $DIR/type-ascription-in-pattern.rs:12:10
--> $DIR/type-ascription-in-pattern.rs:13:10
|
LL | _: f64 => (),
| ^ --- specifying the type of a pattern isn't supported
| |
| expected `|`

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

error: aborting due to 8 previous errors
error[E0308]: mismatched types
--> $DIR/type-ascription-in-pattern.rs:3:19
|
LL | fn foo(x: bool) -> i32 {
| --- expected `i32` because of return type
LL | match x {
LL | x: i32 => x,
| ^ expected `i32`, found `bool`

error: aborting due to 5 previous errors

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