Skip to content

Commit b55d772

Browse files
committed
Rollup merge of rust-lang#32435 - nrc:fix-err-recover, r=nikomatsakis
Some fixes for error recovery in the compiler
2 parents a8d59e0 + 180d6b5 commit b55d772

8 files changed

+162
-31
lines changed

src/libsyntax/parse/parser.rs

+71-24
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,16 +2841,11 @@ impl<'a> Parser<'a> {
27982841
maybe_whole!(deref self, NtTT);
27992842
match self.token {
28002843
token::CloseDelim(_) => {
2844+
// An unexpected closing delimiter (i.e., there is no
2845+
// matching opening delimiter).
28012846
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-
2847+
let err = self.diagnostic().struct_span_err(self.span,
2848+
&format!("unexpected close delimiter: `{}`", token_str));
28112849
Err(err)
28122850
},
28132851
/* we ought to allow different depths of unquotation */
@@ -3825,7 +3863,9 @@ impl<'a> Parser<'a> {
38253863
fn recover_stmt_(&mut self, break_on_semi: SemiColonMode) {
38263864
let mut brace_depth = 0;
38273865
let mut bracket_depth = 0;
3866+
debug!("recover_stmt_ enter loop");
38283867
loop {
3868+
debug!("recover_stmt_ loop {:?}", self.token);
38293869
match self.token {
38303870
token::OpenDelim(token::DelimToken::Brace) => {
38313871
brace_depth += 1;
@@ -3837,6 +3877,7 @@ impl<'a> Parser<'a> {
38373877
}
38383878
token::CloseDelim(token::DelimToken::Brace) => {
38393879
if brace_depth == 0 {
3880+
debug!("recover_stmt_ return - close delim {:?}", self.token);
38403881
return;
38413882
}
38423883
brace_depth -= 1;
@@ -3849,12 +3890,16 @@ impl<'a> Parser<'a> {
38493890
}
38503891
self.bump();
38513892
}
3852-
token::Eof => return,
3893+
token::Eof => {
3894+
debug!("recover_stmt_ return - Eof");
3895+
return;
3896+
}
38533897
token::Semi => {
38543898
self.bump();
38553899
if break_on_semi == SemiColonMode::Break &&
38563900
brace_depth == 0 &&
38573901
bracket_depth == 0 {
3902+
debug!("recover_stmt_ return - Semi");
38583903
return;
38593904
}
38603905
}
@@ -4043,6 +4088,8 @@ impl<'a> Parser<'a> {
40434088
while !self.eat(&token::CloseDelim(token::Brace)) {
40444089
let Spanned {node, span} = if let Some(s) = self.parse_stmt_() {
40454090
s
4091+
} else if self.token == token::Eof {
4092+
break;
40464093
} else {
40474094
// Found only `;` or `}`.
40484095
continue;
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2013-2016 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -11,11 +11,9 @@
1111
// FIXME(31528) we emit a bunch of silly errors here due to continuing past the
1212
// first one. This would be easy-ish to address by better recovery in tokenisation.
1313

14-
// compile-flags: -Z parse-only
15-
16-
pub fn trace_option(option: Option<isize>) { //~ HELP did you mean to close this delimiter?
14+
pub fn trace_option(option: Option<isize>) {
1715
option.map(|some| 42; //~ NOTE: unclosed delimiter
1816
//~^ ERROR: expected one of
17+
//~^^ ERROR: mismatched types
1918
} //~ ERROR: incorrect close delimiter
2019
//~^ ERROR: expected one of
21-
//~ ERROR: this file contains an un-closed delimiter

src/test/compile-fail/issue-31804.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that error recovery in the parser to an EOF does not give an infinite
12+
// spew of errors.
13+
14+
fn main() {
15+
let
16+
} //~ ERROR unexpected token: `}`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that we do some basic error correcton in the tokeniser (and don't ICE).
12+
13+
fn main() {
14+
if foo { //~ NOTE: unclosed delimiter
15+
//~^ ERROR: unresolved name `foo`
16+
) //~ ERROR: incorrect close delimiter: `)`
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that we do some basic error correcton in the tokeniser (and don't spew
12+
// too many bogus errors).
13+
14+
pub mod raw {
15+
use std::{io, fs};
16+
use std::path::Path;
17+
18+
pub fn ensure_dir_exists<P: AsRef<Path>, F: FnOnce(&Path)>(path: P,
19+
callback: F)
20+
-> io::Result<bool> {
21+
if !is_directory(path.as_ref()) { //~ ERROR: unresolved name `is_directory`
22+
callback(path.as_ref(); //~ NOTE: unclosed delimiter
23+
//~^ ERROR: expected one of
24+
fs::create_dir_all(path.as_ref()).map(|()| true) //~ ERROR: expected one of
25+
} else { //~ ERROR: incorrect close delimiter: `}`
26+
Ok(false);
27+
}
28+
29+
panic!();
30+
}
31+
}
32+
33+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that we do some basic error correcton in the tokeniser.
12+
13+
fn main() {
14+
foo(bar(; //~ NOTE: unclosed delimiter
15+
//~^ NOTE: unclosed delimiter
16+
//~^^ ERROR: unexpected token: `;`
17+
//~^^^ ERROR: unresolved name `bar`
18+
//~^^^^ ERROR: unresolved name `foo`
19+
} //~ ERROR: incorrect close delimiter: `}`
20+
//~^ ERROR: incorrect close delimiter: `}`

src/test/parse-fail/issue-2354-1.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@
1010

1111
// compile-flags: -Z parse-only
1212

13-
static foo: isize = 2; } //~ ERROR incorrect close delimiter:
13+
static foo: isize = 2; } //~ ERROR unexpected close delimiter:

src/test/parse-fail/macro-mismatched-delim-paren-brace.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ fn main() {
1414
foo! (
1515
bar, "baz", 1, 2.0
1616
} //~ ERROR incorrect close delimiter
17-
}
17+
} //~ ERROR unexpected close delimiter: `}`

0 commit comments

Comments
 (0)