Auto merge of #45452 - estebank:colon-typo, r=nikomatsakis
Detect `=` -> `:` typo in let bindings
When encountering a let binding type error, attempt to parse as
initializer instead. If successful, it is likely just a typo:
```rust
fn main() {
let x: Vec::with_capacity(10);
}
```
```
error: expected type, found `10`
--> file.rs:3:31
|
3 | let x: Vec::with_capacity(10, 20);
| -- ^^
| ||
| |help: did you mean assign here?: `=`
| while parsing the type for `x`
```
Fix #43703.
This commit is contained in:
@@ -974,11 +974,12 @@ impl<'a> Parser<'a> {
|
|||||||
pub fn eat_to_tokens(&mut self, kets: &[&token::Token]) {
|
pub fn eat_to_tokens(&mut self, kets: &[&token::Token]) {
|
||||||
let handler = self.diagnostic();
|
let handler = self.diagnostic();
|
||||||
|
|
||||||
self.parse_seq_to_before_tokens(kets,
|
if let Err(ref mut err) = self.parse_seq_to_before_tokens(kets,
|
||||||
SeqSep::none(),
|
SeqSep::none(),
|
||||||
TokenExpectType::Expect,
|
TokenExpectType::Expect,
|
||||||
|p| Ok(p.parse_token_tree()),
|
|p| Ok(p.parse_token_tree())) {
|
||||||
|mut e| handler.cancel(&mut e));
|
handler.cancel(err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a sequence, including the closing delimiter. The function
|
/// Parse a sequence, including the closing delimiter. The function
|
||||||
@@ -991,7 +992,7 @@ impl<'a> Parser<'a> {
|
|||||||
-> PResult<'a, Vec<T>> where
|
-> PResult<'a, Vec<T>> where
|
||||||
F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
|
F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
|
||||||
{
|
{
|
||||||
let val = self.parse_seq_to_before_end(ket, sep, f);
|
let val = self.parse_seq_to_before_end(ket, sep, f)?;
|
||||||
self.bump();
|
self.bump();
|
||||||
Ok(val)
|
Ok(val)
|
||||||
}
|
}
|
||||||
@@ -1003,22 +1004,19 @@ impl<'a> Parser<'a> {
|
|||||||
ket: &token::Token,
|
ket: &token::Token,
|
||||||
sep: SeqSep,
|
sep: SeqSep,
|
||||||
f: F)
|
f: F)
|
||||||
-> Vec<T>
|
-> PResult<'a, Vec<T>>
|
||||||
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>
|
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>
|
||||||
{
|
{
|
||||||
self.parse_seq_to_before_tokens(&[ket], sep, TokenExpectType::Expect, f, |mut e| e.emit())
|
self.parse_seq_to_before_tokens(&[ket], sep, TokenExpectType::Expect, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// `fe` is an error handler.
|
fn parse_seq_to_before_tokens<T, F>(&mut self,
|
||||||
fn parse_seq_to_before_tokens<T, F, Fe>(&mut self,
|
|
||||||
kets: &[&token::Token],
|
kets: &[&token::Token],
|
||||||
sep: SeqSep,
|
sep: SeqSep,
|
||||||
expect: TokenExpectType,
|
expect: TokenExpectType,
|
||||||
mut f: F,
|
mut f: F)
|
||||||
mut fe: Fe)
|
-> PResult<'a, Vec<T>>
|
||||||
-> Vec<T>
|
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>
|
||||||
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
|
|
||||||
Fe: FnMut(DiagnosticBuilder)
|
|
||||||
{
|
{
|
||||||
let mut first: bool = true;
|
let mut first: bool = true;
|
||||||
let mut v = vec![];
|
let mut v = vec![];
|
||||||
@@ -1031,14 +1029,14 @@ impl<'a> Parser<'a> {
|
|||||||
if first {
|
if first {
|
||||||
first = false;
|
first = false;
|
||||||
} else {
|
} else {
|
||||||
if let Err(e) = self.expect(t) {
|
if let Err(mut e) = self.expect(t) {
|
||||||
fe(e);
|
|
||||||
// Attempt to keep parsing if it was a similar separator
|
// Attempt to keep parsing if it was a similar separator
|
||||||
if let Some(ref tokens) = t.similar_tokens() {
|
if let Some(ref tokens) = t.similar_tokens() {
|
||||||
if tokens.contains(&self.token) {
|
if tokens.contains(&self.token) {
|
||||||
self.bump();
|
self.bump();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
e.emit();
|
||||||
// Attempt to keep parsing if it was an omitted separator
|
// Attempt to keep parsing if it was an omitted separator
|
||||||
match f(self) {
|
match f(self) {
|
||||||
Ok(t) => {
|
Ok(t) => {
|
||||||
@@ -1062,16 +1060,11 @@ impl<'a> Parser<'a> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
match f(self) {
|
let t = f(self)?;
|
||||||
Ok(t) => v.push(t),
|
v.push(t);
|
||||||
Err(e) => {
|
|
||||||
fe(e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
v
|
Ok(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a sequence, including the closing delimiter. The function
|
/// Parse a sequence, including the closing delimiter. The function
|
||||||
@@ -1086,7 +1079,7 @@ impl<'a> Parser<'a> {
|
|||||||
F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
|
F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
|
||||||
{
|
{
|
||||||
self.expect(bra)?;
|
self.expect(bra)?;
|
||||||
let result = self.parse_seq_to_before_end(ket, sep, f);
|
let result = self.parse_seq_to_before_end(ket, sep, f)?;
|
||||||
if self.token == *ket {
|
if self.token == *ket {
|
||||||
self.bump();
|
self.bump();
|
||||||
}
|
}
|
||||||
@@ -1105,7 +1098,7 @@ impl<'a> Parser<'a> {
|
|||||||
{
|
{
|
||||||
let lo = self.span;
|
let lo = self.span;
|
||||||
self.expect(bra)?;
|
self.expect(bra)?;
|
||||||
let result = self.parse_seq_to_before_end(ket, sep, f);
|
let result = self.parse_seq_to_before_end(ket, sep, f)?;
|
||||||
let hi = self.span;
|
let hi = self.span;
|
||||||
self.bump();
|
self.bump();
|
||||||
Ok(respan(lo.to(hi), result))
|
Ok(respan(lo.to(hi), result))
|
||||||
@@ -1551,7 +1544,7 @@ impl<'a> Parser<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let span = lo.to(self.prev_span);
|
let span = lo.to(self.prev_span);
|
||||||
let ty = Ty { node: node, span: span, id: ast::DUMMY_NODE_ID };
|
let ty = Ty { node, span, id: ast::DUMMY_NODE_ID };
|
||||||
|
|
||||||
// Try to recover from use of `+` with incorrect priority.
|
// Try to recover from use of `+` with incorrect priority.
|
||||||
self.maybe_recover_from_bad_type_plus(allow_plus, &ty)?;
|
self.maybe_recover_from_bad_type_plus(allow_plus, &ty)?;
|
||||||
@@ -1868,8 +1861,11 @@ impl<'a> Parser<'a> {
|
|||||||
self.parse_path(style)
|
self.parse_path(style)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_path_segments(&mut self, segments: &mut Vec<PathSegment>, style: PathStyle,
|
fn parse_path_segments(&mut self,
|
||||||
enable_warning: bool) -> PResult<'a, ()> {
|
segments: &mut Vec<PathSegment>,
|
||||||
|
style: PathStyle,
|
||||||
|
enable_warning: bool)
|
||||||
|
-> PResult<'a, ()> {
|
||||||
loop {
|
loop {
|
||||||
segments.push(self.parse_path_segment(style, enable_warning)?);
|
segments.push(self.parse_path_segment(style, enable_warning)?);
|
||||||
|
|
||||||
@@ -1914,9 +1910,12 @@ impl<'a> Parser<'a> {
|
|||||||
} else {
|
} else {
|
||||||
// `(T, U) -> R`
|
// `(T, U) -> R`
|
||||||
self.bump(); // `(`
|
self.bump(); // `(`
|
||||||
let inputs = self.parse_seq_to_end(&token::CloseDelim(token::Paren),
|
let inputs = self.parse_seq_to_before_tokens(
|
||||||
|
&[&token::CloseDelim(token::Paren)],
|
||||||
SeqSep::trailing_allowed(token::Comma),
|
SeqSep::trailing_allowed(token::Comma),
|
||||||
|
TokenExpectType::Expect,
|
||||||
|p| p.parse_ty())?;
|
|p| p.parse_ty())?;
|
||||||
|
self.bump(); // `)`
|
||||||
let output = if self.eat(&token::RArrow) {
|
let output = if self.eat(&token::RArrow) {
|
||||||
Some(self.parse_ty_no_plus()?)
|
Some(self.parse_ty_no_plus()?)
|
||||||
} else {
|
} else {
|
||||||
@@ -3315,10 +3314,12 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the RHS of a local variable declaration (e.g. '= 14;')
|
/// Parse the RHS of a local variable declaration (e.g. '= 14;')
|
||||||
fn parse_initializer(&mut self) -> PResult<'a, Option<P<Expr>>> {
|
fn parse_initializer(&mut self, skip_eq: bool) -> PResult<'a, Option<P<Expr>>> {
|
||||||
if self.check(&token::Eq) {
|
if self.check(&token::Eq) {
|
||||||
self.bump();
|
self.bump();
|
||||||
Ok(Some(self.parse_expr()?))
|
Ok(Some(self.parse_expr()?))
|
||||||
|
} else if skip_eq {
|
||||||
|
Ok(Some(self.parse_expr()?))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
@@ -3725,12 +3726,56 @@ impl<'a> Parser<'a> {
|
|||||||
let lo = self.prev_span;
|
let lo = self.prev_span;
|
||||||
let pat = self.parse_pat()?;
|
let pat = self.parse_pat()?;
|
||||||
|
|
||||||
let ty = if self.eat(&token::Colon) {
|
let (err, ty) = if self.eat(&token::Colon) {
|
||||||
Some(self.parse_ty()?)
|
// Save the state of the parser before parsing type normally, in case there is a `:`
|
||||||
|
// instead of an `=` typo.
|
||||||
|
let parser_snapshot_before_type = self.clone();
|
||||||
|
let colon_sp = self.prev_span;
|
||||||
|
match self.parse_ty() {
|
||||||
|
Ok(ty) => (None, Some(ty)),
|
||||||
|
Err(mut err) => {
|
||||||
|
// Rewind to before attempting to parse the type and continue parsing
|
||||||
|
let parser_snapshot_after_type = self.clone();
|
||||||
|
mem::replace(self, parser_snapshot_before_type);
|
||||||
|
|
||||||
|
let snippet = self.sess.codemap().span_to_snippet(pat.span).unwrap();
|
||||||
|
err.span_label(pat.span, format!("while parsing the type for `{}`", snippet));
|
||||||
|
(Some((parser_snapshot_after_type, colon_sp, err)), None)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
(None, None)
|
||||||
|
};
|
||||||
|
let init = match (self.parse_initializer(err.is_some()), err) {
|
||||||
|
(Ok(init), None) => { // init parsed, ty parsed
|
||||||
|
init
|
||||||
|
}
|
||||||
|
(Ok(init), Some((_, colon_sp, mut err))) => { // init parsed, ty error
|
||||||
|
// Could parse the type as if it were the initializer, it is likely there was a
|
||||||
|
// typo in the code: `:` instead of `=`. Add suggestion and emit the error.
|
||||||
|
err.span_suggestion_short(colon_sp,
|
||||||
|
"use `=` if you meant to assign",
|
||||||
|
"=".to_string());
|
||||||
|
err.emit();
|
||||||
|
// As this was parsed successfuly, continue as if the code has been fixed for the
|
||||||
|
// rest of the file. It will still fail due to the emitted error, but we avoid
|
||||||
|
// extra noise.
|
||||||
|
init
|
||||||
|
}
|
||||||
|
(Err(mut init_err), Some((snapshot, _, ty_err))) => { // init error, ty error
|
||||||
|
init_err.cancel();
|
||||||
|
// Couldn't parse the type nor the initializer, only raise the type error and
|
||||||
|
// return to the parser state before parsing the type as the initializer.
|
||||||
|
// let x: <parse_error>;
|
||||||
|
mem::replace(self, snapshot);
|
||||||
|
return Err(ty_err);
|
||||||
|
}
|
||||||
|
(Err(err), None) => { // init error, ty parsed
|
||||||
|
// Couldn't parse the initializer and we're not attempting to recover a failed
|
||||||
|
// parse of the type, return the error.
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let init = self.parse_initializer()?;
|
|
||||||
let hi = if self.token == token::Semi {
|
let hi = if self.token == token::Semi {
|
||||||
self.span
|
self.span
|
||||||
} else {
|
} else {
|
||||||
@@ -4797,14 +4842,14 @@ impl<'a> Parser<'a> {
|
|||||||
} else if self.eat(&token::Comma) {
|
} else if self.eat(&token::Comma) {
|
||||||
let mut fn_inputs = vec![self_arg];
|
let mut fn_inputs = vec![self_arg];
|
||||||
fn_inputs.append(&mut self.parse_seq_to_before_end(
|
fn_inputs.append(&mut self.parse_seq_to_before_end(
|
||||||
&token::CloseDelim(token::Paren), sep, parse_arg_fn)
|
&token::CloseDelim(token::Paren), sep, parse_arg_fn)?
|
||||||
);
|
);
|
||||||
fn_inputs
|
fn_inputs
|
||||||
} else {
|
} else {
|
||||||
return self.unexpected();
|
return self.unexpected();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.parse_seq_to_before_end(&token::CloseDelim(token::Paren), sep, parse_arg_fn)
|
self.parse_seq_to_before_end(&token::CloseDelim(token::Paren), sep, parse_arg_fn)?
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse closing paren and return type.
|
// Parse closing paren and return type.
|
||||||
@@ -4827,9 +4872,8 @@ impl<'a> Parser<'a> {
|
|||||||
&[&token::BinOp(token::Or), &token::OrOr],
|
&[&token::BinOp(token::Or), &token::OrOr],
|
||||||
SeqSep::trailing_allowed(token::Comma),
|
SeqSep::trailing_allowed(token::Comma),
|
||||||
TokenExpectType::NoExpect,
|
TokenExpectType::NoExpect,
|
||||||
|p| p.parse_fn_block_arg(),
|
|p| p.parse_fn_block_arg()
|
||||||
|mut e| e.emit()
|
)?;
|
||||||
);
|
|
||||||
self.expect_or()?;
|
self.expect_or()?;
|
||||||
args
|
args
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ impl S {
|
|||||||
fn g(&self::S: &S) {}
|
fn g(&self::S: &S) {}
|
||||||
fn h(&mut self::S: &mut S) {}
|
fn h(&mut self::S: &mut S) {}
|
||||||
fn i(&'a self::S: &S) {} //~ ERROR unexpected lifetime `'a` in pattern
|
fn i(&'a self::S: &S) {} //~ ERROR unexpected lifetime `'a` in pattern
|
||||||
//~^ ERROR expected one of `)` or `mut`, found `'a`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|||||||
@@ -12,5 +12,4 @@
|
|||||||
|
|
||||||
impl S {
|
impl S {
|
||||||
fn f(*, a: u8) -> u8 {} //~ ERROR expected pattern, found `*`
|
fn f(*, a: u8) -> u8 {} //~ ERROR expected pattern, found `*`
|
||||||
//~^ ERROR expected one of `)`, `-`, `box`, `false`, `mut`, `ref`, or `true`, found `*`
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,14 +13,5 @@ error: expected type, found keyword `true`
|
|||||||
18 | foo!(true);
|
18 | foo!(true);
|
||||||
| ^^^^ expecting a type here because of type ascription
|
| ^^^^ expecting a type here because of type ascription
|
||||||
|
|
||||||
error: expected one of `!`, `&&`, `&`, `(`, `*`, `.`, `;`, `<`, `?`, `[`, `_`, `dyn`, `extern`, `fn`, `for`, `impl`, `unsafe`, `}`, an operator, or lifetime, found `true`
|
error: aborting due to 2 previous errors
|
||||||
--> $DIR/issue-44406.rs:18:10
|
|
||||||
|
|
|
||||||
13 | bar(baz: $rest)
|
|
||||||
| - expected one of 20 possible tokens here
|
|
||||||
...
|
|
||||||
18 | foo!(true);
|
|
||||||
| ^^^^ unexpected token
|
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
|
||||||
|
|
||||||
|
|||||||
@@ -28,29 +28,11 @@ error: expected expression, found `;`
|
|||||||
14 | foo(bar(;
|
14 | foo(bar(;
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: expected one of `)`, `,`, `.`, `<`, `?`, `break`, `continue`, `false`, `for`, `if`, `loop`, `match`, `move`, `return`, `true`, `unsafe`, `while`, `yield`, or an operator, found `;`
|
|
||||||
--> $DIR/token-error-correct.rs:14:13
|
|
||||||
|
|
|
||||||
14 | foo(bar(;
|
|
||||||
| ^ expected one of 19 possible tokens here
|
|
||||||
|
|
||||||
error: expected expression, found `)`
|
error: expected expression, found `)`
|
||||||
--> $DIR/token-error-correct.rs:23:1
|
--> $DIR/token-error-correct.rs:23:1
|
||||||
|
|
|
|
||||||
23 | }
|
23 | }
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error[E0425]: cannot find function `foo` in this scope
|
error: aborting due to 4 previous errors
|
||||||
--> $DIR/token-error-correct.rs:14:5
|
|
||||||
|
|
|
||||||
14 | foo(bar(;
|
|
||||||
| ^^^ not found in this scope
|
|
||||||
|
|
||||||
error[E0425]: cannot find function `bar` in this scope
|
|
||||||
--> $DIR/token-error-correct.rs:14:9
|
|
||||||
|
|
|
||||||
14 | foo(bar(;
|
|
||||||
| ^^^ not found in this scope
|
|
||||||
|
|
||||||
error: aborting due to 7 previous errors
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x: Vec::with_capacity(10, 20);
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
error: expected type, found `10`
|
||||||
|
--> $DIR/type-ascription-instead-of-initializer.rs:12:31
|
||||||
|
|
|
||||||
|
12 | let x: Vec::with_capacity(10, 20);
|
||||||
|
| -- ^^
|
||||||
|
| ||
|
||||||
|
| |help: use `=` if you meant to assign
|
||||||
|
| while parsing the type for `x`
|
||||||
|
|
||||||
|
error[E0061]: this function takes 1 parameter but 2 parameters were supplied
|
||||||
|
--> $DIR/type-ascription-instead-of-initializer.rs:12:31
|
||||||
|
|
|
||||||
|
12 | let x: Vec::with_capacity(10, 20);
|
||||||
|
| ^^^^^^ expected 1 parameter
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
Reference in New Issue
Block a user