1
Fork 0

When recovering from a : in a pattern, use adequate AST pattern

This commit is contained in:
Esteban Küber 2021-07-15 08:36:19 -07:00 committed by Esteban Kuber
parent 862962b90e
commit d6e34ad108
3 changed files with 152 additions and 46 deletions

View file

@ -3,13 +3,17 @@ use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor}; use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor};
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::token; use rustc_ast::token;
use rustc_ast::{self as ast, AttrVec, Attribute, MacCall, Pat, PatField, PatKind, RangeEnd}; use rustc_ast::{
use rustc_ast::{BindingMode, Expr, ExprKind, Mutability, Path, QSelf, RangeSyntax}; self as ast, AttrVec, Attribute, BindingMode, Expr, ExprKind, MacCall, Mutability, Pat,
PatField, PatKind, Path, PathSegment, QSelf, RangeEnd, RangeSyntax,
};
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, PResult}; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, PResult};
use rustc_span::source_map::{respan, Span, Spanned}; use rustc_span::source_map::{respan, Span, Spanned};
use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::symbol::{kw, sym, Ident};
use std::mem::take;
type Expected = Option<&'static str>; type Expected = Option<&'static str>;
/// `Expected` for function and lambda parameter patterns. /// `Expected` for function and lambda parameter patterns.
@ -101,11 +105,8 @@ impl<'a> Parser<'a> {
let mut first_pat = first_pat; let mut first_pat = first_pat;
if let (RecoverColon::Yes, token::Colon) = (ra, &self.token.kind) { if let (RecoverColon::Yes, token::Colon) = (ra, &self.token.kind) {
if matches!( if matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..))
first_pat.kind, && self.look_ahead(1, |token| token.is_ident() && !token.is_reserved_ident())
PatKind::Ident(BindingMode::ByValue(Mutability::Not), _, None)
| PatKind::Path(..)
) && self.look_ahead(1, |token| token.is_ident() && !token.is_reserved_ident())
{ {
// The pattern looks like it might be a path with a `::` -> `:` typo: // The pattern looks like it might be a path with a `::` -> `:` typo:
// `match foo { bar:baz => {} }` // `match foo { bar:baz => {} }`
@ -126,17 +127,87 @@ impl<'a> Parser<'a> {
err.cancel(); err.cancel();
*self = snapshot; *self = snapshot;
} }
Ok(pat) => { Ok(mut pat) => {
// We've parsed the rest of the pattern. // We've parsed the rest of the pattern.
err.span_suggestion( let new_span = first_pat.span.to(pat.span);
span, let mut show_sugg = false;
"maybe write a path separator here", match &mut pat.kind {
"::".to_string(), PatKind::Struct(qself @ None, path, ..)
Applicability::MachineApplicable, | PatKind::TupleStruct(qself @ None, path, _)
); | PatKind::Path(qself @ None, path) => {
match &first_pat.kind {
PatKind::Ident(_, ident, _) => {
path.segments.insert(
0,
PathSegment::from_ident(ident.clone()),
);
path.span = new_span;
show_sugg = true;
first_pat = pat;
}
PatKind::Path(old_qself, old_path) => {
path.segments = old_path
.segments
.iter()
.cloned()
.chain(take(&mut path.segments))
.collect();
path.span = new_span;
*qself = old_qself.clone();
first_pat = pat;
show_sugg = true;
}
_ => {}
}
}
PatKind::Ident(
BindingMode::ByValue(Mutability::Not),
ident,
None,
) => match &first_pat.kind {
PatKind::Ident(_, old_ident, _) => {
let path = PatKind::Path(
None,
Path {
span: new_span,
segments: vec![
PathSegment::from_ident(
old_ident.clone(),
),
PathSegment::from_ident(ident.clone()),
],
tokens: None,
},
);
first_pat = self.mk_pat(new_span, path);
show_sugg = true;
}
PatKind::Path(old_qself, old_path) => {
let mut segments = old_path.segments.clone();
segments
.push(PathSegment::from_ident(ident.clone()));
let path = PatKind::Path(
old_qself.clone(),
Path { span: new_span, segments, tokens: None },
);
first_pat = self.mk_pat(new_span, path);
show_sugg = true;
}
_ => {}
},
_ => {}
}
if show_sugg {
err.span_suggestion(
span,
"maybe write a path separator here",
"::".to_string(),
Applicability::MachineApplicable,
);
} else {
first_pat = self.mk_pat(new_span, PatKind::Wild);
}
err.emit(); err.emit();
first_pat =
self.mk_pat(first_pat.span.to(pat.span), PatKind::Wild);
} }
} }
} }

View file

