Skip to content

Commit 2731dc1

Browse files
committed
Error recovery in the tokeniser
Closes #31994
1 parent 3ee841c commit 2731dc1

File tree

1 file changed

+58
-25
lines changed

1 file changed

+58
-25
lines changed

src/libsyntax/parse/parser.rs

+58-25
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,8 @@ pub struct Parser<'a> {
268268
/// Used to determine the path to externally loaded source files
269269
pub filename: Option<String>,
270270
pub mod_path_stack: Vec<InternedString>,
271-
/// Stack of spans of open delimiters. Used for error message.
272-
pub open_braces: Vec<Span>,
271+
/// Stack of open delimiters and their spans. Used for error message.
272+
pub open_braces: Vec<(token::DelimToken, Span)>,
273273
/// Flag if this parser "owns" the directory that it is currently parsing
274274
/// in. This will affect how nested files are looked up.
275275
pub owns_directory: bool,
@@ -895,7 +895,7 @@ impl<'a> Parser<'a> {
895895
sep: SeqSep,
896896
f: F)
897897
-> Vec<T>
898-
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
898+
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>
899899
{
900900
self.parse_seq_to_before_tokens(&[ket], sep, f, |mut e| e.emit())
901901
}
@@ -2755,8 +2755,8 @@ impl<'a> Parser<'a> {
27552755
let mut err: DiagnosticBuilder<'a> =
27562756
self.diagnostic().struct_span_err(self.span,
27572757
"this file contains an un-closed delimiter");
2758-
for sp in &self.open_braces {
2759-
err.span_help(*sp, "did you mean to close this delimiter?");
2758+
for &(_, sp) in &self.open_braces {
2759+
err.span_help(sp, "did you mean to close this delimiter?");
27602760
}
27612761

27622762
Err(err)
@@ -2766,23 +2766,66 @@ impl<'a> Parser<'a> {
27662766
let pre_span = self.span;
27672767

27682768
// Parse the open delimiter.
2769-
self.open_braces.push(self.span);
2769+
self.open_braces.push((delim, self.span));
27702770
let open_span = self.span;
27712771
self.bump();
27722772

2773-
// Parse the token trees within the delimiters
2774-
let tts = self.parse_seq_to_before_end(&token::CloseDelim(delim),
2775-
SeqSep::none(),
2776-
|p| p.parse_token_tree());
2773+
// Parse the token trees within the delimiters.
2774+
// We stop at any delimiter so we can try to recover if the user
2775+
// uses an incorrect delimiter.
2776+
let tts = self.parse_seq_to_before_tokens(&[&token::CloseDelim(token::Brace),
2777+
&token::CloseDelim(token::Paren),
2778+
&token::CloseDelim(token::Bracket)],
2779+
SeqSep::none(),
2780+
|p| p.parse_token_tree(),
2781+
|mut e| e.emit());
27772782

2778-
// Parse the close delimiter.
27792783
let close_span = self.span;
2780-
self.bump();
2781-
self.open_braces.pop().unwrap();
2782-
27832784
// Expand to cover the entire delimited token tree
27842785
let span = Span { hi: close_span.hi, ..pre_span };
27852786

2787+
match self.token {
2788+
// Correct delmiter.
2789+
token::CloseDelim(d) if d == delim => {
2790+
self.open_braces.pop().unwrap();
2791+
2792+
// Parse the close delimiter.
2793+
self.bump();
2794+
}
2795+
// Incorect delimiter.
2796+
token::CloseDelim(other) => {
2797+
let token_str = self.this_token_to_string();
2798+
let mut err = self.diagnostic().struct_span_err(self.span,
2799+
&format!("incorrect close delimiter: `{}`", token_str));
2800+
// This is a conservative error: only report the last unclosed delimiter.
2801+
// The previous unclosed delimiters could actually be closed! The parser
2802+
// just hasn't gotten to them yet.
2803+
if let Some(&(_, sp)) = self.open_braces.last() {
2804+
err.span_note(sp, "unclosed delimiter");
2805+
};
2806+
err.emit();
2807+
2808+
self.open_braces.pop().unwrap();
2809+
2810+
// If the incorrect delimter matches an earlier opening
2811+
// delimiter, then don't consume it (it can be used to
2812+
// close the earlier one)Otherwise, consume it.
2813+
// E.g., we try to recover from:
2814+
// fn foo() {
2815+
// bar(baz(
2816+
// } // Incorrect delimiter but matches the earlier `{`
2817+
if !self.open_braces.iter().any(|&(b, _)| b == other) {
2818+
self.bump();
2819+
}
2820+
}
2821+
token::Eof => {
2822+
// Silently recover, the EOF token will be seen again
2823+
// and an error emitted then. Thus we don't pop from
2824+
// self.open_braces here.
2825+
},
2826+
_ => unreachable!(),
2827+
}
2828+
27862829
Ok(TokenTree::Delimited(span, Rc::new(Delimited {
27872830
delim: delim,
27882831
open_span: open_span,
@@ -2798,17 +2841,7 @@ impl<'a> Parser<'a> {
27982841
maybe_whole!(deref self, NtTT);
27992842
match self.token {
28002843
token::CloseDelim(_) => {
2801-
let token_str = self.this_token_to_string();
2802-
let mut err = self.diagnostic().struct_span_err(self.span,
2803-
&format!("incorrect close delimiter: `{}`", token_str));
2804-
// This is a conservative error: only report the last unclosed delimiter.
2805-
// The previous unclosed delimiters could actually be closed! The parser
2806-
// just hasn't gotten to them yet.
2807-
if let Some(&sp) = self.open_braces.last() {
2808-
err.span_note(sp, "unclosed delimiter");
2809-
};
2810-
2811-
Err(err)
2844+
panic!("should have been caught above");
28122845
},
28132846
/* we ought to allow different depths of unquotation */
28142847
token::Dollar | token::SubstNt(..) if self.quote_depth > 0 => {

0 commit comments

Comments
 (0)