From e3918cf62138157ef4748c2193e0601aaa78f311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 26 Mar 2019 17:36:07 -0700 Subject: [PATCH 1/4] Recover from parse error in tuple syntax --- src/libsyntax/parse/parser.rs | 39 +++++++++++++---- src/test/ui/issues/issue-34334.rs | 8 +++- src/test/ui/issues/issue-34334.stderr | 43 +++++++++++++++++-- src/test/ui/parser/pat-tuple-1.rs | 5 ++- src/test/ui/parser/pat-tuple-5.rs | 2 +- .../ui/parser/recover-from-bad-variant.rs | 14 ++++++ .../ui/parser/recover-from-bad-variant.stderr | 15 +++++++ src/test/ui/parser/recover-tuple-pat.rs | 12 ++++++ src/test/ui/parser/recover-tuple-pat.stderr | 24 +++++++++++ src/test/ui/parser/recover-tuple.rs | 11 +++++ src/test/ui/parser/recover-tuple.stderr | 18 ++++++++ .../ui/parser/trait-object-lifetime-parens.rs | 5 ++- .../trait-object-lifetime-parens.stderr | 17 +++++++- 13 files changed, 196 insertions(+), 17 deletions(-) create mode 100644 src/test/ui/parser/recover-from-bad-variant.rs create mode 100644 src/test/ui/parser/recover-from-bad-variant.stderr create mode 100644 src/test/ui/parser/recover-tuple-pat.rs create mode 100644 src/test/ui/parser/recover-tuple-pat.stderr create mode 100644 src/test/ui/parser/recover-tuple.rs create mode 100644 src/test/ui/parser/recover-tuple.stderr diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index a0c3fe356083f..ea0c7e0e36662 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2637,7 +2637,15 @@ impl<'a> Parser<'a> { let mut trailing_comma = false; let mut recovered = false; while self.token != token::CloseDelim(token::Paren) { - es.push(self.parse_expr()?); + es.push(match self.parse_expr() { + Ok(es) => es, + Err(mut err) => { // recover from parse error in tuple list + err.emit(); + self.consume_block(token::Paren); + hi = self.prev_span; + return Ok(self.mk_expr(lo.to(hi), ExprKind::Err, ThinVec::new())); + } + }); recovered = self.expect_one_of( &[], &[token::Comma, token::CloseDelim(token::Paren)], @@ -3248,16 +3256,24 @@ impl<'a> Parser<'a> { match self.token { // expr(...) token::OpenDelim(token::Paren) => { - let es = self.parse_unspanned_seq( + match self.parse_unspanned_seq( &token::OpenDelim(token::Paren), &token::CloseDelim(token::Paren), SeqSep::trailing_allowed(token::Comma), |p| Ok(p.parse_expr()?) - )?; - hi = self.prev_span; - - let nd = self.mk_call(e, es); - e = self.mk_expr(lo.to(hi), nd, ThinVec::new()); + ) { + Ok(es) => { + let nd = self.mk_call(e, es); + hi = self.prev_span; + e = self.mk_expr(lo.to(hi), nd, ThinVec::new()); + } + Err(mut err) => { // recover from parse error in argument list + err.emit(); + self.consume_block(token::Paren); + hi = self.prev_span; + e = self.mk_expr(lo.to(hi), ExprKind::Err, ThinVec::new()); + } + } } // expr[...] @@ -4262,7 +4278,14 @@ impl<'a> Parser<'a> { // Trailing commas are significant because (p) and (p,) are different patterns. fn parse_parenthesized_pat_list(&mut self) -> PResult<'a, (Vec>, Option, bool)> { self.expect(&token::OpenDelim(token::Paren))?; - let result = self.parse_pat_list()?; + let result = match self.parse_pat_list() { + Ok(result) => result, + Err(mut err) => { // recover from parse error in tuple pattern list + err.emit(); + self.consume_block(token::Paren); + return Ok((vec![], Some(0), false)); + } + }; self.expect(&token::CloseDelim(token::Paren))?; Ok(result) } diff --git a/src/test/ui/issues/issue-34334.rs b/src/test/ui/issues/issue-34334.rs index 5c4f5c86a7fde..928d217441ef2 100644 --- a/src/test/ui/issues/issue-34334.rs +++ b/src/test/ui/issues/issue-34334.rs @@ -1,4 +1,10 @@ fn main () { - let sr: Vec<(u32, _, _) = vec![]; //~ ERROR expected one of `,` or `>`, found `=` + let sr: Vec<(u32, _, _) = vec![]; + //~^ ERROR expected one of `,` or `>`, found `=` + //~| ERROR expected value, found struct `Vec` + //~| ERROR mismatched types + //~| ERROR invalid left-hand side expression + //~| ERROR expected expression, found reserved identifier `_` let sr2: Vec<(u32, _, _)> = sr.iter().map(|(faction, th_sender, th_receiver)| {}).collect(); + //~^ ERROR no method named `iter` found for type `()` in the current scope } diff --git a/src/test/ui/issues/issue-34334.stderr b/src/test/ui/issues/issue-34334.stderr index 1b0babf939044..51ea0c6a90894 100644 --- a/src/test/ui/issues/issue-34334.stderr +++ b/src/test/ui/issues/issue-34334.stderr @@ -1,10 +1,47 @@ +error: expected expression, found reserved identifier `_` + --> $DIR/issue-34334.rs:2:23 + | +LL | let sr: Vec<(u32, _, _) = vec![]; + | ^ expected expression + error: expected one of `,` or `>`, found `=` --> $DIR/issue-34334.rs:2:29 | LL | let sr: Vec<(u32, _, _) = vec![]; - | -- ^ expected one of `,` or `>` here - | | + | --- ^ expected one of `,` or `>` here + | | | + | | help: use `=` if you meant to assign | while parsing the type for `sr` -error: aborting due to previous error +error[E0423]: expected value, found struct `Vec` + --> $DIR/issue-34334.rs:2:13 + | +LL | let sr: Vec<(u32, _, _) = vec![]; + | ^^^ did you mean `Vec { /* fields */ }`? + +error[E0308]: mismatched types + --> $DIR/issue-34334.rs:2:31 + | +LL | let sr: Vec<(u32, _, _) = vec![]; + | ^^^^^^ expected bool, found struct `std::vec::Vec` + | + = note: expected type `bool` + found type `std::vec::Vec<_>` + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +error[E0070]: invalid left-hand side expression + --> $DIR/issue-34334.rs:2:13 + | +LL | let sr: Vec<(u32, _, _) = vec![]; + | ^^^^^^^^^^^^^^^^^^^^^^^^ left-hand of expression not valid + +error[E0599]: no method named `iter` found for type `()` in the current scope + --> $DIR/issue-34334.rs:8:36 + | +LL | let sr2: Vec<(u32, _, _)> = sr.iter().map(|(faction, th_sender, th_receiver)| {}).collect(); + | ^^^^ + +error: aborting due to 6 previous errors +Some errors occurred: E0070, E0308, E0423, E0599. +For more information about an error, try `rustc --explain E0070`. diff --git a/src/test/ui/parser/pat-tuple-1.rs b/src/test/ui/parser/pat-tuple-1.rs index 213fbe371d435..d76096c25988d 100644 --- a/src/test/ui/parser/pat-tuple-1.rs +++ b/src/test/ui/parser/pat-tuple-1.rs @@ -1,5 +1,6 @@ fn main() { - match 0 { - (, ..) => {} //~ ERROR expected pattern, found `,` + match (0, 1) { + (, ..) => {} + //~^ ERROR expected pattern, found `,` } } diff --git a/src/test/ui/parser/pat-tuple-5.rs b/src/test/ui/parser/pat-tuple-5.rs index 03176abaf49a8..d4f05a5eb523e 100644 --- a/src/test/ui/parser/pat-tuple-5.rs +++ b/src/test/ui/parser/pat-tuple-5.rs @@ -1,5 +1,5 @@ fn main() { - match 0 { + match (0, 1) { (pat ..) => {} //~ ERROR unexpected token: `)` } } diff --git a/src/test/ui/parser/recover-from-bad-variant.rs b/src/test/ui/parser/recover-from-bad-variant.rs new file mode 100644 index 0000000000000..35088fb306882 --- /dev/null +++ b/src/test/ui/parser/recover-from-bad-variant.rs @@ -0,0 +1,14 @@ +enum Enum { + Foo { a: usize, b: usize }, + Bar(usize, usize), +} + +fn main() { + let x = Enum::Foo(a: 3, b: 4); + //~^ ERROR expected type, found `3` + match x { + Enum::Foo(a, b) => {} + //~^ ERROR expected tuple struct/variant, found struct variant `Enum::Foo` + Enum::Bar(a, b) => {} + } +} diff --git a/src/test/ui/parser/recover-from-bad-variant.stderr b/src/test/ui/parser/recover-from-bad-variant.stderr new file mode 100644 index 0000000000000..bd4a562d72d19 --- /dev/null +++ b/src/test/ui/parser/recover-from-bad-variant.stderr @@ -0,0 +1,15 @@ +error: expected type, found `3` + --> $DIR/recover-from-bad-variant.rs:7:26 + | +LL | let x = Enum::Foo(a: 3, b: 4); + | ^ expecting a type here because of type ascription + +error[E0532]: expected tuple struct/variant, found struct variant `Enum::Foo` + --> $DIR/recover-from-bad-variant.rs:10:9 + | +LL | Enum::Foo(a, b) => {} + | ^^^^^^^^^ did you mean `Enum::Foo { /* fields */ }`? + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0532`. diff --git a/src/test/ui/parser/recover-tuple-pat.rs b/src/test/ui/parser/recover-tuple-pat.rs new file mode 100644 index 0000000000000..488e8db6b8789 --- /dev/null +++ b/src/test/ui/parser/recover-tuple-pat.rs @@ -0,0 +1,12 @@ +fn main() { + let x = (1, 2, 3, 4); + match x { + (1, .., 4) => {} + (1, .=., 4) => { let _: usize = ""; } + //~^ ERROR expected pattern, found `.` + //~| ERROR mismatched types + (.=., 4) => {} + //~^ ERROR expected pattern, found `.` + (1, 2, 3, 4) => {} + } +} diff --git a/src/test/ui/parser/recover-tuple-pat.stderr b/src/test/ui/parser/recover-tuple-pat.stderr new file mode 100644 index 0000000000000..5919aa72355ac --- /dev/null +++ b/src/test/ui/parser/recover-tuple-pat.stderr @@ -0,0 +1,24 @@ +error: expected pattern, found `.` + --> $DIR/recover-tuple-pat.rs:5:13 + | +LL | (1, .=., 4) => { let _: usize = ""; } + | ^ expected pattern + +error: expected pattern, found `.` + --> $DIR/recover-tuple-pat.rs:8:10 + | +LL | (.=., 4) => {} + | ^ expected pattern + +error[E0308]: mismatched types + --> $DIR/recover-tuple-pat.rs:5:41 + | +LL | (1, .=., 4) => { let _: usize = ""; } + | ^^ expected usize, found reference + | + = note: expected type `usize` + found type `&'static str` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/parser/recover-tuple.rs b/src/test/ui/parser/recover-tuple.rs new file mode 100644 index 0000000000000..59e2695dec6dc --- /dev/null +++ b/src/test/ui/parser/recover-tuple.rs @@ -0,0 +1,11 @@ +fn main() { + // no complaints about the tuple not matching the expected type + let x: (usize, usize, usize) = (3, .=.); + //~^ ERROR expected expression, found `.` + // verify that the parser recovers: + let y: usize = ""; //~ ERROR mismatched types + // no complaints about the type + foo(x); +} + +fn foo(_: (usize, usize, usize)) {} diff --git a/src/test/ui/parser/recover-tuple.stderr b/src/test/ui/parser/recover-tuple.stderr new file mode 100644 index 0000000000000..4252fc1fd1e1b --- /dev/null +++ b/src/test/ui/parser/recover-tuple.stderr @@ -0,0 +1,18 @@ +error: expected expression, found `.` + --> $DIR/recover-tuple.rs:3:40 + | +LL | let x: (usize, usize, usize) = (3, .=.); + | ^ expected expression + +error[E0308]: mismatched types + --> $DIR/recover-tuple.rs:6:20 + | +LL | let y: usize = ""; + | ^^ expected usize, found reference + | + = note: expected type `usize` + found type `&'static str` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/parser/trait-object-lifetime-parens.rs b/src/test/ui/parser/trait-object-lifetime-parens.rs index b188e14778be5..43f6497f7e71c 100644 --- a/src/test/ui/parser/trait-object-lifetime-parens.rs +++ b/src/test/ui/parser/trait-object-lifetime-parens.rs @@ -6,7 +6,10 @@ fn f<'a, T: Trait + ('a)>() {} //~ ERROR parenthesized lifetime bounds are not s fn check<'a>() { let _: Box; //~ ERROR parenthesized lifetime bounds are not supported - let _: Box<('a) + Trait>; //~ ERROR expected type, found `'a` + let _: Box<('a) + Trait>; + //~^ ERROR expected type, found `'a` + //~| ERROR expected `:`, found `)` + //~| ERROR chained comparison operators require parentheses } fn main() {} diff --git a/src/test/ui/parser/trait-object-lifetime-parens.stderr b/src/test/ui/parser/trait-object-lifetime-parens.stderr index 084e6d5b11fcd..a31b7aea8fee6 100644 --- a/src/test/ui/parser/trait-object-lifetime-parens.stderr +++ b/src/test/ui/parser/trait-object-lifetime-parens.stderr @@ -10,6 +10,21 @@ error: parenthesized lifetime bounds are not supported LL | let _: Box; | ^^^^ help: remove the parentheses +error: expected `:`, found `)` + --> $DIR/trait-object-lifetime-parens.rs:9:19 + | +LL | let _: Box<('a) + Trait>; + | ^ expected `:` + +error: chained comparison operators require parentheses + --> $DIR/trait-object-lifetime-parens.rs:9:15 + | +LL | let _: Box<('a) + Trait>; + | ^^^^^^^^^^^^^^^ + | + = help: use `::<...>` instead of `<...>` if you meant to specify type arguments + = help: or use `(...)` if you meant to specify fn arguments + error: expected type, found `'a` --> $DIR/trait-object-lifetime-parens.rs:9:17 | @@ -18,5 +33,5 @@ LL | let _: Box<('a) + Trait>; | | | while parsing the type for `_` -error: aborting due to 3 previous errors +error: aborting due to 5 previous errors From b7dc8e71ccb15ff54b9cdf0f67776cc3cf4bea33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 28 Mar 2019 19:58:00 -0700 Subject: [PATCH 2/4] fix text after rebase --- src/test/ui/parser/recover-from-bad-variant.stderr | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/ui/parser/recover-from-bad-variant.stderr b/src/test/ui/parser/recover-from-bad-variant.stderr index bd4a562d72d19..1eba6d7d52877 100644 --- a/src/test/ui/parser/recover-from-bad-variant.stderr +++ b/src/test/ui/parser/recover-from-bad-variant.stderr @@ -3,6 +3,14 @@ error: expected type, found `3` | LL | let x = Enum::Foo(a: 3, b: 4); | ^ expecting a type here because of type ascription + | + = note: type ascription is a nightly-only feature that lets you annotate an expression with a type: `: ` +note: this expression expects an ascribed type after the colon + --> $DIR/recover-from-bad-variant.rs:7:23 + | +LL | let x = Enum::Foo(a: 3, b: 4); + | ^ + = help: this might be indicative of a syntax error elsewhere error[E0532]: expected tuple struct/variant, found struct variant `Enum::Foo` --> $DIR/recover-from-bad-variant.rs:10:9 From 9ea6790a6444fbf65d0cba9c9518abf70077f3de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 28 Mar 2019 19:58:45 -0700 Subject: [PATCH 3/4] Deduplicate parse recovery code --- src/libsyntax/parse/parser.rs | 78 +++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index ea0c7e0e36662..d4b8c7ce15941 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2639,11 +2639,9 @@ impl<'a> Parser<'a> { while self.token != token::CloseDelim(token::Paren) { es.push(match self.parse_expr() { Ok(es) => es, - Err(mut err) => { // recover from parse error in tuple list - err.emit(); - self.consume_block(token::Paren); - hi = self.prev_span; - return Ok(self.mk_expr(lo.to(hi), ExprKind::Err, ThinVec::new())); + Err(err) => { + // recover from parse error in tuple list + return Ok(self.recover_seq_parse_error(token::Paren, lo, Err(err))); } }); recovered = self.expect_one_of( @@ -3254,44 +3252,54 @@ impl<'a> Parser<'a> { } if self.expr_is_complete(&e) { break; } match self.token { - // expr(...) - token::OpenDelim(token::Paren) => { - match self.parse_unspanned_seq( - &token::OpenDelim(token::Paren), - &token::CloseDelim(token::Paren), - SeqSep::trailing_allowed(token::Comma), - |p| Ok(p.parse_expr()?) - ) { - Ok(es) => { + // expr(...) + token::OpenDelim(token::Paren) => { + let seq = self.parse_unspanned_seq( + &token::OpenDelim(token::Paren), + &token::CloseDelim(token::Paren), + SeqSep::trailing_allowed(token::Comma), + |p| Ok(p.parse_expr()?) + ).map(|es| { let nd = self.mk_call(e, es); - hi = self.prev_span; - e = self.mk_expr(lo.to(hi), nd, ThinVec::new()); - } - Err(mut err) => { // recover from parse error in argument list - err.emit(); - self.consume_block(token::Paren); - hi = self.prev_span; - e = self.mk_expr(lo.to(hi), ExprKind::Err, ThinVec::new()); - } + let hi = self.prev_span; + self.mk_expr(lo.to(hi), nd, ThinVec::new()) + }); + e = self.recover_seq_parse_error(token::Paren, lo, seq); } - } - // expr[...] - // Could be either an index expression or a slicing expression. - token::OpenDelim(token::Bracket) => { - self.bump(); - let ix = self.parse_expr()?; - hi = self.span; - self.expect(&token::CloseDelim(token::Bracket))?; - let index = self.mk_index(e, ix); - e = self.mk_expr(lo.to(hi), index, ThinVec::new()) - } - _ => return Ok(e) + // expr[...] + // Could be either an index expression or a slicing expression. + token::OpenDelim(token::Bracket) => { + self.bump(); + let ix = self.parse_expr()?; + hi = self.span; + self.expect(&token::CloseDelim(token::Bracket))?; + let index = self.mk_index(e, ix); + e = self.mk_expr(lo.to(hi), index, ThinVec::new()) + } + _ => return Ok(e) } } return Ok(e); } + fn recover_seq_parse_error( + &mut self, + delim: token::DelimToken, + lo: Span, + result: PResult<'a, P>, + ) -> P { + match result { + Ok(x) => x, + Err(mut err) => { + err.emit(); + // recover from parse error + self.consume_block(delim); + self.mk_expr(lo.to(self.prev_span), ExprKind::Err, ThinVec::new()) + } + } + } + crate fn process_potential_macro_variable(&mut self) { let (token, span) = match self.token { token::Dollar if self.span.ctxt() != syntax_pos::hygiene::SyntaxContext::empty() && From 3592079765d4d76f19b3a9501de5f8cc47f11a04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 28 Mar 2019 20:10:00 -0700 Subject: [PATCH 4/4] revert change to test file as per review request --- src/test/ui/parser/pat-tuple-1.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/ui/parser/pat-tuple-1.rs b/src/test/ui/parser/pat-tuple-1.rs index d76096c25988d..0e49b547f7d0f 100644 --- a/src/test/ui/parser/pat-tuple-1.rs +++ b/src/test/ui/parser/pat-tuple-1.rs @@ -1,6 +1,5 @@ fn main() { match (0, 1) { - (, ..) => {} - //~^ ERROR expected pattern, found `,` + (, ..) => {} //~ ERROR expected pattern, found `,` } }