@ -1,11 +1,15 @@
// Tests that a suggestion is issued if the user wrote a colon instead of // Tests that a suggestion is issued if the user wrote a colon instead of
// a path separator in a match arm. // a path separator in a match arm.
enum Foo { mod qux {
Bar, pub enum Foo {
Baz, Bar,
Baz,
}
} }
use qux::Foo;
fn f() -> Foo { Foo::Bar } fn f() -> Foo { Foo::Bar }
fn g1() { fn g1() {
@ -16,24 +20,24 @@ fn g1() {
_ => {} _ => {}
} }
match f() { match f() {
Foo::Bar:Baz => {} qux::Foo:Bar => {}
//~^ ERROR: expected one of //~^ ERROR: expected one of
//~| HELP: maybe write a path separator here //~| HELP: maybe write a path separator here
_ => {} _ => {}
} }
match f() { match f() {
Foo:Bar::Baz => {} qux:Foo::Baz => {}
//~^ ERROR: expected one of //~^ ERROR: expected one of
//~| HELP: maybe write a path separator here //~| HELP: maybe write a path separator here
_ => {} _ => {}
} }
match f() { match f() {
Foo: Bar::Baz if true => {} qux: Foo::Baz if true => {}
//~^ ERROR: expected one of //~^ ERROR: expected one of
//~| HELP: maybe write a path separator here //~| HELP: maybe write a path separator here
_ => {} _ => {}
} }
if let Bar:Baz = f() { if let Foo:Bar = f() {
//~^ ERROR: expected one of //~^ ERROR: expected one of
//~| HELP: maybe write a path separator here //~| HELP: maybe write a path separator here
} }
@ -41,16 +45,18 @@ fn g1() {
fn g1_neg() { fn g1_neg() {
match f() { match f() {
ref Foo: Bar::Baz => {} ref qux: Foo::Baz => {}
//~^ ERROR: expected one of //~^ ERROR: expected one of
//~| HELP: maybe write a path separator here
_ => {} _ => {}
} }
} }
fn g2_neg() { fn g2_neg() {
match f() { match f() {
mut Foo: Bar::Baz => {} mut qux: Foo::Baz => {}
//~^ ERROR: expected one of //~^ ERROR: expected one of
//~| HELP: maybe write a path separator here
_ => {} _ => {}
} }
} }
@ -62,5 +68,12 @@ fn main() {
Foo:Bar::Baz => {} Foo:Bar::Baz => {}
//~^ ERROR: expected one of //~^ ERROR: expected one of
//~| HELP: maybe write a path separator here //~| HELP: maybe write a path separator here
//~| ERROR: failed to resolve: `Bar` is a variant, not a module
}
match myfoo {
Foo::Bar => {}
Foo:Bar => {}
//~^ ERROR: expected one of
//~| HELP: maybe write a path separator here
} }
} }

View file

@ -1,5 +1,5 @@
error: expected one of `@` or `|`, found `:` error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:13:12 --> $DIR/issue-87086-colon-path-sep.rs:17:12
| |
LL | Foo:Bar => {} LL | Foo:Bar => {}
| ^ | ^
@ -8,55 +8,61 @@ LL | Foo:Bar => {}
| help: maybe write a path separator here: `::` | help: maybe write a path separator here: `::`
error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `{`, or `|`, found `:` error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `{`, or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:19:17 --> $DIR/issue-87086-colon-path-sep.rs:23:17
| |
LL | Foo::Bar:Baz => {} LL | qux::Foo:Bar => {}
| ^ | ^
| | | |
| expected one of 8 possible tokens | expected one of 8 possible tokens
| help: maybe write a path separator here: `::` | help: maybe write a path separator here: `::`
error: expected one of `@` or `|`, found `:` error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:25:12 --> $DIR/issue-87086-colon-path-sep.rs:29:12
| |
LL | Foo:Bar::Baz => {} LL | qux:Foo::Baz => {}
| ^ | ^
| | | |
| expected one of `@` or `|` | expected one of `@` or `|`
| help: maybe write a path separator here: `::` | help: maybe write a path separator here: `::`
error: expected one of `@` or `|`, found `:` error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:31:12 --> $DIR/issue-87086-colon-path-sep.rs:35:12
| |
LL | Foo: Bar::Baz if true => {} LL | qux: Foo::Baz if true => {}
| ^ | ^
| | | |
| expected one of `@` or `|` | expected one of `@` or `|`
| help: maybe write a path separator here: `::` | help: maybe write a path separator here: `::`
error: expected one of `@` or `|`, found `:` error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:36:15 --> $DIR/issue-87086-colon-path-sep.rs:40:15
| |
LL | if let Bar:Baz = f() { LL | if let Foo:Bar = f() {
| ^ | ^
| | | |
| expected one of `@` or `|` | expected one of `@` or `|`
| help: maybe write a path separator here: `::` | help: maybe write a path separator here: `::`
error: expected one of `=>`, `@`, `if`, or `|`, found `:` error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:44:16 --> $DIR/issue-87086-colon-path-sep.rs:48:16
| |
LL | ref Foo: Bar::Baz => {} LL | ref qux: Foo::Baz => {}
| ^ expected one of `=>`, `@`, `if`, or `|` | ^
| |
error: expected one of `=>`, `@`, `if`, or `|`, found `:` | expected one of `@` or `|`
--> $DIR/issue-87086-colon-path-sep.rs:52:16 | help: maybe write a path separator here: `::`
|
LL | mut Foo: Bar::Baz => {}
| ^ expected one of `=>`, `@`, `if`, or `|`
error: expected one of `@` or `|`, found `:` error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:62:12 --> $DIR/issue-87086-colon-path-sep.rs:57:16
|
LL | mut qux: Foo::Baz => {}
| ^
| |
| expected one of `@` or `|`
| help: maybe write a path separator here: `::`
error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:68:12
| |
LL | Foo:Bar::Baz => {} LL | Foo:Bar::Baz => {}
| ^ | ^
@ -64,5 +70,21 @@ LL | Foo:Bar::Baz => {}
| expected one of `@` or `|` | expected one of `@` or `|`
| help: maybe write a path separator here: `::` | help: maybe write a path separator here: `::`
error: aborting due to 8 previous errors error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:75:12
|
LL | Foo:Bar => {}
| ^
| |
| expected one of `@` or `|`
| help: maybe write a path separator here: `::`
error[E0433]: failed to resolve: `Bar` is a variant, not a module
--> $DIR/issue-87086-colon-path-sep.rs:68:13
|
LL | Foo:Bar::Baz => {}
| ^^^ `Bar` is a variant, not a module
error: aborting due to 10 previous errors
For more information about this error, try `rustc --explain E0433`.