Skip to content

Commit 847a0d2

Browse files
committed
Some error recovery in the parser
1 parent ffd2a0b commit 847a0d2

12 files changed

+173
-41
lines changed

src/libsyntax/parse/parser.rs

+150-32
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ pub enum ParsePub {
105105
No,
106106
}
107107

108+
#[derive(Clone, Copy, PartialEq)]
109+
pub enum SemiColonMode {
110+
Break,
111+
Ignore,
112+
}
113+
108114
/// Possibly accept an `token::Interpolated` expression (a pre-parsed expression
109115
/// dropped into the token stream, which happens while parsing the result of
110116
/// macro expansion). Placement of these is not as complex as I feared it would
@@ -843,7 +849,10 @@ impl<'a> Parser<'a> {
843849
/// Eat and discard tokens until one of `kets` is encountered. Respects token trees,
844850
/// passes through any errors encountered. Used for error recovery.
845851
pub fn eat_to_tokens(&mut self, kets: &[&token::Token]) {
846-
self.parse_seq_to_before_tokens(kets, seq_sep_none(), |p| p.parse_token_tree());
852+
self.parse_seq_to_before_tokens(kets,
853+
seq_sep_none(),
854+
|p| p.parse_token_tree(),
855+
|mut e| e.cancel());
847856
}
848857

849858
/// Parse a sequence, including the closing delimiter. The function
@@ -871,15 +880,18 @@ impl<'a> Parser<'a> {
871880
-> Vec<T>
872881
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
873882
{
874-
self.parse_seq_to_before_tokens(&[ket], sep, f)
883+
self.parse_seq_to_before_tokens(&[ket], sep, f, |mut e| e.emit())
875884
}
876885

877-
pub fn parse_seq_to_before_tokens<T, F>(&mut self,
886+
// `fe` is an error handler.
887+
fn parse_seq_to_before_tokens<T, F, Fe>(&mut self,
878888
kets: &[&token::Token],
879889
sep: SeqSep,
880-
mut f: F)
890+
mut f: F,
891+
mut fe: Fe)
881892
-> Vec<T>
882893
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
894+
Fe: FnMut(DiagnosticBuilder)
883895
{
884896
let mut first: bool = true;
885897
let mut v = vec!();
@@ -889,8 +901,8 @@ impl<'a> Parser<'a> {
889901
if first {
890902
first = false;
891903
} else {
892-
if let Err(mut e) = self.expect(t) {
893-
e.emit();
904+
if let Err(e) = self.expect(t) {
905+
fe(e);
894906
break;
895907
}
896908
}
@@ -903,8 +915,8 @@ impl<'a> Parser<'a> {
903915

904916
match f(self) {
905917
Ok(t) => v.push(t),
906-
Err(mut e) => {
907-
e.emit();
918+
Err(e) => {
919+
fe(e);
908920
break;
909921
}
910922
}
@@ -1263,7 +1275,7 @@ impl<'a> Parser<'a> {
12631275
break;
12641276
}
12651277
}
1266-
1278+
12671279
return Err(e);
12681280
}
12691281
};
@@ -2339,14 +2351,37 @@ impl<'a> Parser<'a> {
23392351

23402352
while self.token != token::CloseDelim(token::Brace) {
23412353
if self.eat(&token::DotDot) {
2342-
base = Some(try!(self.parse_expr()));
2354+
match self.parse_expr() {
2355+
Ok(e) => {
2356+
base = Some(e);
2357+
}
2358+
Err(mut e) => {
2359+
e.emit();
2360+
self.recover_stmt();
2361+
}
2362+
}
23432363
break;
23442364
}
23452365

2346-
fields.push(try!(self.parse_field()));
2347-
try!(self.commit_expr(&fields.last().unwrap().expr,
2348-
&[token::Comma],
2349-
&[token::CloseDelim(token::Brace)]));
2366+
match self.parse_field() {
2367+
Ok(f) => fields.push(f),
2368+
Err(mut e) => {
2369+
e.emit();
2370+
self.recover_stmt();
2371+
break;
2372+
}
2373+
}
2374+
2375+
match self.commit_expr(&fields.last().unwrap().expr,
2376+
&[token::Comma],
2377+
&[token::CloseDelim(token::Brace)]) {
2378+
Ok(()) => {}
2379+
Err(mut e) => {
2380+
e.emit();
2381+
self.recover_stmt();
2382+
break;
2383+
}
2384+
}
23502385
}
23512386

23522387
hi = self.span.hi;
@@ -2748,6 +2783,7 @@ impl<'a> Parser<'a> {
27482783
if let Some(&sp) = self.open_braces.last() {
27492784
err.span_note(sp, "unclosed delimiter");
27502785
};
2786+
27512787
Err(err)
27522788
},
27532789
/* we ought to allow different depths of unquotation */
@@ -3195,8 +3231,8 @@ impl<'a> Parser<'a> {
31953231
fn parse_match_expr(&mut self, attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
31963232
let match_span = self.last_span;
31973233
let lo = self.last_span.lo;
3198-
let discriminant = try!(self.parse_expr_res(
3199-
Restrictions::RESTRICTION_NO_STRUCT_LITERAL, None));
3234+
let discriminant = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL,
3235+
None));
32003236
if let Err(mut e) = self.commit_expr_expecting(&discriminant,
32013237
token::OpenDelim(token::Brace)) {
32023238
if self.token == token::Token::Semi {
@@ -3208,7 +3244,19 @@ impl<'a> Parser<'a> {
32083244
try!(self.parse_inner_attributes()).into_thin_attrs());
32093245
let mut arms: Vec<Arm> = Vec::new();
32103246
while self.token != token::CloseDelim(token::Brace) {
3211-
arms.push(try!(self.parse_arm()));
3247+
match self.parse_arm() {
3248+
Ok(arm) => arms.push(arm),
3249+
Err(mut e) => {
3250+
// Recover by skipping to the end of the block.
3251+
e.emit();
3252+
self.recover_stmt();
3253+
let hi = self.span.hi;
3254+
if self.token == token::CloseDelim(token::Brace) {
3255+
self.bump();
3256+
}
3257+
return Ok(self.mk_expr(lo, hi, ExprMatch(discriminant, arms), attrs));
3258+
}
3259+
}
32123260
}
32133261
let hi = self.span.hi;
32143262
self.bump();
@@ -3566,7 +3614,11 @@ impl<'a> Parser<'a> {
35663614
}
35673615
// Parse struct pattern
35683616
self.bump();
3569-
let (fields, etc) = try!(self.parse_pat_fields());
3617+
let (fields, etc) = self.parse_pat_fields().unwrap_or_else(|mut e| {
3618+
e.emit();
3619+
self.recover_stmt();
3620+
(vec![], false)
3621+
});
35703622
self.bump();
35713623
pat = PatKind::Struct(path, fields, etc);
35723624
}
@@ -3720,10 +3772,72 @@ impl<'a> Parser<'a> {
37203772

37213773
/// Parse a statement. may include decl.
37223774
pub fn parse_stmt(&mut self) -> PResult<'a, Option<Stmt>> {
3723-
Ok(try!(self.parse_stmt_()))
3775+
Ok(self.parse_stmt_().map(P))
3776+
}
3777+
3778+
// Eat tokens until we can be relatively sure we reached the end of the
3779+
// statement. This is something of a best-effort heuristic.
3780+
//
3781+
// We terminate when we find an unmatched `}` (without consuming it).
3782+
fn recover_stmt(&mut self) {
3783+
self.recover_stmt_(SemiColonMode::Ignore)
3784+
}
3785+
// If `break_on_semi` is `Break`, then we will stop consuming tokens after
3786+
// finding (and consuming) a `;` outside of `{}` or `[]` (note that this is
3787+
// approximate - it can mean we break too early due to macros, but that
3788+
// shoud only lead to sub-optimal recovery, not inaccurate parsing).
3789+
fn recover_stmt_(&mut self, break_on_semi: SemiColonMode) {
3790+
let mut brace_depth = 0;
3791+
let mut bracket_depth = 0;
3792+
loop {
3793+
match self.token {
3794+
token::OpenDelim(token::DelimToken::Brace) => {
3795+
brace_depth += 1;
3796+
self.bump();
3797+
}
3798+
token::OpenDelim(token::DelimToken::Bracket) => {
3799+
bracket_depth += 1;
3800+
self.bump();
3801+
}
3802+
token::CloseDelim(token::DelimToken::Brace) => {
3803+
if brace_depth == 0 {
3804+
return;
3805+
}
3806+
brace_depth -= 1;
3807+
self.bump();
3808+
}
3809+
token::CloseDelim(token::DelimToken::Bracket) => {
3810+
bracket_depth -= 1;
3811+
if bracket_depth < 0 {
3812+
bracket_depth = 0;
3813+
}
3814+
self.bump();
3815+
}
3816+
token::Eof => return,
3817+
token::Semi => {
3818+
self.bump();
3819+
if break_on_semi == SemiColonMode::Break &&
3820+
brace_depth == 0 &&
3821+
bracket_depth == 0 {
3822+
return;
3823+
}
3824+
}
3825+
_ => {
3826+
self.bump()
3827+
}
3828+
}
3829+
}
37243830
}
37253831

3726-
fn parse_stmt_(&mut self) -> PResult<'a, Option<Stmt>> {
3832+
fn parse_stmt_(&mut self) -> Option<Stmt> {
3833+
self.parse_stmt_without_recovery().unwrap_or_else(|mut e| {
3834+
e.emit();
3835+
self.recover_stmt_(SemiColonMode::Break);
3836+
None
3837+
})
3838+
}
3839+
3840+
fn parse_stmt_without_recovery(&mut self) -> PResult<'a, Option<Stmt>> {
37273841
maybe_whole!(Some deref self, NtStmt);
37283842

37293843
let attrs = try!(self.parse_outer_attributes());
@@ -3879,7 +3993,7 @@ impl<'a> Parser<'a> {
38793993
let lo = self.span.lo;
38803994
try!(self.expect(&token::OpenDelim(token::Brace)));
38813995
Ok((try!(self.parse_inner_attributes()),
3882-
try!(self.parse_block_tail(lo, BlockCheckMode::Default))))
3996+
try!(self.parse_block_tail(lo, BlockCheckMode::Default))))
38833997
}
38843998

38853999
/// Parse the rest of a block expression or function body
@@ -3889,7 +4003,7 @@ impl<'a> Parser<'a> {
38894003
let mut expr = None;
38904004

38914005
while !self.eat(&token::CloseDelim(token::Brace)) {
3892-
let Spanned {node, span} = if let Some(s) = try!(self.parse_stmt_()) {
4006+
let Spanned {node, span} = if let Some(s) = self.parse_stmt_() {
38934007
s
38944008
} else {
38954009
// Found only `;` or `}`.
@@ -3974,17 +4088,21 @@ impl<'a> Parser<'a> {
39744088
}))
39754089
}
39764090

3977-
fn handle_expression_like_statement(
3978-
&mut self,
3979-
e: P<Expr>,
3980-
span: Span,
3981-
stmts: &mut Vec<Stmt>,
3982-
last_block_expr: &mut Option<P<Expr>>) -> PResult<'a, ()> {
4091+
fn handle_expression_like_statement(&mut self,
4092+
e: P<Expr>,
4093+
span: Span,
4094+
stmts: &mut Vec<Stmt>,
4095+
last_block_expr: &mut Option<P<Expr>>)
4096+
-> PResult<'a, ()> {
39834097
// expression without semicolon
39844098
if classify::expr_requires_semi_to_be_stmt(&e) {
39854099
// Just check for errors and recover; do not eat semicolon yet.
3986-
try!(self.commit_stmt(&[],
3987-
&[token::Semi, token::CloseDelim(token::Brace)]));
4100+
if let Err(mut e) =
4101+
self.commit_stmt(&[], &[token::Semi, token::CloseDelim(token::Brace)])
4102+
{
4103+
e.emit();
4104+
self.recover_stmt();
4105+
}
39884106
}
39894107

39904108
match self.token {
@@ -4381,13 +4499,13 @@ impl<'a> Parser<'a> {
43814499
}
43824500
));
43834501

4502+
let args: Vec<_> = args.into_iter().filter_map(|x| x).collect();
4503+
43844504
if variadic && args.is_empty() {
43854505
self.span_err(sp,
43864506
"variadic function must be declared with at least one named argument");
43874507
}
43884508

4389-
let args = args.into_iter().filter_map(|x| x).collect();
4390-
43914509
Ok((args, variadic))
43924510
}
43934511

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ macro_rules! parallel {
2525
fn main() {
2626
parallel! {
2727
for i in 0..n {
28-
x += i; //~ ERROR no rules expected the token `+=`
29-
}
28+
x += i; //~ ERROR expected `:`, found `+=`
29+
} //~ ERROR unexpected end of macro invocation
3030
}
3131
}

src/test/compile-fail/macro-incomplete-parse.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ macro_rules! ignored_item {
1818

1919
macro_rules! ignored_expr {
2020
() => ( 1, //~ ERROR unexpected token: `,`
21-
2 ) //~ ERROR macro expansion ignores token `2`
21+
2 )
2222
}
2323

2424
macro_rules! ignored_pat {
@@ -28,7 +28,7 @@ macro_rules! ignored_pat {
2828
ignored_item!(); //~ NOTE caused by the macro expansion here
2929

3030
fn main() {
31-
ignored_expr!(); //~ NOTE caused by the macro expansion here
31+
ignored_expr!();
3232
match 1 {
3333
ignored_pat!() => (), //~ NOTE caused by the macro expansion here
3434
_ => (),

src/test/parse-fail/brace-after-qualified-path-in-match.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// compile-flags: -Z parse-only
12+
1113
fn foo() {
1214
match x {
1315
<T as Trait>::Type{key: value} => (),

src/test/parse-fail/issue-10636-2.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,14 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// FIXME(31528) we emit a bunch of silly errors here due to continuing past the
12+
// first one. This would be easy-ish to address by better recovery in tokenisation.
13+
1114
// compile-flags: -Z parse-only
1215

13-
pub fn trace_option(option: Option<isize>) {
16+
pub fn trace_option(option: Option<isize>) { //~ HELP did you mean to close this delimiter?
1417
option.map(|some| 42; //~ NOTE: unclosed delimiter
18+
//~^ ERROR: expected one of
1519
} //~ ERROR: incorrect close delimiter
20+
//~^ ERROR: expected one of
21+
//~ ERROR: this file contains an un-closed delimiter

src/test/parse-fail/match-refactor-to-expr.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// compile-flags: -Z parse-only
12+
1113
fn main() {
1214
let foo =
1315
match //~ NOTE did you mean to remove this `match` keyword?

src/test/parse-fail/paren-after-qualified-path-in-match.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// compile-flags: -Z parse-only
12+
1113
fn foo() {
1214
match x {
1315
<T as Trait>::Type(2) => (),

src/test/parse-fail/pat-lt-bracket-4.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// compile-flags: -Z parse-only
12+
1113
enum BtNode {
1214
Node(u32,Box<BtNode>,Box<BtNode>),
1315
Leaf(u32),

0 commit comments

Comments
 (0)