From 36a9f0cff16ac8cf20c9b82ea34a2002802a88f7 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Fri, 29 Jan 2016 14:57:58 +1300 Subject: [PATCH 1/6] refactoring: inline some function in the parser --- src/libsyntax/parse/parser.rs | 59 +++++++++++++++++------------------ 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index a8d35783c6f9b..58a291bc85e32 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2641,36 +2641,6 @@ impl<'a> Parser<'a> { // yet. maybe_whole!(deref self, NtTT); - // this is the fall-through for the 'match' below. - // invariants: the current token is not a left-delimiter, - // not an EOF, and not the desired right-delimiter (if - // it were, parse_seq_to_before_end would have prevented - // reaching this point. - fn parse_non_delim_tt_tok<'b>(p: &mut Parser<'b>) -> PResult<'b, TokenTree> { - maybe_whole!(deref p, NtTT); - match p.token { - token::CloseDelim(_) => { - let token_str = p.this_token_to_string(); - let mut err = p.fatal( - &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) = p.open_braces.last() { - err.span_note(sp, "unclosed delimiter"); - }; - Err(err) - }, - /* we ought to allow different depths of unquotation */ - token::Dollar | token::SubstNt(..) if p.quote_depth > 0 => { - p.parse_unquoted() - } - _ => { - Ok(TokenTree::Token(p.span, p.bump_and_get())) - } - } - } - match self.token { token::Eof => { let open_braces = self.open_braces.clone(); @@ -2712,7 +2682,34 @@ impl<'a> Parser<'a> { close_span: close_span, }))) }, - _ => parse_non_delim_tt_tok(self), + _ => { + // invariants: the current token is not a left-delimiter, + // not an EOF, and not the desired right-delimiter (if + // it were, parse_seq_to_before_end would have prevented + // reaching this point. + maybe_whole!(deref self, NtTT); + match self.token { + token::CloseDelim(_) => { + let token_str = self.this_token_to_string(); + let mut err = self.fatal( + &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(err) + }, + /* we ought to allow different depths of unquotation */ + token::Dollar | token::SubstNt(..) if self.quote_depth > 0 => { + self.parse_unquoted() + } + _ => { + Ok(TokenTree::Token(self.span, self.bump_and_get())) + } + } + } } } From 0ef9c5f5853ce35db142e4e2793984148df3d5f8 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Fri, 29 Jan 2016 17:49:59 +1300 Subject: [PATCH 2/6] error correction for missing or mismatched closing brackets --- src/libsyntax/parse/parser.rs | 68 ++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 58a291bc85e32..5972f8f4b7116 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -850,7 +850,7 @@ impl<'a> Parser<'a> { -> PResult<'a, Vec> where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, { - let val = try!(self.parse_seq_to_before_end(ket, sep, f)); + let val = self.parse_seq_to_before_end(ket, sep, f); self.bump(); Ok(val) } @@ -862,23 +862,37 @@ impl<'a> Parser<'a> { ket: &token::Token, sep: SeqSep, mut f: F) - -> PResult<'a, Vec> where - F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, + -> Vec + where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, { let mut first: bool = true; let mut v = vec!(); while self.token != *ket { match sep.sep { - Some(ref t) => { - if first { first = false; } - else { try!(self.expect(t)); } - } - _ => () + Some(ref t) => { + if first { + first = false; + } else { + if let Err(mut e) = self.expect(t) { + e.emit(); + break; + } + } + } + _ => () } if sep.trailing_sep_allowed && self.check(ket) { break; } - v.push(try!(f(self))); + + match f(self) { + Ok(t) => v.push(t), + Err(mut e) => { + e.emit(); + break; + } + } } - return Ok(v); + + v } /// Parse a sequence, including the closing delimiter. The function @@ -893,7 +907,7 @@ impl<'a> Parser<'a> { F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, { try!(self.expect(bra)); - let result = try!(self.parse_seq_to_before_end(ket, sep, f)); + let result = self.parse_seq_to_before_end(ket, sep, f); self.bump(); Ok(result) } @@ -929,7 +943,7 @@ impl<'a> Parser<'a> { { let lo = self.span.lo; try!(self.expect(bra)); - let result = try!(self.parse_seq_to_before_end(ket, sep, f)); + let result = self.parse_seq_to_before_end(ket, sep, f); let hi = self.span.hi; self.bump(); Ok(spanned(lo, hi, result)) @@ -2643,13 +2657,14 @@ impl<'a> Parser<'a> { match self.token { token::Eof => { - let open_braces = self.open_braces.clone(); let mut err: DiagnosticBuilder<'a> = - self.fatal("this file contains an un-closed delimiter"); - for sp in &open_braces { + self.diagnostic().struct_span_err(self.span, + "this file contains an un-closed delimiter"); + for sp in &self.open_braces { err.span_help(*sp, "did you mean to close this delimiter?"); } - return Err(err); + + Err(err) }, token::OpenDelim(delim) => { // The span for beginning of the delimited section @@ -2661,11 +2676,9 @@ impl<'a> Parser<'a> { self.bump(); // Parse the token trees within the delimiters - let tts = try!(self.parse_seq_to_before_end( - &token::CloseDelim(delim), - seq_sep_none(), - |p| p.parse_token_tree() - )); + let tts = self.parse_seq_to_before_end(&token::CloseDelim(delim), + seq_sep_none(), + |p| p.parse_token_tree()); // Parse the close delimiter. let close_span = self.span; @@ -2691,7 +2704,7 @@ impl<'a> Parser<'a> { match self.token { token::CloseDelim(_) => { let token_str = self.this_token_to_string(); - let mut err = self.fatal( + 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 @@ -4516,11 +4529,11 @@ impl<'a> Parser<'a> { token::Comma => { self.bump(); let sep = seq_sep_trailing_allowed(token::Comma); - let mut fn_inputs = try!(self.parse_seq_to_before_end( + let mut fn_inputs = self.parse_seq_to_before_end( &token::CloseDelim(token::Paren), sep, parse_arg_fn - )); + ); fn_inputs.insert(0, Arg::new_self(explicit_self_sp, mutbl_self, $self_id)); fn_inputs } @@ -4539,8 +4552,7 @@ impl<'a> Parser<'a> { let fn_inputs = match explicit_self { SelfKind::Static => { let sep = seq_sep_trailing_allowed(token::Comma); - try!(self.parse_seq_to_before_end(&token::CloseDelim(token::Paren), - sep, parse_arg_fn)) + self.parse_seq_to_before_end(&token::CloseDelim(token::Paren), sep, parse_arg_fn) } SelfKind::Value(id) => parse_remaining_arguments!(id), SelfKind::Region(_,_,id) => parse_remaining_arguments!(id), @@ -4571,11 +4583,11 @@ impl<'a> Parser<'a> { } else { try!(self.expect(&token::BinOp(token::Or))); try!(self.parse_obsolete_closure_kind()); - let args = try!(self.parse_seq_to_before_end( + let args = self.parse_seq_to_before_end( &token::BinOp(token::Or), seq_sep_trailing_allowed(token::Comma), |p| p.parse_fn_block_arg() - )); + ); self.bump(); args } From ffd2a0b9d78191b8a3d97f687077852d15c9b7aa Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Mon, 1 Feb 2016 08:39:50 +1300 Subject: [PATCH 3/6] Add some simple error recovery to the parser and fix tests Some tests just add the extra errors, others I fix by doing some simple error recovery. I've tried to avoid doing too much in the hope of doing something more principled later. In general error messages are getting worse at this stage, but I think in the long run they will get better. --- src/libsyntax/parse/parser.rs | 91 +++++++++++++++++-------- src/test/parse-fail/issue-14303-path.rs | 1 + src/test/parse-fail/issue-2354.rs | 4 +- src/test/parse-fail/pat-lt-bracket-6.rs | 1 + src/test/parse-fail/pat-lt-bracket-7.rs | 3 +- src/test/parse-fail/variadic-ffi-1.rs | 18 ----- 6 files changed, 69 insertions(+), 49 deletions(-) delete mode 100644 src/test/parse-fail/variadic-ffi-1.rs diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 5972f8f4b7116..14c663b698c23 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -598,7 +598,7 @@ impl<'a> Parser<'a> { /// Check if the next token is `tok`, and return `true` if so. /// - /// This method is will automatically add `tok` to `expected_tokens` if `tok` is not + /// This method will automatically add `tok` to `expected_tokens` if `tok` is not /// encountered. pub fn check(&mut self, tok: &token::Token) -> bool { let is_present = self.token == *tok; @@ -840,6 +840,12 @@ impl<'a> Parser<'a> { return Ok((v, returned)); } + /// Eat and discard tokens until one of `kets` is encountered. Respects token trees, + /// passes through any errors encountered. Used for error recovery. + pub fn eat_to_tokens(&mut self, kets: &[&token::Token]) { + self.parse_seq_to_before_tokens(kets, seq_sep_none(), |p| p.parse_token_tree()); + } + /// Parse a sequence, including the closing delimiter. The function /// f must consume tokens until reaching the next separator or /// closing bracket. @@ -861,13 +867,23 @@ impl<'a> Parser<'a> { pub fn parse_seq_to_before_end(&mut self, ket: &token::Token, sep: SeqSep, - mut f: F) + f: F) -> Vec where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, + { + self.parse_seq_to_before_tokens(&[ket], sep, f) + } + + pub fn parse_seq_to_before_tokens(&mut self, + kets: &[&token::Token], + sep: SeqSep, + mut f: F) + -> Vec + where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, { let mut first: bool = true; let mut v = vec!(); - while self.token != *ket { + while !kets.contains(&&self.token) { match sep.sep { Some(ref t) => { if first { @@ -881,7 +897,9 @@ impl<'a> Parser<'a> { } _ => () } - if sep.trailing_sep_allowed && self.check(ket) { break; } + if sep.trailing_sep_allowed && kets.iter().any(|k| self.check(k)) { + break; + } match f(self) { Ok(t) => v.push(t), @@ -1230,7 +1248,25 @@ impl<'a> Parser<'a> { }; (ident, TraitItemKind::Const(ty, default)) } else { - let (constness, unsafety, abi) = try!(p.parse_fn_front_matter()); + let (constness, unsafety, abi) = match p.parse_fn_front_matter() { + Ok(cua) => cua, + Err(e) => { + loop { + p.bump(); + if p.token == token::Semi { + p.bump(); + break; + } + + if p.token == token::OpenDelim(token::DelimToken::Brace) { + try!(p.parse_token_tree()); + break; + } + } + + return Err(e); + } + }; let ident = try!(p.parse_ident()); let mut generics = try!(p.parse_generics()); @@ -4181,8 +4217,8 @@ impl<'a> Parser<'a> { fn forbid_lifetime(&mut self) -> PResult<'a, ()> { if self.token.is_lifetime() { let span = self.span; - return Err(self.span_fatal(span, "lifetime parameters must be declared \ - prior to type parameters")) + return Err(self.diagnostic().struct_span_err(span, "lifetime parameters must be \ + declared prior to type parameters")) } Ok(()) } @@ -4310,7 +4346,8 @@ impl<'a> Parser<'a> { fn parse_fn_args(&mut self, named_args: bool, allow_variadic: bool) -> PResult<'a, (Vec , bool)> { let sp = self.span; - let mut args: Vec> = + let mut variadic = false; + let args: Vec> = try!(self.parse_unspanned_seq( &token::OpenDelim(token::Paren), &token::CloseDelim(token::Paren), @@ -4321,37 +4358,35 @@ impl<'a> Parser<'a> { if allow_variadic { if p.token != token::CloseDelim(token::Paren) { let span = p.span; - return Err(p.span_fatal(span, - "`...` must be last in argument list for variadic function")) + p.span_err(span, + "`...` must be last in argument list for variadic function"); } } else { let span = p.span; - return Err(p.span_fatal(span, - "only foreign functions are allowed to be variadic")) + p.span_err(span, + "only foreign functions are allowed to be variadic"); } + variadic = true; Ok(None) } else { - Ok(Some(try!(p.parse_arg_general(named_args)))) + match p.parse_arg_general(named_args) { + Ok(arg) => Ok(Some(arg)), + Err(mut e) => { + e.emit(); + p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(token::Paren)]); + Ok(None) + } + } } } )); - let variadic = match args.pop() { - Some(None) => true, - Some(x) => { - // Need to put back that last arg - args.push(x); - false - } - None => false - }; - if variadic && args.is_empty() { self.span_err(sp, "variadic function must be declared with at least one named argument"); } - let args = args.into_iter().map(|x| x.unwrap()).collect(); + let args = args.into_iter().filter_map(|x| x).collect(); Ok((args, variadic)) } @@ -4749,8 +4784,8 @@ impl<'a> Parser<'a> { // eat a matched-delimiter token tree: let delim = try!(self.expect_open_delim()); let tts = try!(self.parse_seq_to_end(&token::CloseDelim(delim), - seq_sep_none(), - |p| p.parse_token_tree())); + seq_sep_none(), + |p| p.parse_token_tree())); let m_ = Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT }; let m: ast::Mac = codemap::Spanned { node: m_, span: mk_sp(lo, @@ -5809,8 +5844,8 @@ impl<'a> Parser<'a> { // eat a matched-delimiter token tree: let delim = try!(self.expect_open_delim()); let tts = try!(self.parse_seq_to_end(&token::CloseDelim(delim), - seq_sep_none(), - |p| p.parse_token_tree())); + seq_sep_none(), + |p| p.parse_token_tree())); // single-variant-enum... : let m = Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT }; let m: ast::Mac = codemap::Spanned { node: m, diff --git a/src/test/parse-fail/issue-14303-path.rs b/src/test/parse-fail/issue-14303-path.rs index f0d1feffec80b..7c30b5f26296b 100644 --- a/src/test/parse-fail/issue-14303-path.rs +++ b/src/test/parse-fail/issue-14303-path.rs @@ -12,3 +12,4 @@ fn bar<'a, T>(x: mymodule::X<'a, T, 'b, 'c>) {} //~^ ERROR lifetime parameters must be declared prior to type parameters +//~^^ ERROR unexpected token diff --git a/src/test/parse-fail/issue-2354.rs b/src/test/parse-fail/issue-2354.rs index 0b380d6ae8847..2e799a72c81af 100644 --- a/src/test/parse-fail/issue-2354.rs +++ b/src/test/parse-fail/issue-2354.rs @@ -12,8 +12,8 @@ fn foo() { //~ HELP did you mean to close this delimiter? match Some(x) { - Some(y) { panic!(); } - None { panic!(); } + Some(y) => { panic!(); } + None => { panic!(); } } fn bar() { diff --git a/src/test/parse-fail/pat-lt-bracket-6.rs b/src/test/parse-fail/pat-lt-bracket-6.rs index 72fdae82260ea..bc27aedb627ee 100644 --- a/src/test/parse-fail/pat-lt-bracket-6.rs +++ b/src/test/parse-fail/pat-lt-bracket-6.rs @@ -10,4 +10,5 @@ fn main() { let Test(&desc[..]) = x; //~ error: expected one of `,` or `@`, found `[` + //~^ ERROR expected one of `:`, `;`, or `=`, found `..` } diff --git a/src/test/parse-fail/pat-lt-bracket-7.rs b/src/test/parse-fail/pat-lt-bracket-7.rs index c7731d156ad62..3e9478da44de5 100644 --- a/src/test/parse-fail/pat-lt-bracket-7.rs +++ b/src/test/parse-fail/pat-lt-bracket-7.rs @@ -9,5 +9,6 @@ // except according to those terms. fn main() { - for thing(x[]) {} //~ error: expected one of `,` or `@`, found `[` + for thing(x[]) in foo {} //~ error: expected one of `,` or `@`, found `[` + //~^ ERROR: expected `in`, found `]` } diff --git a/src/test/parse-fail/variadic-ffi-1.rs b/src/test/parse-fail/variadic-ffi-1.rs deleted file mode 100644 index 63a36e984399d..0000000000000 --- a/src/test/parse-fail/variadic-ffi-1.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2013 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags: -Z parse-only - -extern { - fn printf(...); //~ ERROR: variadic function must be declared with at least one named argument - fn printf(..., foo: isize); //~ ERROR: `...` must be last in argument list for variadic function -} - -fn main() {} From 847a0d21507faeca6413782c12e9071ca44c97e8 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Wed, 10 Feb 2016 16:11:27 +1300 Subject: [PATCH 4/6] Some error recovery in the parser --- src/libsyntax/parse/parser.rs | 182 +++++++++++++++--- src/test/compile-fail/issue-30715.rs | 4 +- .../compile-fail/macro-incomplete-parse.rs | 4 +- .../brace-after-qualified-path-in-match.rs | 2 + src/test/parse-fail/issue-10636-2.rs | 8 +- src/test/parse-fail/match-refactor-to-expr.rs | 2 + .../paren-after-qualified-path-in-match.rs | 2 + src/test/parse-fail/pat-lt-bracket-4.rs | 2 + src/test/parse-fail/struct-literal-in-for.rs | 2 +- src/test/parse-fail/struct-literal-in-if.rs | 2 +- .../struct-literal-in-match-discriminant.rs | 2 +- .../parse-fail/struct-literal-in-while.rs | 2 +- 12 files changed, 173 insertions(+), 41 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 14c663b698c23..572c51cc2f4ba 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -105,6 +105,12 @@ pub enum ParsePub { No, } +#[derive(Clone, Copy, PartialEq)] +pub enum SemiColonMode { + Break, + Ignore, +} + /// Possibly accept an `token::Interpolated` expression (a pre-parsed expression /// dropped into the token stream, which happens while parsing the result of /// macro expansion). Placement of these is not as complex as I feared it would @@ -843,7 +849,10 @@ impl<'a> Parser<'a> { /// Eat and discard tokens until one of `kets` is encountered. Respects token trees, /// passes through any errors encountered. Used for error recovery. pub fn eat_to_tokens(&mut self, kets: &[&token::Token]) { - self.parse_seq_to_before_tokens(kets, seq_sep_none(), |p| p.parse_token_tree()); + self.parse_seq_to_before_tokens(kets, + seq_sep_none(), + |p| p.parse_token_tree(), + |mut e| e.cancel()); } /// Parse a sequence, including the closing delimiter. The function @@ -871,15 +880,18 @@ impl<'a> Parser<'a> { -> Vec where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, { - self.parse_seq_to_before_tokens(&[ket], sep, f) + self.parse_seq_to_before_tokens(&[ket], sep, f, |mut e| e.emit()) } - pub fn parse_seq_to_before_tokens(&mut self, + // `fe` is an error handler. + fn parse_seq_to_before_tokens(&mut self, kets: &[&token::Token], sep: SeqSep, - mut f: F) + mut f: F, + mut fe: Fe) -> Vec where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, + Fe: FnMut(DiagnosticBuilder) { let mut first: bool = true; let mut v = vec!(); @@ -889,8 +901,8 @@ impl<'a> Parser<'a> { if first { first = false; } else { - if let Err(mut e) = self.expect(t) { - e.emit(); + if let Err(e) = self.expect(t) { + fe(e); break; } } @@ -903,8 +915,8 @@ impl<'a> Parser<'a> { match f(self) { Ok(t) => v.push(t), - Err(mut e) => { - e.emit(); + Err(e) => { + fe(e); break; } } @@ -1263,7 +1275,7 @@ impl<'a> Parser<'a> { break; } } - + return Err(e); } }; @@ -2339,14 +2351,37 @@ impl<'a> Parser<'a> { while self.token != token::CloseDelim(token::Brace) { if self.eat(&token::DotDot) { - base = Some(try!(self.parse_expr())); + match self.parse_expr() { + Ok(e) => { + base = Some(e); + } + Err(mut e) => { + e.emit(); + self.recover_stmt(); + } + } break; } - fields.push(try!(self.parse_field())); - try!(self.commit_expr(&fields.last().unwrap().expr, - &[token::Comma], - &[token::CloseDelim(token::Brace)])); + match self.parse_field() { + Ok(f) => fields.push(f), + Err(mut e) => { + e.emit(); + self.recover_stmt(); + break; + } + } + + match self.commit_expr(&fields.last().unwrap().expr, + &[token::Comma], + &[token::CloseDelim(token::Brace)]) { + Ok(()) => {} + Err(mut e) => { + e.emit(); + self.recover_stmt(); + break; + } + } } hi = self.span.hi; @@ -2748,6 +2783,7 @@ impl<'a> Parser<'a> { if let Some(&sp) = self.open_braces.last() { err.span_note(sp, "unclosed delimiter"); }; + Err(err) }, /* we ought to allow different depths of unquotation */ @@ -3195,8 +3231,8 @@ impl<'a> Parser<'a> { fn parse_match_expr(&mut self, attrs: ThinAttributes) -> PResult<'a, P> { let match_span = self.last_span; let lo = self.last_span.lo; - let discriminant = try!(self.parse_expr_res( - Restrictions::RESTRICTION_NO_STRUCT_LITERAL, None)); + let discriminant = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL, + None)); if let Err(mut e) = self.commit_expr_expecting(&discriminant, token::OpenDelim(token::Brace)) { if self.token == token::Token::Semi { @@ -3208,7 +3244,19 @@ impl<'a> Parser<'a> { try!(self.parse_inner_attributes()).into_thin_attrs()); let mut arms: Vec = Vec::new(); while self.token != token::CloseDelim(token::Brace) { - arms.push(try!(self.parse_arm())); + match self.parse_arm() { + Ok(arm) => arms.push(arm), + Err(mut e) => { + // Recover by skipping to the end of the block. + e.emit(); + self.recover_stmt(); + let hi = self.span.hi; + if self.token == token::CloseDelim(token::Brace) { + self.bump(); + } + return Ok(self.mk_expr(lo, hi, ExprMatch(discriminant, arms), attrs)); + } + } } let hi = self.span.hi; self.bump(); @@ -3566,7 +3614,11 @@ impl<'a> Parser<'a> { } // Parse struct pattern self.bump(); - let (fields, etc) = try!(self.parse_pat_fields()); + let (fields, etc) = self.parse_pat_fields().unwrap_or_else(|mut e| { + e.emit(); + self.recover_stmt(); + (vec![], false) + }); self.bump(); pat = PatKind::Struct(path, fields, etc); } @@ -3720,10 +3772,72 @@ impl<'a> Parser<'a> { /// Parse a statement. may include decl. pub fn parse_stmt(&mut self) -> PResult<'a, Option> { - Ok(try!(self.parse_stmt_())) + Ok(self.parse_stmt_().map(P)) + } + + // Eat tokens until we can be relatively sure we reached the end of the + // statement. This is something of a best-effort heuristic. + // + // We terminate when we find an unmatched `}` (without consuming it). + fn recover_stmt(&mut self) { + self.recover_stmt_(SemiColonMode::Ignore) + } + // If `break_on_semi` is `Break`, then we will stop consuming tokens after + // finding (and consuming) a `;` outside of `{}` or `[]` (note that this is + // approximate - it can mean we break too early due to macros, but that + // shoud only lead to sub-optimal recovery, not inaccurate parsing). + fn recover_stmt_(&mut self, break_on_semi: SemiColonMode) { + let mut brace_depth = 0; + let mut bracket_depth = 0; + loop { + match self.token { + token::OpenDelim(token::DelimToken::Brace) => { + brace_depth += 1; + self.bump(); + } + token::OpenDelim(token::DelimToken::Bracket) => { + bracket_depth += 1; + self.bump(); + } + token::CloseDelim(token::DelimToken::Brace) => { + if brace_depth == 0 { + return; + } + brace_depth -= 1; + self.bump(); + } + token::CloseDelim(token::DelimToken::Bracket) => { + bracket_depth -= 1; + if bracket_depth < 0 { + bracket_depth = 0; + } + self.bump(); + } + token::Eof => return, + token::Semi => { + self.bump(); + if break_on_semi == SemiColonMode::Break && + brace_depth == 0 && + bracket_depth == 0 { + return; + } + } + _ => { + self.bump() + } + } + } } - fn parse_stmt_(&mut self) -> PResult<'a, Option> { + fn parse_stmt_(&mut self) -> Option { + self.parse_stmt_without_recovery().unwrap_or_else(|mut e| { + e.emit(); + self.recover_stmt_(SemiColonMode::Break); + None + }) + } + + fn parse_stmt_without_recovery(&mut self) -> PResult<'a, Option> { maybe_whole!(Some deref self, NtStmt); let attrs = try!(self.parse_outer_attributes()); @@ -3879,7 +3993,7 @@ impl<'a> Parser<'a> { let lo = self.span.lo; try!(self.expect(&token::OpenDelim(token::Brace))); Ok((try!(self.parse_inner_attributes()), - try!(self.parse_block_tail(lo, BlockCheckMode::Default)))) + try!(self.parse_block_tail(lo, BlockCheckMode::Default)))) } /// Parse the rest of a block expression or function body @@ -3889,7 +4003,7 @@ impl<'a> Parser<'a> { let mut expr = None; while !self.eat(&token::CloseDelim(token::Brace)) { - let Spanned {node, span} = if let Some(s) = try!(self.parse_stmt_()) { + let Spanned {node, span} = if let Some(s) = self.parse_stmt_() { s } else { // Found only `;` or `}`. @@ -3974,17 +4088,21 @@ impl<'a> Parser<'a> { })) } - fn handle_expression_like_statement( - &mut self, - e: P, - span: Span, - stmts: &mut Vec, - last_block_expr: &mut Option>) -> PResult<'a, ()> { + fn handle_expression_like_statement(&mut self, + e: P, + span: Span, + stmts: &mut Vec, + last_block_expr: &mut Option>) + -> PResult<'a, ()> { // expression without semicolon if classify::expr_requires_semi_to_be_stmt(&e) { // Just check for errors and recover; do not eat semicolon yet. - try!(self.commit_stmt(&[], - &[token::Semi, token::CloseDelim(token::Brace)])); + if let Err(mut e) = + self.commit_stmt(&[], &[token::Semi, token::CloseDelim(token::Brace)]) + { + e.emit(); + self.recover_stmt(); + } } match self.token { @@ -4381,13 +4499,13 @@ impl<'a> Parser<'a> { } )); + let args: Vec<_> = args.into_iter().filter_map(|x| x).collect(); + if variadic && args.is_empty() { self.span_err(sp, "variadic function must be declared with at least one named argument"); } - let args = args.into_iter().filter_map(|x| x).collect(); - Ok((args, variadic)) } diff --git a/src/test/compile-fail/issue-30715.rs b/src/test/compile-fail/issue-30715.rs index 7ad43954010b9..67f619b4de4f4 100644 --- a/src/test/compile-fail/issue-30715.rs +++ b/src/test/compile-fail/issue-30715.rs @@ -25,7 +25,7 @@ macro_rules! parallel { fn main() { parallel! { for i in 0..n { - x += i; //~ ERROR no rules expected the token `+=` - } + x += i; //~ ERROR expected `:`, found `+=` + } //~ ERROR unexpected end of macro invocation } } diff --git a/src/test/compile-fail/macro-incomplete-parse.rs b/src/test/compile-fail/macro-incomplete-parse.rs index 32770d9018938..364a7e9cf6d75 100644 --- a/src/test/compile-fail/macro-incomplete-parse.rs +++ b/src/test/compile-fail/macro-incomplete-parse.rs @@ -18,7 +18,7 @@ macro_rules! ignored_item { macro_rules! ignored_expr { () => ( 1, //~ ERROR unexpected token: `,` - 2 ) //~ ERROR macro expansion ignores token `2` + 2 ) } macro_rules! ignored_pat { @@ -28,7 +28,7 @@ macro_rules! ignored_pat { ignored_item!(); //~ NOTE caused by the macro expansion here fn main() { - ignored_expr!(); //~ NOTE caused by the macro expansion here + ignored_expr!(); match 1 { ignored_pat!() => (), //~ NOTE caused by the macro expansion here _ => (), diff --git a/src/test/parse-fail/brace-after-qualified-path-in-match.rs b/src/test/parse-fail/brace-after-qualified-path-in-match.rs index 66f462df05ac9..ff434d87e1fb2 100644 --- a/src/test/parse-fail/brace-after-qualified-path-in-match.rs +++ b/src/test/parse-fail/brace-after-qualified-path-in-match.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// compile-flags: -Z parse-only + fn foo() { match x { ::Type{key: value} => (), diff --git a/src/test/parse-fail/issue-10636-2.rs b/src/test/parse-fail/issue-10636-2.rs index f57abf8929e83..41a3b06e6556e 100644 --- a/src/test/parse-fail/issue-10636-2.rs +++ b/src/test/parse-fail/issue-10636-2.rs @@ -8,8 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// 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. + // compile-flags: -Z parse-only -pub fn trace_option(option: Option) { +pub fn trace_option(option: Option) { //~ HELP did you mean to close this delimiter? option.map(|some| 42; //~ NOTE: unclosed delimiter + //~^ ERROR: expected one of } //~ ERROR: incorrect close delimiter +//~^ ERROR: expected one of +//~ ERROR: this file contains an un-closed delimiter diff --git a/src/test/parse-fail/match-refactor-to-expr.rs b/src/test/parse-fail/match-refactor-to-expr.rs index e85fb3c9dd53d..b99d0493ff733 100644 --- a/src/test/parse-fail/match-refactor-to-expr.rs +++ b/src/test/parse-fail/match-refactor-to-expr.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// compile-flags: -Z parse-only + fn main() { let foo = match //~ NOTE did you mean to remove this `match` keyword? diff --git a/src/test/parse-fail/paren-after-qualified-path-in-match.rs b/src/test/parse-fail/paren-after-qualified-path-in-match.rs index d06fd2bb4e704..d3aa4b72b78a5 100644 --- a/src/test/parse-fail/paren-after-qualified-path-in-match.rs +++ b/src/test/parse-fail/paren-after-qualified-path-in-match.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// compile-flags: -Z parse-only + fn foo() { match x { ::Type(2) => (), diff --git a/src/test/parse-fail/pat-lt-bracket-4.rs b/src/test/parse-fail/pat-lt-bracket-4.rs index 3d9b492307a7c..a163b38b49270 100644 --- a/src/test/parse-fail/pat-lt-bracket-4.rs +++ b/src/test/parse-fail/pat-lt-bracket-4.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// compile-flags: -Z parse-only + enum BtNode { Node(u32,Box,Box), Leaf(u32), diff --git a/src/test/parse-fail/struct-literal-in-for.rs b/src/test/parse-fail/struct-literal-in-for.rs index 107b836d160a5..e57298f72804a 100644 --- a/src/test/parse-fail/struct-literal-in-for.rs +++ b/src/test/parse-fail/struct-literal-in-for.rs @@ -23,7 +23,7 @@ impl Foo { fn main() { for x in Foo { x: 3 //~ ERROR expected type, found `3` - }.hi() { + }.hi() { //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `{` println!("yo"); } } diff --git a/src/test/parse-fail/struct-literal-in-if.rs b/src/test/parse-fail/struct-literal-in-if.rs index b1cccc51d7bb9..6bf41b7a450af 100644 --- a/src/test/parse-fail/struct-literal-in-if.rs +++ b/src/test/parse-fail/struct-literal-in-if.rs @@ -23,7 +23,7 @@ impl Foo { fn main() { if Foo { x: 3 //~ ERROR expected type, found `3` - }.hi() { + }.hi() { //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `{` println!("yo"); } } diff --git a/src/test/parse-fail/struct-literal-in-match-discriminant.rs b/src/test/parse-fail/struct-literal-in-match-discriminant.rs index 42b3e75bcf6d1..679f4542824fb 100644 --- a/src/test/parse-fail/struct-literal-in-match-discriminant.rs +++ b/src/test/parse-fail/struct-literal-in-match-discriminant.rs @@ -20,6 +20,6 @@ fn main() { } { Foo { x: x - } => {} + } => {} //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `=>` } } diff --git a/src/test/parse-fail/struct-literal-in-while.rs b/src/test/parse-fail/struct-literal-in-while.rs index 1c52dc48ccd1a..b388aac2c5487 100644 --- a/src/test/parse-fail/struct-literal-in-while.rs +++ b/src/test/parse-fail/struct-literal-in-while.rs @@ -23,7 +23,7 @@ impl Foo { fn main() { while Foo { x: 3 //~ ERROR expected type, found `3` - }.hi() { + }.hi() { //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `{` println!("yo"); } } From 4e1a6f6b363a04218583ab10bdf3cbe6bca41f00 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Thu, 11 Feb 2016 14:50:19 +1300 Subject: [PATCH 5/6] Tests --- src/test/compile-fail/parser-recovery-1.rs | 22 ++++++++++++++++++++++ src/test/compile-fail/parser-recovery-2.rs | 22 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/test/compile-fail/parser-recovery-1.rs create mode 100644 src/test/compile-fail/parser-recovery-2.rs diff --git a/src/test/compile-fail/parser-recovery-1.rs b/src/test/compile-fail/parser-recovery-1.rs new file mode 100644 index 0000000000000..674418dcca6ad --- /dev/null +++ b/src/test/compile-fail/parser-recovery-1.rs @@ -0,0 +1,22 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we can recover from missing braces in the parser. + +trait Foo { + fn bar() { + let x = foo(); //~ ERROR unresolved name `foo` + +} + +fn main() { + let x = y.; //~ ERROR unexpected token + //~^ ERROR unresolved name `y` +} //~ ERROR this file contains an un-closed delimiter diff --git a/src/test/compile-fail/parser-recovery-2.rs b/src/test/compile-fail/parser-recovery-2.rs new file mode 100644 index 0000000000000..f1eb696f6ff84 --- /dev/null +++ b/src/test/compile-fail/parser-recovery-2.rs @@ -0,0 +1,22 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we can recover from mismatched braces in the parser. + +trait Foo { + fn bar() { + let x = foo(); //~ ERROR unresolved name `foo` + ) //~ ERROR incorrect close delimiter: `)` +} + +fn main() { + let x = y.; //~ ERROR unexpected token + //~^ ERROR unresolved name `y` +} From 73a8513b88f28344c37d3b3296f93cbc8f30cefa Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Mon, 15 Feb 2016 13:14:31 +1300 Subject: [PATCH 6/6] Rebasing --- src/libsyntax/parse/parser.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 572c51cc2f4ba..59e79200568a0 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3254,7 +3254,7 @@ impl<'a> Parser<'a> { if self.token == token::CloseDelim(token::Brace) { self.bump(); } - return Ok(self.mk_expr(lo, hi, ExprMatch(discriminant, arms), attrs)); + return Ok(self.mk_expr(lo, hi, ExprKind::Match(discriminant, arms), attrs)); } } } @@ -3772,7 +3772,7 @@ impl<'a> Parser<'a> { /// Parse a statement. may include decl. pub fn parse_stmt(&mut self) -> PResult<'a, Option> { - Ok(self.parse_stmt_().map(P)) + Ok(self.parse_stmt_()) } // Eat tokens until we can be relatively sure we reached the end of the