Auto merge of #31065 - nrc:ident-correct, r=pnkfelix
This PR adds some minor error correction to the parser - if there is a missing ident, we recover and carry on. It also makes compilation more robust so that non-fatal errors (which is still most of them, unfortunately) in parsing do not cause us to abort compilation. The effect is that a program with a missing or incorrect ident can get all the way to type checking.
This commit is contained in:
@@ -555,6 +555,9 @@ impl Handler {
|
||||
pub enum Level {
|
||||
Bug,
|
||||
Fatal,
|
||||
// An error which while not immediately fatal, should stop the compiler
|
||||
// progressing beyond the current phase.
|
||||
PhaseFatal,
|
||||
Error,
|
||||
Warning,
|
||||
Note,
|
||||
@@ -573,7 +576,7 @@ impl fmt::Display for Level {
|
||||
impl Level {
|
||||
fn color(self) -> term::color::Color {
|
||||
match self {
|
||||
Bug | Fatal | Error => term::color::BRIGHT_RED,
|
||||
Bug | Fatal | PhaseFatal | Error => term::color::BRIGHT_RED,
|
||||
Warning => term::color::BRIGHT_YELLOW,
|
||||
Note => term::color::BRIGHT_GREEN,
|
||||
Help => term::color::BRIGHT_CYAN,
|
||||
@@ -584,7 +587,7 @@ impl Level {
|
||||
fn to_str(self) -> &'static str {
|
||||
match self {
|
||||
Bug => "error: internal compiler error",
|
||||
Fatal | Error => "error",
|
||||
Fatal | PhaseFatal | Error => "error",
|
||||
Warning => "warning",
|
||||
Note => "note",
|
||||
Help => "help",
|
||||
|
||||
@@ -1304,9 +1304,14 @@ pub fn expand_crate(mut cx: ExtCtxt,
|
||||
expander.cx.syntax_env.insert(name, extension);
|
||||
}
|
||||
|
||||
let err_count = cx.parse_sess.span_diagnostic.err_count();
|
||||
let mut ret = expander.fold_crate(c);
|
||||
ret.exported_macros = expander.cx.exported_macros.clone();
|
||||
cx.parse_sess.span_diagnostic.abort_if_errors();
|
||||
|
||||
if cx.parse_sess.span_diagnostic.err_count() > err_count {
|
||||
cx.parse_sess.span_diagnostic.abort_if_errors();
|
||||
}
|
||||
|
||||
ret
|
||||
};
|
||||
return (ret, cx.syntax_env.names);
|
||||
|
||||
@@ -98,7 +98,7 @@ pub fn parse_crate_from_source_str(name: String,
|
||||
cfg,
|
||||
name,
|
||||
source);
|
||||
maybe_aborted(panictry!(p.parse_crate_mod()),p)
|
||||
panictry!(p.parse_crate_mod())
|
||||
}
|
||||
|
||||
pub fn parse_crate_attrs_from_source_str(name: String,
|
||||
@@ -110,7 +110,7 @@ pub fn parse_crate_attrs_from_source_str(name: String,
|
||||
cfg,
|
||||
name,
|
||||
source);
|
||||
maybe_aborted(panictry!(p.parse_inner_attributes()), p)
|
||||
panictry!(p.parse_inner_attributes())
|
||||
}
|
||||
|
||||
pub fn parse_expr_from_source_str(name: String,
|
||||
@@ -119,7 +119,7 @@ pub fn parse_expr_from_source_str(name: String,
|
||||
sess: &ParseSess)
|
||||
-> P<ast::Expr> {
|
||||
let mut p = new_parser_from_source_str(sess, cfg, name, source);
|
||||
maybe_aborted(panictry!(p.parse_expr()), p)
|
||||
panictry!(p.parse_expr())
|
||||
}
|
||||
|
||||
pub fn parse_item_from_source_str(name: String,
|
||||
@@ -128,7 +128,7 @@ pub fn parse_item_from_source_str(name: String,
|
||||
sess: &ParseSess)
|
||||
-> Option<P<ast::Item>> {
|
||||
let mut p = new_parser_from_source_str(sess, cfg, name, source);
|
||||
maybe_aborted(panictry!(p.parse_item()), p)
|
||||
panictry!(p.parse_item())
|
||||
}
|
||||
|
||||
pub fn parse_meta_from_source_str(name: String,
|
||||
@@ -137,7 +137,7 @@ pub fn parse_meta_from_source_str(name: String,
|
||||
sess: &ParseSess)
|
||||
-> P<ast::MetaItem> {
|
||||
let mut p = new_parser_from_source_str(sess, cfg, name, source);
|
||||
maybe_aborted(panictry!(p.parse_meta_item()), p)
|
||||
panictry!(p.parse_meta_item())
|
||||
}
|
||||
|
||||
pub fn parse_stmt_from_source_str(name: String,
|
||||
@@ -151,7 +151,7 @@ pub fn parse_stmt_from_source_str(name: String,
|
||||
name,
|
||||
source
|
||||
);
|
||||
maybe_aborted(panictry!(p.parse_stmt()), p)
|
||||
panictry!(p.parse_stmt())
|
||||
}
|
||||
|
||||
// Warning: This parses with quote_depth > 0, which is not the default.
|
||||
@@ -168,7 +168,7 @@ pub fn parse_tts_from_source_str(name: String,
|
||||
);
|
||||
p.quote_depth += 1;
|
||||
// right now this is re-creating the token trees from ... token trees.
|
||||
maybe_aborted(panictry!(p.parse_all_token_trees()),p)
|
||||
panictry!(p.parse_all_token_trees())
|
||||
}
|
||||
|
||||
// Create a new parser from a source string
|
||||
@@ -265,16 +265,10 @@ pub fn tts_to_parser<'a>(sess: &'a ParseSess,
|
||||
p
|
||||
}
|
||||
|
||||
/// Abort if necessary
|
||||
pub fn maybe_aborted<T>(result: T, p: Parser) -> T {
|
||||
p.abort_if_errors();
|
||||
result
|
||||
}
|
||||
|
||||
fn abort_if_errors<'a, T>(result: PResult<'a, T>, p: &Parser) -> T {
|
||||
match result {
|
||||
Ok(c) => {
|
||||
p.abort_if_errors();
|
||||
c
|
||||
}
|
||||
Err(mut e) => {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user