-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Error correction for missing idents #31065
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
Changes from all commits
b1b6b33
0ac8915
585cf6f
f3b525f
ff009d1
43b3681
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2355,6 +2355,59 @@ impl<'a> Parser<'a> { | |
) | ||
} | ||
|
||
// Assuming we have just parsed `.foo` (i.e., a dot and an ident), continue | ||
// parsing into an expression. | ||
fn parse_dot_suffix(&mut self, | ||
ident: Ident, | ||
ident_span: Span, | ||
self_value: P<Expr>) | ||
-> PResult<'a, P<Expr>> { | ||
let (_, tys, bindings) = if self.eat(&token::ModSep) { | ||
try!(self.expect_lt()); | ||
try!(self.parse_generic_values_after_lt()) | ||
} else { | ||
(Vec::new(), Vec::new(), Vec::new()) | ||
}; | ||
|
||
if !bindings.is_empty() { | ||
let last_span = self.last_span; | ||
self.span_err(last_span, "type bindings are only permitted on trait paths"); | ||
} | ||
|
||
let lo = self_value.span.lo; | ||
|
||
Ok(match self.token { | ||
// expr.f() method call. | ||
token::OpenDelim(token::Paren) => { | ||
let mut es = try!(self.parse_unspanned_seq( | ||
&token::OpenDelim(token::Paren), | ||
&token::CloseDelim(token::Paren), | ||
seq_sep_trailing_allowed(token::Comma), | ||
|p| Ok(try!(p.parse_expr())) | ||
)); | ||
let hi = self.last_span.hi; | ||
|
||
es.insert(0, self_value); | ||
let id = spanned(ident_span.lo, ident_span.hi, ident); | ||
let nd = self.mk_method_call(id, tys, es); | ||
self.mk_expr(lo, hi, nd, None) | ||
} | ||
// Field access. | ||
_ => { | ||
if !tys.is_empty() { | ||
let last_span = self.last_span; | ||
self.span_err(last_span, | ||
"field expressions may not \ | ||
have type parameters"); | ||
} | ||
|
||
let id = spanned(ident_span.lo, ident_span.hi, ident); | ||
let field = self.mk_field(self_value, id); | ||
self.mk_expr(lo, ident_span.hi, field, None) | ||
} | ||
}) | ||
} | ||
|
||
fn parse_dot_or_call_expr_with_(&mut self, e0: P<Expr>) -> PResult<'a, P<Expr>> { | ||
let mut e = e0; | ||
let lo = e.span.lo; | ||
|
@@ -2364,50 +2417,11 @@ impl<'a> Parser<'a> { | |
if self.eat(&token::Dot) { | ||
match self.token { | ||
token::Ident(i, _) => { | ||
let dot = self.last_span.hi; | ||
let dot_pos = self.last_span.hi; | ||
hi = self.span.hi; | ||
self.bump(); | ||
let (_, tys, bindings) = if self.eat(&token::ModSep) { | ||
try!(self.expect_lt()); | ||
try!(self.parse_generic_values_after_lt()) | ||
} else { | ||
(Vec::new(), Vec::new(), Vec::new()) | ||
}; | ||
|
||
if !bindings.is_empty() { | ||
let last_span = self.last_span; | ||
self.span_err(last_span, "type bindings are only permitted on trait paths"); | ||
} | ||
|
||
// expr.f() method call | ||
match self.token { | ||
token::OpenDelim(token::Paren) => { | ||
let mut es = try!(self.parse_unspanned_seq( | ||
&token::OpenDelim(token::Paren), | ||
&token::CloseDelim(token::Paren), | ||
seq_sep_trailing_allowed(token::Comma), | ||
|p| Ok(try!(p.parse_expr())) | ||
)); | ||
hi = self.last_span.hi; | ||
|
||
es.insert(0, e); | ||
let id = spanned(dot, hi, i); | ||
let nd = self.mk_method_call(id, tys, es); | ||
e = self.mk_expr(lo, hi, nd, None); | ||
} | ||
_ => { | ||
if !tys.is_empty() { | ||
let last_span = self.last_span; | ||
self.span_err(last_span, | ||
"field expressions may not \ | ||
have type parameters"); | ||
} | ||
|
||
let id = spanned(dot, hi, i); | ||
let field = self.mk_field(e, id); | ||
e = self.mk_expr(lo, hi, field, None); | ||
} | ||
} | ||
e = try!(self.parse_dot_suffix(i, mk_sp(dot_pos, hi), e)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yay refactoring |
||
} | ||
token::Literal(token::Integer(n), suf) => { | ||
let sp = self.span; | ||
|
@@ -2452,7 +2466,16 @@ impl<'a> Parser<'a> { | |
self.abort_if_errors(); | ||
|
||
} | ||
_ => return self.unexpected() | ||
_ => { | ||
// FIXME Could factor this out into non_fatal_unexpected or something. | ||
let actual = self.this_token_to_string(); | ||
self.span_err(self.span, &format!("unexpected token: `{}`", actual)); | ||
|
||
let dot_pos = self.last_span.hi; | ||
e = try!(self.parse_dot_suffix(special_idents::invalid, | ||
mk_sp(dot_pos, dot_pos), | ||
e)); | ||
} | ||
} | ||
continue; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,5 +20,5 @@ mod foo { | |
} | ||
|
||
fn main() { | ||
let _ = foo::X; | ||
let _ = foo::X; //~ ERROR unresolved name `foo::X` | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,3 +16,4 @@ mod spam { | |
} | ||
|
||
fn main() { ham(); eggs(); } | ||
//~^ ERROR unresolved name `eggs` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm I'm trying to decide if I'll be annoyed by this message ("what are you talking about, I clearly tried to import it above!"), or if I will be happy that it is there. I guess our user base should know enough to read and address the compiler error messages in order from top down... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I was thinking the same thing. I think we do want to avoid this and I plan to file an issue once this PR lands |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Copyright 2016 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. | ||
|
||
// Test that the parser is error correcting missing idents. Despite a parsing | ||
// error (or two), we still run type checking (and don't get extra errors there). | ||
|
||
fn main() { | ||
let y = 42; | ||
let x = y.; //~ ERROR unexpected token | ||
let x = y.(); //~ ERROR unexpected token | ||
let x = y.foo; //~ ERROR no field | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. okay this is impressive. :) |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know the following suggestion belonged with PR #30320, but: Can you rename
fn abort_if_new_errors
to make it clearer what its relationship with the callback is?In particular, when I see an invocation like
sess.abort_if_new_errors(|| { stuff ... })
, especially if my main experience is with the code base prior to PR #30320, my first thought is "this will somehow abort if we've seen new errors since the last time I invoked it, and it will also call this callback as part of the aborting process."(That is of course not what is happening at all.)
The function you have here is more like a
try_and_abort_on_new_errors(|| { stuff ... })
, which I admit is verbose.Anyway I won't r- the PR even if this suggestion is ignored, but I would appreciate if you consider the idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(and the other problem with
fn try_anything
is that it may imply to some thattry!
orResult
is somehow involved, which really isn't case here. I'd be okay withfn call_and_abort_on_new_errors
or something along those lines.)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have another PR which removes a bunch of
abort_if_new_errors
calls, and plan to do more there. So, I'll do a rename, but later, if that is OK.