Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect = -> : typo in let bindings #45452

Merged
merged 1 commit into from
Nov 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2597,7 +2597,7 @@ impl<'a> Resolver<'a> {
}
}
err.span_label(span, format!("did you mean `{} {{ /* fields */ }}`?",
path_str));
path_str));
return (err, candidates);
}
_ => {}
Expand Down
132 changes: 88 additions & 44 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -974,11 +974,12 @@ impl<'a> Parser<'a> {
pub fn eat_to_tokens(&mut self, kets: &[&token::Token]) {
let handler = self.diagnostic();

self.parse_seq_to_before_tokens(kets,
SeqSep::none(),
TokenExpectType::Expect,
|p| Ok(p.parse_token_tree()),
|mut e| handler.cancel(&mut e));
if let Err(ref mut err) = self.parse_seq_to_before_tokens(kets,
SeqSep::none(),
TokenExpectType::Expect,
|p| Ok(p.parse_token_tree())) {
handler.cancel(err);
}
}

/// Parse a sequence, including the closing delimiter. The function
Expand All @@ -991,7 +992,7 @@ impl<'a> Parser<'a> {
-> PResult<'a, Vec<T>> where
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();
Ok(val)
}
Expand All @@ -1003,22 +1004,19 @@ impl<'a> Parser<'a> {
ket: &token::Token,
sep: SeqSep,
f: F)
-> Vec<T>
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>
-> PResult<'a, Vec<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, Fe>(&mut self,
fn parse_seq_to_before_tokens<T, F>(&mut self,
kets: &[&token::Token],
sep: SeqSep,
expect: TokenExpectType,
mut f: F,
mut fe: Fe)
-> Vec<T>
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
Fe: FnMut(DiagnosticBuilder)
mut f: F)
-> PResult<'a, Vec<T>>
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>
{
let mut first: bool = true;
let mut v = vec![];
Expand All @@ -1031,14 +1029,14 @@ impl<'a> Parser<'a> {
if first {
first = false;
} else {
if let Err(e) = self.expect(t) {
fe(e);
if let Err(mut e) = self.expect(t) {
// Attempt to keep parsing if it was a similar separator
if let Some(ref tokens) = t.similar_tokens() {
if tokens.contains(&self.token) {
self.bump();
}
}
e.emit();
// Attempt to keep parsing if it was an omitted separator
match f(self) {
Ok(t) => {
Expand All @@ -1062,16 +1060,11 @@ impl<'a> Parser<'a> {
break;
}

match f(self) {
Ok(t) => v.push(t),
Err(e) => {
fe(e);
break;
}
}
let t = f(self)?;
v.push(t);
}

v
Ok(v)
}

/// Parse a sequence, including the closing delimiter. The function
Expand All @@ -1086,7 +1079,7 @@ impl<'a> Parser<'a> {
F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
{
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 {
self.bump();
}
Expand All @@ -1105,7 +1098,7 @@ impl<'a> Parser<'a> {
{
let lo = self.span;
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;
self.bump();
Ok(respan(lo.to(hi), result))
Expand Down Expand Up @@ -1551,7 +1544,7 @@ impl<'a> Parser<'a> {
};

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.
self.maybe_recover_from_bad_type_plus(allow_plus, &ty)?;
Expand Down Expand Up @@ -1868,8 +1861,11 @@ impl<'a> Parser<'a> {
self.parse_path(style)
}

fn parse_path_segments(&mut self, segments: &mut Vec<PathSegment>, style: PathStyle,
enable_warning: bool) -> PResult<'a, ()> {
fn parse_path_segments(&mut self,
segments: &mut Vec<PathSegment>,
style: PathStyle,
enable_warning: bool)
-> PResult<'a, ()> {
loop {
segments.push(self.parse_path_segment(style, enable_warning)?);

Expand Down Expand Up @@ -1914,9 +1910,12 @@ impl<'a> Parser<'a> {
} else {
// `(T, U) -> R`
self.bump(); // `(`
let inputs = self.parse_seq_to_end(&token::CloseDelim(token::Paren),
SeqSep::trailing_allowed(token::Comma),
|p| p.parse_ty())?;
let inputs = self.parse_seq_to_before_tokens(
&[&token::CloseDelim(token::Paren)],
SeqSep::trailing_allowed(token::Comma),
TokenExpectType::Expect,
|p| p.parse_ty())?;
self.bump(); // `)`
let output = if self.eat(&token::RArrow) {
Some(self.parse_ty_no_plus()?)
} else {
Expand Down Expand Up @@ -3309,10 +3308,12 @@ impl<'a> Parser<'a> {
}

/// 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) {
self.bump();
Ok(Some(self.parse_expr()?))
} else if skip_eq {
Ok(Some(self.parse_expr()?))
} else {
Ok(None)
}
Expand Down Expand Up @@ -3719,12 +3720,56 @@ impl<'a> Parser<'a> {
let lo = self.prev_span;
let pat = self.parse_pat()?;

let ty = if self.eat(&token::Colon) {
Some(self.parse_ty()?)
let (err, ty) = if self.eat(&token::Colon) {
// 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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this something we do elsewhere? Maybe we should find some way to make it more obvious that people might be cloning the parser like this? It sort of surprises me, but I think it makes sense, and I can't see any obvious reasons it will cause problems.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the first time something like this was introduced to the parser was in #42578 to verify wether a as usize < b could be parsed and if it could, emit a suggestion for the correct code (a as usize) < b. It might have been used elsewhere since. I thought about adding a method to the parser but balked at the idea to not give the impression that we should use of this error recovery style everywhere.

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 {
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 {
self.span
} else {
Expand Down Expand Up @@ -4781,14 +4826,14 @@ impl<'a> Parser<'a> {
} else if self.eat(&token::Comma) {
let mut fn_inputs = vec![self_arg];
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
} else {
return self.unexpected();
}
} 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.
Expand All @@ -4811,9 +4856,8 @@ impl<'a> Parser<'a> {
&[&token::BinOp(token::Or), &token::OrOr],
SeqSep::trailing_allowed(token::Comma),
TokenExpectType::NoExpect,
|p| p.parse_fn_block_arg(),
|mut e| e.emit()
);
|p| p.parse_fn_block_arg()
)?;
self.expect_or()?;
args
}
Expand Down
1 change: 0 additions & 1 deletion src/test/compile-fail/self-vs-path-ambiguity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ impl S {
fn g(&self::S: &S) {}
fn h(&mut self::S: &mut S) {}
fn i(&'a self::S: &S) {} //~ ERROR unexpected lifetime `'a` in pattern
//~^ ERROR expected one of `)` or `mut`, found `'a`
}

fn main() {}
1 change: 0 additions & 1 deletion src/test/parse-fail/issue-33413.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,4 @@

impl S {
fn f(*, a: u8) -> u8 {} //~ ERROR expected pattern, found `*`
//~^ ERROR expected one of `)`, `-`, `box`, `false`, `mut`, `ref`, or `true`, found `*`
}
11 changes: 1 addition & 10 deletions src/test/ui/issue-44406.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,5 @@ error: expected type, found keyword `true`
18 | foo!(true);
| ^^^^ expecting a type here because of type ascription

error: expected one of `!`, `&&`, `&`, `(`, `*`, `.`, `;`, `<`, `?`, `[`, `_`, `dyn`, `extern`, `fn`, `for`, `impl`, `unsafe`, `}`, an operator, or lifetime, found `true`
--> $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
error: aborting due to 2 previous errors

20 changes: 1 addition & 19 deletions src/test/ui/resolve/token-error-correct.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,11 @@ error: expected expression, found `;`
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 `)`
--> $DIR/token-error-correct.rs:23:1
|
23 | }
| ^

error[E0425]: cannot find function `foo` in this scope
--> $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
error: aborting due to 4 previous errors

13 changes: 13 additions & 0 deletions src/test/ui/suggestions/type-ascription-instead-of-initializer.rs
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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