Detect missing .
in method chain in let bindings and statements
On parse errors where an ident is found where one wasn't expected, see if the next elements might have been meant as method call or field access. ``` error: expected one of `.`, `;`, `?`, `else`, or an operator, found `map` --> $DIR/missing-dot-on-statement-expression.rs:7:29 | LL | let _ = [1, 2, 3].iter()map(|x| x); | ^^^ expected one of `.`, `;`, `?`, `else`, or an operator | help: you might have meant to write a method call | LL | let _ = [1, 2, 3].iter().map(|x| x); | + ```
This commit is contained in:
parent
9e136a30a9
commit
1ce0fa98c7
7 changed files with 165 additions and 2 deletions
|
@ -745,6 +745,42 @@ impl<'a> Parser<'a> {
|
||||||
Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span)))
|
Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn recover_missing_dot(&mut self, err: &mut Diag<'_>) {
|
||||||
|
if !self.token.is_ident() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if self.token.is_reserved_ident() && !self.token.is_ident_named(kw::Await) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if self.prev_token.is_reserved_ident() && self.prev_token.is_ident_named(kw::Await) {
|
||||||
|
// Likely `foo.await bar`
|
||||||
|
} else if !self.prev_token.is_reserved_ident() && self.prev_token.is_ident() {
|
||||||
|
// Likely `foo bar`
|
||||||
|
} else if self.prev_token.kind == token::Question {
|
||||||
|
// `foo? bar`
|
||||||
|
} else if self.prev_token.kind == token::CloseDelim(Delimiter::Parenthesis) {
|
||||||
|
// `foo() bar`
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if self.look_ahead(1, |t| [token::Semi, token::Question, token::Dot].contains(&t.kind)) {
|
||||||
|
err.span_suggestion_verbose(
|
||||||
|
self.prev_token.span.between(self.token.span),
|
||||||
|
"you might have meant to write a field access",
|
||||||
|
".".to_string(),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Parenthesis)) {
|
||||||
|
err.span_suggestion_verbose(
|
||||||
|
self.prev_token.span.between(self.token.span),
|
||||||
|
"you might have meant to write a method call",
|
||||||
|
".".to_string(),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses a statement, including the trailing semicolon.
|
/// Parses a statement, including the trailing semicolon.
|
||||||
pub fn parse_full_stmt(
|
pub fn parse_full_stmt(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -851,7 +887,8 @@ impl<'a> Parser<'a> {
|
||||||
Some(if recover.no() {
|
Some(if recover.no() {
|
||||||
res?
|
res?
|
||||||
} else {
|
} else {
|
||||||
res.unwrap_or_else(|e| {
|
res.unwrap_or_else(|mut e| {
|
||||||
|
self.recover_missing_dot(&mut e);
|
||||||
let guar = e.emit();
|
let guar = e.emit();
|
||||||
self.recover_stmt();
|
self.recover_stmt();
|
||||||
guar
|
guar
|
||||||
|
@ -872,7 +909,12 @@ impl<'a> Parser<'a> {
|
||||||
// We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover.
|
// We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover.
|
||||||
match &mut local.kind {
|
match &mut local.kind {
|
||||||
LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => {
|
LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => {
|
||||||
self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?;
|
self.check_mistyped_turbofish_with_multiple_type_params(e, expr).map_err(
|
||||||
|
|mut e| {
|
||||||
|
self.recover_missing_dot(&mut e);
|
||||||
|
e
|
||||||
|
},
|
||||||
|
)?;
|
||||||
// We found `foo<bar, baz>`, have we fully recovered?
|
// We found `foo<bar, baz>`, have we fully recovered?
|
||||||
self.expect_semi()?;
|
self.expect_semi()?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,22 @@ error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found
|
||||||
|
|
|
|
||||||
LL | r#struct Test;
|
LL | r#struct Test;
|
||||||
| ^^^^ expected one of 8 possible tokens
|
| ^^^^ expected one of 8 possible tokens
|
||||||
|
|
|
||||||
|
help: you might have meant to write a field access
|
||||||
|
|
|
||||||
|
LL | r#struct.Test;
|
||||||
|
| +
|
||||||
|
|
||||||
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `Test`
|
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `Test`
|
||||||
--> $DIR/raw-literal-keywords.rs:10:13
|
--> $DIR/raw-literal-keywords.rs:10:13
|
||||||
|
|
|
|
||||||
LL | r#union Test;
|
LL | r#union Test;
|
||||||
| ^^^^ expected one of 8 possible tokens
|
| ^^^^ expected one of 8 possible tokens
|
||||||
|
|
|
||||||
|
help: you might have meant to write a field access
|
||||||
|
|
|
||||||
|
LL | r#union.Test;
|
||||||
|
| +
|
||||||
|
|
||||||
error[E0425]: cannot find value `r#if` in this scope
|
error[E0425]: cannot find value `r#if` in this scope
|
||||||
--> $DIR/raw-literal-keywords.rs:14:13
|
--> $DIR/raw-literal-keywords.rs:14:13
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
//@ run-rustfix
|
||||||
|
#![allow(unused_must_use, dead_code)]
|
||||||
|
struct S {
|
||||||
|
field: (),
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
let _ = [1, 2, 3].iter().map(|x| x); //~ ERROR expected one of `.`, `;`, `?`, `else`, or an operator, found `map`
|
||||||
|
//~^ HELP you might have meant to write a method call
|
||||||
|
}
|
||||||
|
fn foo() {
|
||||||
|
let baz = S {
|
||||||
|
field: ()
|
||||||
|
};
|
||||||
|
let _ = baz.field; //~ ERROR expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `field`
|
||||||
|
//~^ HELP you might have meant to write a field
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
[1, 2, 3].iter().map(|x| x); //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `map`
|
||||||
|
//~^ HELP you might have meant to write a method call
|
||||||
|
}
|
||||||
|
fn baz() {
|
||||||
|
let baz = S {
|
||||||
|
field: ()
|
||||||
|
};
|
||||||
|
baz.field; //~ ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `field`
|
||||||
|
//~^ HELP you might have meant to write a field
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
//@ run-rustfix
|
||||||
|
#![allow(unused_must_use, dead_code)]
|
||||||
|
struct S {
|
||||||
|
field: (),
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
let _ = [1, 2, 3].iter()map(|x| x); //~ ERROR expected one of `.`, `;`, `?`, `else`, or an operator, found `map`
|
||||||
|
//~^ HELP you might have meant to write a method call
|
||||||
|
}
|
||||||
|
fn foo() {
|
||||||
|
let baz = S {
|
||||||
|
field: ()
|
||||||
|
};
|
||||||
|
let _ = baz field; //~ ERROR expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `field`
|
||||||
|
//~^ HELP you might have meant to write a field
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
[1, 2, 3].iter()map(|x| x); //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `map`
|
||||||
|
//~^ HELP you might have meant to write a method call
|
||||||
|
}
|
||||||
|
fn baz() {
|
||||||
|
let baz = S {
|
||||||
|
field: ()
|
||||||
|
};
|
||||||
|
baz field; //~ ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `field`
|
||||||
|
//~^ HELP you might have meant to write a field
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
error: expected one of `.`, `;`, `?`, `else`, or an operator, found `map`
|
||||||
|
--> $DIR/missing-dot-on-statement-expression.rs:7:29
|
||||||
|
|
|
||||||
|
LL | let _ = [1, 2, 3].iter()map(|x| x);
|
||||||
|
| ^^^ expected one of `.`, `;`, `?`, `else`, or an operator
|
||||||
|
|
|
||||||
|
help: you might have meant to write a method call
|
||||||
|
|
|
||||||
|
LL | let _ = [1, 2, 3].iter().map(|x| x);
|
||||||
|
| +
|
||||||
|
|
||||||
|
error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `field`
|
||||||
|
--> $DIR/missing-dot-on-statement-expression.rs:14:17
|
||||||
|
|
|
||||||
|
LL | let _ = baz field;
|
||||||
|
| ^^^^^ expected one of 8 possible tokens
|
||||||
|
|
|
||||||
|
help: you might have meant to write a field access
|
||||||
|
|
|
||||||
|
LL | let _ = baz.field;
|
||||||
|
| +
|
||||||
|
|
||||||
|
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `map`
|
||||||
|
--> $DIR/missing-dot-on-statement-expression.rs:19:21
|
||||||
|
|
|
||||||
|
LL | [1, 2, 3].iter()map(|x| x);
|
||||||
|
| ^^^ expected one of `.`, `;`, `?`, `}`, or an operator
|
||||||
|
|
|
||||||
|
help: you might have meant to write a method call
|
||||||
|
|
|
||||||
|
LL | [1, 2, 3].iter().map(|x| x);
|
||||||
|
| +
|
||||||
|
|
||||||
|
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `field`
|
||||||
|
--> $DIR/missing-dot-on-statement-expression.rs:26:9
|
||||||
|
|
|
||||||
|
LL | baz field;
|
||||||
|
| ^^^^^ expected one of 8 possible tokens
|
||||||
|
|
|
||||||
|
help: you might have meant to write a field access
|
||||||
|
|
|
||||||
|
LL | baz.field;
|
||||||
|
| +
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
|
@ -5,6 +5,10 @@ LL | make_bad_struct!(S);
|
||||||
| ^^^^^^^^^^^^^^^^^^^ expected one of 8 possible tokens
|
| ^^^^^^^^^^^^^^^^^^^ expected one of 8 possible tokens
|
||||||
|
|
|
|
||||||
= note: this error originates in the macro `make_bad_struct` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `make_bad_struct` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
help: you might have meant to write a field access
|
||||||
|
|
|
||||||
|
LL | .;
|
||||||
|
| ~
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,11 @@ error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found
|
||||||
|
|
|
|
||||||
LL | not rust;
|
LL | not rust;
|
||||||
| ^^^^ expected one of 8 possible tokens
|
| ^^^^ expected one of 8 possible tokens
|
||||||
|
|
|
||||||
|
help: you might have meant to write a field access
|
||||||
|
|
|
||||||
|
LL | not.rust;
|
||||||
|
| +
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue