Rollup merge of #32435 - nrc:fix-err-recover, r=nikomatsakis
Some fixes for error recovery in the compiler
This commit is contained in:
commit
b55d7729c2
|
@ -268,8 +268,8 @@ pub struct Parser<'a> {
|
||||||
/// Used to determine the path to externally loaded source files
|
/// Used to determine the path to externally loaded source files
|
||||||
pub filename: Option<String>,
|
pub filename: Option<String>,
|
||||||
pub mod_path_stack: Vec<InternedString>,
|
pub mod_path_stack: Vec<InternedString>,
|
||||||
/// Stack of spans of open delimiters. Used for error message.
|
/// Stack of open delimiters and their spans. Used for error message.
|
||||||
pub open_braces: Vec<Span>,
|
pub open_braces: Vec<(token::DelimToken, Span)>,
|
||||||
/// Flag if this parser "owns" the directory that it is currently parsing
|
/// Flag if this parser "owns" the directory that it is currently parsing
|
||||||
/// in. This will affect how nested files are looked up.
|
/// in. This will affect how nested files are looked up.
|
||||||
pub owns_directory: bool,
|
pub owns_directory: bool,
|
||||||
|
@ -895,7 +895,7 @@ impl<'a> Parser<'a> {
|
||||||
sep: SeqSep,
|
sep: SeqSep,
|
||||||
f: F)
|
f: F)
|
||||||
-> Vec<T>
|
-> 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, f, |mut e| e.emit())
|
self.parse_seq_to_before_tokens(&[ket], sep, f, |mut e| e.emit())
|
||||||
}
|
}
|
||||||
|
@ -2755,8 +2755,8 @@ impl<'a> Parser<'a> {
|
||||||
let mut err: DiagnosticBuilder<'a> =
|
let mut err: DiagnosticBuilder<'a> =
|
||||||
self.diagnostic().struct_span_err(self.span,
|
self.diagnostic().struct_span_err(self.span,
|
||||||
"this file contains an un-closed delimiter");
|
"this file contains an un-closed delimiter");
|
||||||
for sp in &self.open_braces {
|
for &(_, sp) in &self.open_braces {
|
||||||
err.span_help(*sp, "did you mean to close this delimiter?");
|
err.span_help(sp, "did you mean to close this delimiter?");
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(err)
|
Err(err)
|
||||||
|
@ -2766,23 +2766,66 @@ impl<'a> Parser<'a> {
|
||||||
let pre_span = self.span;
|
let pre_span = self.span;
|
||||||
|
|
||||||
// Parse the open delimiter.
|
// Parse the open delimiter.
|
||||||
self.open_braces.push(self.span);
|
self.open_braces.push((delim, self.span));
|
||||||
let open_span = self.span;
|
let open_span = self.span;
|
||||||
self.bump();
|
self.bump();
|
||||||
|
|
||||||
// Parse the token trees within the delimiters
|
// Parse the token trees within the delimiters.
|
||||||
let tts = self.parse_seq_to_before_end(&token::CloseDelim(delim),
|
// We stop at any delimiter so we can try to recover if the user
|
||||||
SeqSep::none(),
|
// uses an incorrect delimiter.
|
||||||
|p| p.parse_token_tree());
|
let tts = self.parse_seq_to_before_tokens(&[&token::CloseDelim(token::Brace),
|
||||||
|
&token::CloseDelim(token::Paren),
|
||||||
|
&token::CloseDelim(token::Bracket)],
|
||||||
|
SeqSep::none(),
|
||||||
|
|p| p.parse_token_tree(),
|
||||||
|
|mut e| e.emit());
|
||||||
|
|
||||||
// Parse the close delimiter.
|
|
||||||
let close_span = self.span;
|
let close_span = self.span;
|
||||||
self.bump();
|
|
||||||
self.open_braces.pop().unwrap();
|
|
||||||
|
|
||||||
// Expand to cover the entire delimited token tree
|
// Expand to cover the entire delimited token tree
|
||||||
let span = Span { hi: close_span.hi, ..pre_span };
|
let span = Span { hi: close_span.hi, ..pre_span };
|
||||||
|
|
||||||
|
match self.token {
|
||||||
|
// Correct delmiter.
|
||||||
|
token::CloseDelim(d) if d == delim => {
|
||||||
|
self.open_braces.pop().unwrap();
|
||||||
|
|
||||||
|
// Parse the close delimiter.
|
||||||
|
self.bump();
|
||||||
|
}
|
||||||
|
// Incorect delimiter.
|
||||||
|
token::CloseDelim(other) => {
|
||||||
|
let token_str = self.this_token_to_string();
|
||||||
|
let mut err = self.diagnostic().struct_span_err(self.span,
|
||||||
|
&format!("incorrect close delimiter: `{}`", token_str));
|
||||||
|
// This is a conservative error: only report the last unclosed delimiter.
|
||||||
|
// The previous unclosed delimiters could actually be closed! The parser
|
||||||
|
// just hasn't gotten to them yet.
|
||||||
|
if let Some(&(_, sp)) = self.open_braces.last() {
|
||||||
|
err.span_note(sp, "unclosed delimiter");
|
||||||
|
};
|
||||||
|
err.emit();
|
||||||
|
|
||||||
|
self.open_braces.pop().unwrap();
|
||||||
|
|
||||||
|
// If the incorrect delimter matches an earlier opening
|
||||||
|
// delimiter, then don't consume it (it can be used to
|
||||||
|
// close the earlier one)Otherwise, consume it.
|
||||||
|
// E.g., we try to recover from:
|
||||||
|
// fn foo() {
|
||||||
|
// bar(baz(
|
||||||
|
// } // Incorrect delimiter but matches the earlier `{`
|
||||||
|
if !self.open_braces.iter().any(|&(b, _)| b == other) {
|
||||||
|
self.bump();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
token::Eof => {
|
||||||
|
// Silently recover, the EOF token will be seen again
|
||||||
|
// and an error emitted then. Thus we don't pop from
|
||||||
|
// self.open_braces here.
|
||||||
|
},
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
Ok(TokenTree::Delimited(span, Rc::new(Delimited {
|
Ok(TokenTree::Delimited(span, Rc::new(Delimited {
|
||||||
delim: delim,
|
delim: delim,
|
||||||
open_span: open_span,
|
open_span: open_span,
|
||||||
|
@ -2798,16 +2841,11 @@ impl<'a> Parser<'a> {
|
||||||
maybe_whole!(deref self, NtTT);
|
maybe_whole!(deref self, NtTT);
|
||||||
match self.token {
|
match self.token {
|
||||||
token::CloseDelim(_) => {
|
token::CloseDelim(_) => {
|
||||||
|
// An unexpected closing delimiter (i.e., there is no
|
||||||
|
// matching opening delimiter).
|
||||||
let token_str = self.this_token_to_string();
|
let token_str = self.this_token_to_string();
|
||||||
let mut err = self.diagnostic().struct_span_err(self.span,
|
let err = self.diagnostic().struct_span_err(self.span,
|
||||||
&format!("incorrect close delimiter: `{}`", token_str));
|
&format!("unexpected close delimiter: `{}`", token_str));
|
||||||
// This is a conservative error: only report the last unclosed delimiter.
|
|
||||||
// The previous unclosed delimiters could actually be closed! The parser
|
|
||||||
// just hasn't gotten to them yet.
|
|
||||||
if let Some(&sp) = self.open_braces.last() {
|
|
||||||
err.span_note(sp, "unclosed delimiter");
|
|
||||||
};
|
|
||||||
|
|
||||||
Err(err)
|
Err(err)
|
||||||
},
|
},
|
||||||
/* we ought to allow different depths of unquotation */
|
/* we ought to allow different depths of unquotation */
|
||||||
|
@ -3825,7 +3863,9 @@ impl<'a> Parser<'a> {
|
||||||
fn recover_stmt_(&mut self, break_on_semi: SemiColonMode) {
|
fn recover_stmt_(&mut self, break_on_semi: SemiColonMode) {
|
||||||
let mut brace_depth = 0;
|
let mut brace_depth = 0;
|
||||||
let mut bracket_depth = 0;
|
let mut bracket_depth = 0;
|
||||||
|
debug!("recover_stmt_ enter loop");
|
||||||
loop {
|
loop {
|
||||||
|
debug!("recover_stmt_ loop {:?}", self.token);
|
||||||
match self.token {
|
match self.token {
|
||||||
token::OpenDelim(token::DelimToken::Brace) => {
|
token::OpenDelim(token::DelimToken::Brace) => {
|
||||||
brace_depth += 1;
|
brace_depth += 1;
|
||||||
|
@ -3837,6 +3877,7 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
token::CloseDelim(token::DelimToken::Brace) => {
|
token::CloseDelim(token::DelimToken::Brace) => {
|
||||||
if brace_depth == 0 {
|
if brace_depth == 0 {
|
||||||
|
debug!("recover_stmt_ return - close delim {:?}", self.token);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
brace_depth -= 1;
|
brace_depth -= 1;
|
||||||
|
@ -3849,12 +3890,16 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
self.bump();
|
self.bump();
|
||||||
}
|
}
|
||||||
token::Eof => return,
|
token::Eof => {
|
||||||
|
debug!("recover_stmt_ return - Eof");
|
||||||
|
return;
|
||||||
|
}
|
||||||
token::Semi => {
|
token::Semi => {
|
||||||
self.bump();
|
self.bump();
|
||||||
if break_on_semi == SemiColonMode::Break &&
|
if break_on_semi == SemiColonMode::Break &&
|
||||||
brace_depth == 0 &&
|
brace_depth == 0 &&
|
||||||
bracket_depth == 0 {
|
bracket_depth == 0 {
|
||||||
|
debug!("recover_stmt_ return - Semi");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4043,6 +4088,8 @@ impl<'a> Parser<'a> {
|
||||||
while !self.eat(&token::CloseDelim(token::Brace)) {
|
while !self.eat(&token::CloseDelim(token::Brace)) {
|
||||||
let Spanned {node, span} = if let Some(s) = self.parse_stmt_() {
|
let Spanned {node, span} = if let Some(s) = self.parse_stmt_() {
|
||||||
s
|
s
|
||||||
|
} else if self.token == token::Eof {
|
||||||
|
break;
|
||||||
} else {
|
} else {
|
||||||
// Found only `;` or `}`.
|
// Found only `;` or `}`.
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
// Copyright 2013-2016 The Rust Project Developers. See the COPYRIGHT
|
||||||
// file at the top-level directory of this distribution and at
|
// file at the top-level directory of this distribution and at
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
//
|
//
|
||||||
|
@ -11,11 +11,9 @@
|
||||||
// FIXME(31528) we emit a bunch of silly errors here due to continuing past the
|
// FIXME(31528) we emit a bunch of silly errors here due to continuing past the
|
||||||
// first one. This would be easy-ish to address by better recovery in tokenisation.
|
// first one. This would be easy-ish to address by better recovery in tokenisation.
|
||||||
|
|
||||||
// compile-flags: -Z parse-only
|
pub fn trace_option(option: Option<isize>) {
|
||||||
|
|
||||||
pub fn trace_option(option: Option<isize>) { //~ HELP did you mean to close this delimiter?
|
|
||||||
option.map(|some| 42; //~ NOTE: unclosed delimiter
|
option.map(|some| 42; //~ NOTE: unclosed delimiter
|
||||||
//~^ ERROR: expected one of
|
//~^ ERROR: expected one of
|
||||||
|
//~^^ ERROR: mismatched types
|
||||||
} //~ ERROR: incorrect close delimiter
|
} //~ ERROR: incorrect close delimiter
|
||||||
//~^ ERROR: expected one of
|
//~^ ERROR: expected one of
|
||||||
//~ ERROR: this file contains an un-closed delimiter
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
// 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 error recovery in the parser to an EOF does not give an infinite
|
||||||
|
// spew of errors.
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let
|
||||||
|
} //~ ERROR unexpected token: `}`
|
|
@ -0,0 +1,17 @@
|
||||||
|
// 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 we do some basic error correcton in the tokeniser (and don't ICE).
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if foo { //~ NOTE: unclosed delimiter
|
||||||
|
//~^ ERROR: unresolved name `foo`
|
||||||
|
) //~ ERROR: incorrect close delimiter: `)`
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
// 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 we do some basic error correcton in the tokeniser (and don't spew
|
||||||
|
// too many bogus errors).
|
||||||
|
|
||||||
|
pub mod raw {
|
||||||
|
use std::{io, fs};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub fn ensure_dir_exists<P: AsRef<Path>, F: FnOnce(&Path)>(path: P,
|
||||||
|
callback: F)
|
||||||
|
-> io::Result<bool> {
|
||||||
|
if !is_directory(path.as_ref()) { //~ ERROR: unresolved name `is_directory`
|
||||||
|
callback(path.as_ref(); //~ NOTE: unclosed delimiter
|
||||||
|
//~^ ERROR: expected one of
|
||||||
|
fs::create_dir_all(path.as_ref()).map(|()| true) //~ ERROR: expected one of
|
||||||
|
} else { //~ ERROR: incorrect close delimiter: `}`
|
||||||
|
Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,20 @@
|
||||||
|
// 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 we do some basic error correcton in the tokeniser.
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
foo(bar(; //~ NOTE: unclosed delimiter
|
||||||
|
//~^ NOTE: unclosed delimiter
|
||||||
|
//~^^ ERROR: unexpected token: `;`
|
||||||
|
//~^^^ ERROR: unresolved name `bar`
|
||||||
|
//~^^^^ ERROR: unresolved name `foo`
|
||||||
|
} //~ ERROR: incorrect close delimiter: `}`
|
||||||
|
//~^ ERROR: incorrect close delimiter: `}`
|
|
@ -10,4 +10,4 @@
|
||||||
|
|
||||||
// compile-flags: -Z parse-only
|
// compile-flags: -Z parse-only
|
||||||
|
|
||||||
static foo: isize = 2; } //~ ERROR incorrect close delimiter:
|
static foo: isize = 2; } //~ ERROR unexpected close delimiter:
|
||||||
|
|
|
@ -14,4 +14,4 @@ fn main() {
|
||||||
foo! (
|
foo! (
|
||||||
bar, "baz", 1, 2.0
|
bar, "baz", 1, 2.0
|
||||||
} //~ ERROR incorrect close delimiter
|
} //~ ERROR incorrect close delimiter
|
||||||
}
|
} //~ ERROR unexpected close delimiter: `}`
|
||||||
|
|
Loading…
Reference in New Issue