Skip to content

Commit d6e34ad

Browse files
committed
When recovering from a : in a pattern, use adequate AST pattern
1 parent 862962b commit d6e34ad

File tree

3 files changed

+150
-44
lines changed

3 files changed

+150
-44
lines changed

compiler/rustc_parse/src/parser/pat.rs

+87-16
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@ use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
33
use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor};
44
use rustc_ast::ptr::P;
55
use rustc_ast::token;
6-
use rustc_ast::{self as ast, AttrVec, Attribute, MacCall, Pat, PatField, PatKind, RangeEnd};
7-
use rustc_ast::{BindingMode, Expr, ExprKind, Mutability, Path, QSelf, RangeSyntax};
6+
use rustc_ast::{
7+
self as ast, AttrVec, Attribute, BindingMode, Expr, ExprKind, MacCall, Mutability, Pat,
8+
PatField, PatKind, Path, PathSegment, QSelf, RangeEnd, RangeSyntax,
9+
};
810
use rustc_ast_pretty::pprust;
911
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, PResult};
1012
use rustc_span::source_map::{respan, Span, Spanned};
1113
use rustc_span::symbol::{kw, sym, Ident};
1214

15+
use std::mem::take;
16+
1317
type Expected = Option<&'static str>;
1418

1519
/// `Expected` for function and lambda parameter patterns.
@@ -101,11 +105,8 @@ impl<'a> Parser<'a> {
101105
let mut first_pat = first_pat;
102106

103107
if let (RecoverColon::Yes, token::Colon) = (ra, &self.token.kind) {
104-
if matches!(
105-
first_pat.kind,
106-
PatKind::Ident(BindingMode::ByValue(Mutability::Not), _, None)
107-
| PatKind::Path(..)
108-
) && self.look_ahead(1, |token| token.is_ident() && !token.is_reserved_ident())
108+
if matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..))
109+
&& self.look_ahead(1, |token| token.is_ident() && !token.is_reserved_ident())
109110
{
110111
// The pattern looks like it might be a path with a `::` -> `:` typo:
111112
// `match foo { bar:baz => {} }`
@@ -126,17 +127,87 @@ impl<'a> Parser<'a> {
126127
err.cancel();
127128
*self = snapshot;
128129
}
129-
Ok(pat) => {
130+
Ok(mut pat) => {
130131
// We've parsed the rest of the pattern.
131-
err.span_suggestion(
132-
span,
133-
"maybe write a path separator here",
134-
"::".to_string(),
135-
Applicability::MachineApplicable,
136-
);
132+
let new_span = first_pat.span.to(pat.span);
133+
let mut show_sugg = false;
134+
match &mut pat.kind {
135+
PatKind::Struct(qself @ None, path, ..)
136+
| PatKind::TupleStruct(qself @ None, path, _)
137+
| PatKind::Path(qself @ None, path) => {
138+
match &first_pat.kind {
139+
PatKind::Ident(_, ident, _) => {
140+
path.segments.insert(
141+
0,
142+
PathSegment::from_ident(ident.clone()),
143+
);
144+
path.span = new_span;
145+
show_sugg = true;
146+
first_pat = pat;
147+
}
148+
PatKind::Path(old_qself, old_path) => {
149+
path.segments = old_path
150+
.segments
151+
.iter()
152+
.cloned()
153+
.chain(take(&mut path.segments))
154+
.collect();
155+
path.span = new_span;
156+
*qself = old_qself.clone();
157+
first_pat = pat;
158+
show_sugg = true;
159+
}
160+
_ => {}
161+
}
162+
}
163+
PatKind::Ident(
164+
BindingMode::ByValue(Mutability::Not),
165+
ident,
166+
None,
167+
) => match &first_pat.kind {
168+
PatKind::Ident(_, old_ident, _) => {
169+
let path = PatKind::Path(
170+
None,
171+
Path {
172+
span: new_span,
173+
segments: vec![
174+
PathSegment::from_ident(
175+
old_ident.clone(),
176+
),
177+
PathSegment::from_ident(ident.clone()),
178+
],
179+
tokens: None,
180+
},
181+
);
182+
first_pat = self.mk_pat(new_span, path);
183+
show_sugg = true;
184+
}
185+
PatKind::Path(old_qself, old_path) => {
186+
let mut segments = old_path.segments.clone();
187+
segments
188+
.push(PathSegment::from_ident(ident.clone()));
189+
let path = PatKind::Path(
190+
old_qself.clone(),
191+
Path { span: new_span, segments, tokens: None },
192+
);
193+
first_pat = self.mk_pat(new_span, path);
194+
show_sugg = true;
195+
}
196+
_ => {}
197+
},
198+
_ => {}
199+
}
200+
if show_sugg {
201+
err.span_suggestion(
202+
span,
203+
"maybe write a path separator here",
204+
"::".to_string(),
205+
Applicability::MachineApplicable,
206+
);
207+
} else {
208+
first_pat = self.mk_pat(new_span, PatKind::Wild);
209+
}
137210
err.emit();
138-
first_pat =
139-
self.mk_pat(first_pat.span.to(pat.span), PatKind::Wild);
140211
}
141212
}
142213
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
// Tests that a suggestion is issued if the user wrote a colon instead of
22
// a path separator in a match arm.
33

4-
enum Foo {
5-
Bar,
6-
Baz,
4+
mod qux {
5+
pub enum Foo {
6+
Bar,
7+
Baz,
8+
}
79
}
810

11+
use qux::Foo;
12+
913
fn f() -> Foo { Foo::Bar }
1014

1115
fn g1() {
@@ -16,41 +20,43 @@ fn g1() {
1620
_ => {}
1721
}
1822
match f() {
19-
Foo::Bar:Baz => {}
23+
qux::Foo:Bar => {}
2024
//~^ ERROR: expected one of
2125
//~| HELP: maybe write a path separator here
2226
_ => {}
2327
}
2428
match f() {
25-
Foo:Bar::Baz => {}
29+
qux:Foo::Baz => {}
2630
//~^ ERROR: expected one of
2731
//~| HELP: maybe write a path separator here
2832
_ => {}
2933
}
3034
match f() {
31-
Foo: Bar::Baz if true => {}
35+
qux: Foo::Baz if true => {}
3236
//~^ ERROR: expected one of
3337
//~| HELP: maybe write a path separator here
3438
_ => {}
3539
}
36-
if let Bar:Baz = f() {
40+
if let Foo:Bar = f() {
3741
//~^ ERROR: expected one of
3842
//~| HELP: maybe write a path separator here
3943
}
4044
}
4145

4246
fn g1_neg() {
4347
match f() {
44-
ref Foo: Bar::Baz => {}
48+
ref qux: Foo::Baz => {}
4549
//~^ ERROR: expected one of
50+
//~| HELP: maybe write a path separator here
4651
_ => {}
4752
}
4853
}
4954

5055
fn g2_neg() {
5156
match f() {
52-
mut Foo: Bar::Baz => {}
57+
mut qux: Foo::Baz => {}
5358
//~^ ERROR: expected one of
59+
//~| HELP: maybe write a path separator here
5460
_ => {}
5561
}
5662
}
@@ -62,5 +68,12 @@ fn main() {
6268
Foo:Bar::Baz => {}
6369
//~^ ERROR: expected one of
6470
//~| HELP: maybe write a path separator here
71+
//~| ERROR: failed to resolve: `Bar` is a variant, not a module
72+
}
73+
match myfoo {
74+
Foo::Bar => {}
75+
Foo:Bar => {}
76+
//~^ ERROR: expected one of
77+
//~| HELP: maybe write a path separator here
6578
}
6679
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: expected one of `@` or `|`, found `:`
2-
--> $DIR/issue-87086-colon-path-sep.rs:13:12
2+
--> $DIR/issue-87086-colon-path-sep.rs:17:12
33
|
44
LL | Foo:Bar => {}
55
| ^
@@ -8,61 +8,83 @@ LL | Foo:Bar => {}
88
| help: maybe write a path separator here: `::`
99

1010
error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `{`, or `|`, found `:`
11-
--> $DIR/issue-87086-colon-path-sep.rs:19:17
11+
--> $DIR/issue-87086-colon-path-sep.rs:23:17
1212
|
13-
LL | Foo::Bar:Baz => {}
13+
LL | qux::Foo:Bar => {}
1414
| ^
1515
| |
1616
| expected one of 8 possible tokens
1717
| help: maybe write a path separator here: `::`
1818

1919
error: expected one of `@` or `|`, found `:`
20-
--> $DIR/issue-87086-colon-path-sep.rs:25:12
20+
--> $DIR/issue-87086-colon-path-sep.rs:29:12
2121
|
22-
LL | Foo:Bar::Baz => {}
22+
LL | qux:Foo::Baz => {}
2323
| ^
2424
| |
2525
| expected one of `@` or `|`
2626
| help: maybe write a path separator here: `::`
2727

2828
error: expected one of `@` or `|`, found `:`
29-
--> $DIR/issue-87086-colon-path-sep.rs:31:12
29+
--> $DIR/issue-87086-colon-path-sep.rs:35:12
3030
|
31-
LL | Foo: Bar::Baz if true => {}
31+
LL | qux: Foo::Baz if true => {}
3232
| ^
3333
| |
3434
| expected one of `@` or `|`
3535
| help: maybe write a path separator here: `::`
3636

3737
error: expected one of `@` or `|`, found `:`
38-
--> $DIR/issue-87086-colon-path-sep.rs:36:15
38+
--> $DIR/issue-87086-colon-path-sep.rs:40:15
3939
|
40-
LL | if let Bar:Baz = f() {
40+
LL | if let Foo:Bar = f() {
4141
| ^
4242
| |
4343
| expected one of `@` or `|`
4444
| help: maybe write a path separator here: `::`
4545

46-
error: expected one of `=>`, `@`, `if`, or `|`, found `:`
47-
--> $DIR/issue-87086-colon-path-sep.rs:44:16
46+
error: expected one of `@` or `|`, found `:`
47+
--> $DIR/issue-87086-colon-path-sep.rs:48:16
4848
|
49-
LL | ref Foo: Bar::Baz => {}
50-
| ^ expected one of `=>`, `@`, `if`, or `|`
49+
LL | ref qux: Foo::Baz => {}
50+
| ^
51+
| |
52+
| expected one of `@` or `|`
53+
| help: maybe write a path separator here: `::`
5154

52-
error: expected one of `=>`, `@`, `if`, or `|`, found `:`
53-
--> $DIR/issue-87086-colon-path-sep.rs:52:16
55+
error: expected one of `@` or `|`, found `:`
56+
--> $DIR/issue-87086-colon-path-sep.rs:57:16
5457
|
55-
LL | mut Foo: Bar::Baz => {}
56-
| ^ expected one of `=>`, `@`, `if`, or `|`
58+
LL | mut qux: Foo::Baz => {}
59+
| ^
60+
| |
61+
| expected one of `@` or `|`
62+
| help: maybe write a path separator here: `::`
5763

5864
error: expected one of `@` or `|`, found `:`
59-
--> $DIR/issue-87086-colon-path-sep.rs:62:12
65+
--> $DIR/issue-87086-colon-path-sep.rs:68:12
6066
|
6167
LL | Foo:Bar::Baz => {}
6268
| ^
6369
| |
6470
| expected one of `@` or `|`
6571
| help: maybe write a path separator here: `::`
6672

67-
error: aborting due to 8 previous errors
73+
error: expected one of `@` or `|`, found `:`
74+
--> $DIR/issue-87086-colon-path-sep.rs:75:12
75+
|
76+
LL | Foo:Bar => {}
77+
| ^
78+
| |
79+
| expected one of `@` or `|`
80+
| help: maybe write a path separator here: `::`
81+
82+
error[E0433]: failed to resolve: `Bar` is a variant, not a module
83+
--> $DIR/issue-87086-colon-path-sep.rs:68:13
84+
|
85+
LL | Foo:Bar::Baz => {}
86+
| ^^^ `Bar` is a variant, not a module
87+
88+
error: aborting due to 10 previous errors
6889

90+
For more information about this error, try `rustc --explain E0433`.

0 commit comments

Comments
 (0)