Skip to content

Commit 02004ef

Browse files
committed
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.
2 parents 7ca430d + 9dc7abe commit 02004ef

File tree

8 files changed

+121
-76
lines changed

8 files changed

+121
-76
lines changed

src/librustc_resolve/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2603,7 +2603,7 @@ impl<'a> Resolver<'a> {
26032603
}
26042604
}
26052605
err.span_label(span, format!("did you mean `{} {{ /* fields */ }}`?",
2606-
path_str));
2606+
path_str));
26072607
return (err, candidates);
26082608
}
26092609
_ => {}

src/libsyntax/parse/parser.rs

+88-44
Original file line numberDiff line numberDiff line change
@@ -974,11 +974,12 @@ impl<'a> Parser<'a> {
974974
pub fn eat_to_tokens(&mut self, kets: &[&token::Token]) {
975975
let handler = self.diagnostic();
976976

977-
self.parse_seq_to_before_tokens(kets,
978-
SeqSep::none(),
979-
TokenExpectType::Expect,
980-
|p| Ok(p.parse_token_tree()),
981-
|mut e| handler.cancel(&mut e));
977+
if let Err(ref mut err) = self.parse_seq_to_before_tokens(kets,
978+
SeqSep::none(),
979+
TokenExpectType::Expect,
980+
|p| Ok(p.parse_token_tree())) {
981+
handler.cancel(err);
982+
}
982983
}
983984

984985
/// Parse a sequence, including the closing delimiter. The function
@@ -991,7 +992,7 @@ impl<'a> Parser<'a> {
991992
-> PResult<'a, Vec<T>> where
992993
F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
993994
{
994-
let val = self.parse_seq_to_before_end(ket, sep, f);
995+
let val = self.parse_seq_to_before_end(ket, sep, f)?;
995996
self.bump();
996997
Ok(val)
997998
}
@@ -1003,22 +1004,19 @@ impl<'a> Parser<'a> {
10031004
ket: &token::Token,
10041005
sep: SeqSep,
10051006
f: F)
1006-
-> Vec<T>
1007-
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>
1007+
-> PResult<'a, Vec<T>>
1008+
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>
10081009
{
1009-
self.parse_seq_to_before_tokens(&[ket], sep, TokenExpectType::Expect, f, |mut e| e.emit())
1010+
self.parse_seq_to_before_tokens(&[ket], sep, TokenExpectType::Expect, f)
10101011
}
10111012

1012-
// `fe` is an error handler.
1013-
fn parse_seq_to_before_tokens<T, F, Fe>(&mut self,
1013+
fn parse_seq_to_before_tokens<T, F>(&mut self,
10141014
kets: &[&token::Token],
10151015
sep: SeqSep,
10161016
expect: TokenExpectType,
1017-
mut f: F,
1018-
mut fe: Fe)
1019-
-> Vec<T>
1020-
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
1021-
Fe: FnMut(DiagnosticBuilder)
1017+
mut f: F)
1018+
-> PResult<'a, Vec<T>>
1019+
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>
10221020
{
10231021
let mut first: bool = true;
10241022
let mut v = vec![];
@@ -1031,14 +1029,14 @@ impl<'a> Parser<'a> {
10311029
if first {
10321030
first = false;
10331031
} else {
1034-
if let Err(e) = self.expect(t) {
1035-
fe(e);
1032+
if let Err(mut e) = self.expect(t) {
10361033
// Attempt to keep parsing if it was a similar separator
10371034
if let Some(ref tokens) = t.similar_tokens() {
10381035
if tokens.contains(&self.token) {
10391036
self.bump();
10401037
}
10411038
}
1039+
e.emit();
10421040
// Attempt to keep parsing if it was an omitted separator
10431041
match f(self) {
10441042
Ok(t) => {
@@ -1062,16 +1060,11 @@ impl<'a> Parser<'a> {
10621060
break;
10631061
}
10641062

1065-
match f(self) {
1066-
Ok(t) => v.push(t),
1067-
Err(e) => {
1068-
fe(e);
1069-
break;
1070-
}
1071-
}
1063+
let t = f(self)?;
1064+
v.push(t);
10721065
}
10731066

1074-
v
1067+
Ok(v)
10751068
}
10761069

10771070
/// Parse a sequence, including the closing delimiter. The function
@@ -1086,7 +1079,7 @@ impl<'a> Parser<'a> {
10861079
F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
10871080
{
10881081
self.expect(bra)?;
1089-
let result = self.parse_seq_to_before_end(ket, sep, f);
1082+
let result = self.parse_seq_to_before_end(ket, sep, f)?;
10901083
if self.token == *ket {
10911084
self.bump();
10921085
}
@@ -1105,7 +1098,7 @@ impl<'a> Parser<'a> {
11051098
{
11061099
let lo = self.span;
11071100
self.expect(bra)?;
1108-
let result = self.parse_seq_to_before_end(ket, sep, f);
1101+
let result = self.parse_seq_to_before_end(ket, sep, f)?;
11091102
let hi = self.span;
11101103
self.bump();
11111104
Ok(respan(lo.to(hi), result))
@@ -1551,7 +1544,7 @@ impl<'a> Parser<'a> {
15511544
};
15521545

15531546
let span = lo.to(self.prev_span);
1554-
let ty = Ty { node: node, span: span, id: ast::DUMMY_NODE_ID };
1547+
let ty = Ty { node, span, id: ast::DUMMY_NODE_ID };
15551548

15561549
// Try to recover from use of `+` with incorrect priority.
15571550
self.maybe_recover_from_bad_type_plus(allow_plus, &ty)?;
@@ -1868,8 +1861,11 @@ impl<'a> Parser<'a> {
18681861
self.parse_path(style)
18691862
}
18701863

1871-
fn parse_path_segments(&mut self, segments: &mut Vec<PathSegment>, style: PathStyle,
1872-
enable_warning: bool) -> PResult<'a, ()> {
1864+
fn parse_path_segments(&mut self,
1865+
segments: &mut Vec<PathSegment>,
1866+
style: PathStyle,
1867+
enable_warning: bool)
1868+
-> PResult<'a, ()> {
18731869
loop {
18741870
segments.push(self.parse_path_segment(style, enable_warning)?);
18751871

@@ -1914,9 +1910,12 @@ impl<'a> Parser<'a> {
19141910
} else {
19151911
// `(T, U) -> R`
19161912
self.bump(); // `(`
1917-
let inputs = self.parse_seq_to_end(&token::CloseDelim(token::Paren),
1918-
SeqSep::trailing_allowed(token::Comma),
1919-
|p| p.parse_ty())?;
1913+
let inputs = self.parse_seq_to_before_tokens(
1914+
&[&token::CloseDelim(token::Paren)],
1915+
SeqSep::trailing_allowed(token::Comma),
1916+
TokenExpectType::Expect,
1917+
|p| p.parse_ty())?;
1918+
self.bump(); // `)`
19201919
let output = if self.eat(&token::RArrow) {
19211920
Some(self.parse_ty_no_plus()?)
19221921
} else {
@@ -3315,10 +3314,12 @@ impl<'a> Parser<'a> {
33153314
}
33163315

33173316
/// Parse the RHS of a local variable declaration (e.g. '= 14;')
3318-
fn parse_initializer(&mut self) -> PResult<'a, Option<P<Expr>>> {
3317+
fn parse_initializer(&mut self, skip_eq: bool) -> PResult<'a, Option<P<Expr>>> {
33193318
if self.check(&token::Eq) {
33203319
self.bump();
33213320
Ok(Some(self.parse_expr()?))
3321+
} else if skip_eq {
3322+
Ok(Some(self.parse_expr()?))
33223323
} else {
33233324
Ok(None)
33243325
}
@@ -3725,12 +3726,56 @@ impl<'a> Parser<'a> {
37253726
let lo = self.prev_span;
37263727
let pat = self.parse_pat()?;
37273728

3728-
let ty = if self.eat(&token::Colon) {
3729-
Some(self.parse_ty()?)
3729+
let (err, ty) = if self.eat(&token::Colon) {
3730+
// Save the state of the parser before parsing type normally, in case there is a `:`
3731+
// instead of an `=` typo.
3732+
let parser_snapshot_before_type = self.clone();
3733+
let colon_sp = self.prev_span;
3734+
match self.parse_ty() {
3735+
Ok(ty) => (None, Some(ty)),
3736+
Err(mut err) => {
3737+
// Rewind to before attempting to parse the type and continue parsing
3738+
let parser_snapshot_after_type = self.clone();
3739+
mem::replace(self, parser_snapshot_before_type);
3740+
3741+
let snippet = self.sess.codemap().span_to_snippet(pat.span).unwrap();
3742+
err.span_label(pat.span, format!("while parsing the type for `{}`", snippet));
3743+
(Some((parser_snapshot_after_type, colon_sp, err)), None)
3744+
}
3745+
}
37303746
} else {
3731-
None
3747+
(None, None)
3748+
};
3749+
let init = match (self.parse_initializer(err.is_some()), err) {
3750+
(Ok(init), None) => { // init parsed, ty parsed
3751+
init
3752+
}
3753+
(Ok(init), Some((_, colon_sp, mut err))) => { // init parsed, ty error
3754+
// Could parse the type as if it were the initializer, it is likely there was a
3755+
// typo in the code: `:` instead of `=`. Add suggestion and emit the error.
3756+
err.span_suggestion_short(colon_sp,
3757+
"use `=` if you meant to assign",
3758+
"=".to_string());
3759+
err.emit();
3760+
// As this was parsed successfuly, continue as if the code has been fixed for the
3761+
// rest of the file. It will still fail due to the emitted error, but we avoid
3762+
// extra noise.
3763+
init
3764+
}
3765+
(Err(mut init_err), Some((snapshot, _, ty_err))) => { // init error, ty error
3766+
init_err.cancel();
3767+
// Couldn't parse the type nor the initializer, only raise the type error and
3768+
// return to the parser state before parsing the type as the initializer.
3769+
// let x: <parse_error>;
3770+
mem::replace(self, snapshot);
3771+
return Err(ty_err);
3772+
}
3773+
(Err(err), None) => { // init error, ty parsed
3774+
// Couldn't parse the initializer and we're not attempting to recover a failed
3775+
// parse of the type, return the error.
3776+
return Err(err);
3777+
}
37323778
};
3733-
let init = self.parse_initializer()?;
37343779
let hi = if self.token == token::Semi {
37353780
self.span
37363781
} else {
@@ -4797,14 +4842,14 @@ impl<'a> Parser<'a> {
47974842
} else if self.eat(&token::Comma) {
47984843
let mut fn_inputs = vec![self_arg];
47994844
fn_inputs.append(&mut self.parse_seq_to_before_end(
4800-
&token::CloseDelim(token::Paren), sep, parse_arg_fn)
4845+
&token::CloseDelim(token::Paren), sep, parse_arg_fn)?
48014846
);
48024847
fn_inputs
48034848
} else {
48044849
return self.unexpected();
48054850
}
48064851
} else {
4807-
self.parse_seq_to_before_end(&token::CloseDelim(token::Paren), sep, parse_arg_fn)
4852+
self.parse_seq_to_before_end(&token::CloseDelim(token::Paren), sep, parse_arg_fn)?
48084853
};
48094854

48104855
// Parse closing paren and return type.
@@ -4827,9 +4872,8 @@ impl<'a> Parser<'a> {
48274872
&[&token::BinOp(token::Or), &token::OrOr],
48284873
SeqSep::trailing_allowed(token::Comma),
48294874
TokenExpectType::NoExpect,
4830-
|p| p.parse_fn_block_arg(),
4831-
|mut e| e.emit()
4832-
);
4875+
|p| p.parse_fn_block_arg()
4876+
)?;
48334877
self.expect_or()?;
48344878
args
48354879
}

src/test/compile-fail/self-vs-path-ambiguity.rs

-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ impl S {
1717
fn g(&self::S: &S) {}
1818
fn h(&mut self::S: &mut S) {}
1919
fn i(&'a self::S: &S) {} //~ ERROR unexpected lifetime `'a` in pattern
20-
//~^ ERROR expected one of `)` or `mut`, found `'a`
2120
}
2221

2322
fn main() {}

src/test/parse-fail/issue-33413.rs

-1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,4 @@
1212

1313
impl S {
1414
fn f(*, a: u8) -> u8 {} //~ ERROR expected pattern, found `*`
15-
//~^ ERROR expected one of `)`, `-`, `box`, `false`, `mut`, `ref`, or `true`, found `*`
1615
}

src/test/ui/issue-44406.stderr

+1-10
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,5 @@ error: expected type, found keyword `true`
1313
18 | foo!(true);
1414
| ^^^^ expecting a type here because of type ascription
1515

16-
error: expected one of `!`, `&&`, `&`, `(`, `*`, `.`, `;`, `<`, `?`, `[`, `_`, `dyn`, `extern`, `fn`, `for`, `impl`, `unsafe`, `}`, an operator, or lifetime, found `true`
17-
--> $DIR/issue-44406.rs:18:10
18-
|
19-
13 | bar(baz: $rest)
20-
| - expected one of 20 possible tokens here
21-
...
22-
18 | foo!(true);
23-
| ^^^^ unexpected token
24-
25-
error: aborting due to 3 previous errors
16+
error: aborting due to 2 previous errors
2617

src/test/ui/resolve/token-error-correct.stderr

+1-19
Original file line numberDiff line numberDiff line change
@@ -28,29 +28,11 @@ error: expected expression, found `;`
2828
14 | foo(bar(;
2929
| ^
3030

31-
error: expected one of `)`, `,`, `.`, `<`, `?`, `break`, `continue`, `false`, `for`, `if`, `loop`, `match`, `move`, `return`, `true`, `unsafe`, `while`, `yield`, or an operator, found `;`
32-
--> $DIR/token-error-correct.rs:14:13
33-
|
34-
14 | foo(bar(;
35-
| ^ expected one of 19 possible tokens here
36-
3731
error: expected expression, found `)`
3832
--> $DIR/token-error-correct.rs:23:1
3933
|
4034
23 | }
4135
| ^
4236

43-
error[E0425]: cannot find function `foo` in this scope
44-
--> $DIR/token-error-correct.rs:14:5
45-
|
46-
14 | foo(bar(;
47-
| ^^^ not found in this scope
48-
49-
error[E0425]: cannot find function `bar` in this scope
50-
--> $DIR/token-error-correct.rs:14:9
51-
|
52-
14 | foo(bar(;
53-
| ^^^ not found in this scope
54-
55-
error: aborting due to 7 previous errors
37+
error: aborting due to 4 previous errors
5638

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
let x: Vec::with_capacity(10, 20);
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: expected type, found `10`
2+
--> $DIR/type-ascription-instead-of-initializer.rs:12:31
3+
|
4+
12 | let x: Vec::with_capacity(10, 20);
5+
| -- ^^
6+
| ||
7+
| |help: use `=` if you meant to assign
8+
| while parsing the type for `x`
9+
10+
error[E0061]: this function takes 1 parameter but 2 parameters were supplied
11+
--> $DIR/type-ascription-instead-of-initializer.rs:12:31
12+
|
13+
12 | let x: Vec::with_capacity(10, 20);
14+
| ^^^^^^ expected 1 parameter
15+
16+
error: aborting due to 2 previous errors
17+

0 commit comments

Comments
 (0)