From ff61949860813247b26d96eb374b41b46becba81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 25 Oct 2020 14:20:44 -0700 Subject: [PATCH] Tweak invalid `fn` header and body parsing * Recover empty `fn` bodies when encountering `}` * Recover trailing `>` in return types * Recover from non-type in array type `[; LEN]` --- compiler/rustc_parse/src/parser/item.rs | 38 +++++++++++++++++++------ compiler/rustc_parse/src/parser/ty.rs | 14 ++++++++- src/test/ui/issues/issue-39616.rs | 1 - src/test/ui/issues/issue-39616.stderr | 8 +----- src/test/ui/parser/issue-24780.rs | 7 +++-- src/test/ui/parser/issue-24780.stderr | 4 +-- src/test/ui/parser/issue-6610.stderr | 8 ++---- 7 files changed, 53 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 2b143cd7696..3e964709634 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1538,7 +1538,7 @@ impl<'a> Parser<'a> { generics.where_clause = self.parse_where_clause()?; // `where T: Ord` let mut sig_hi = self.prev_token.span; - let body = self.parse_fn_body(attrs, &mut sig_hi)?; // `;` or `{ ... }`. + let body = self.parse_fn_body(attrs, &ident, &mut sig_hi)?; // `;` or `{ ... }`. let fn_sig_span = sig_lo.to(sig_hi); Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, body)) } @@ -1549,6 +1549,7 @@ impl<'a> Parser<'a> { fn parse_fn_body( &mut self, attrs: &mut Vec, + ident: &Ident, sig_hi: &mut Span, ) -> PResult<'a, Option>> { let (inner_attrs, body) = if self.eat(&token::Semi) { @@ -1573,9 +1574,21 @@ impl<'a> Parser<'a> { .emit(); (Vec::new(), Some(self.mk_block_err(span))) } else { - return self - .expected_one_of_not_found(&[], &[token::Semi, token::OpenDelim(token::Brace)]) - .map(|_| None); + if let Err(mut err) = + self.expected_one_of_not_found(&[], &[token::Semi, token::OpenDelim(token::Brace)]) + { + if self.token.kind == token::CloseDelim(token::Brace) { + // The enclosing `mod`, `trait` or `impl` is being closed, so keep the `fn` in + // the AST for typechecking. + err.span_label(ident.span, "while parsing this `fn`"); + err.emit(); + (Vec::new(), None) + } else { + return Err(err); + } + } else { + unreachable!() + } }; attrs.extend(inner_attrs); Ok(body) @@ -1653,10 +1666,19 @@ impl<'a> Parser<'a> { req_name: ReqName, ret_allow_plus: AllowPlus, ) -> PResult<'a, P> { - Ok(P(FnDecl { - inputs: self.parse_fn_params(req_name)?, - output: self.parse_ret_ty(ret_allow_plus, RecoverQPath::Yes)?, - })) + let inputs = self.parse_fn_params(req_name)?; + let output = self.parse_ret_ty(ret_allow_plus, RecoverQPath::Yes)?; + + if let ast::FnRetTy::Ty(ty) = &output { + if let TyKind::Path(_, Path { segments, .. }) = &ty.kind { + if let [.., last] = &segments[..] { + // Detect and recover `fn foo() -> Vec> {}` + self.check_trailing_angle_brackets(last, &[&token::OpenDelim(token::Brace)]); + } + } + } + + Ok(P(FnDecl { inputs, output })) } /// Parses the parameter list of a function, including the `(` and `)` delimiters. diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index d42a786a18f..7a6ebca4e15 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -265,7 +265,19 @@ impl<'a> Parser<'a> { /// Parses an array (`[TYPE; EXPR]`) or slice (`[TYPE]`) type. /// The opening `[` bracket is already eaten. fn parse_array_or_slice_ty(&mut self) -> PResult<'a, TyKind> { - let elt_ty = self.parse_ty()?; + let elt_ty = match self.parse_ty() { + Ok(ty) => ty, + Err(mut err) + if self.look_ahead(1, |t| t.kind == token::CloseDelim(token::Bracket)) + | self.look_ahead(1, |t| t.kind == token::Semi) => + { + // Recover from `[LIT; EXPR]` and `[LIT]` + self.bump(); + err.emit(); + self.mk_ty(self.prev_token.span, TyKind::Err) + } + Err(err) => return Err(err), + }; let ty = if self.eat(&token::Semi) { TyKind::Array(elt_ty, self.parse_anon_const_expr()?) } else { diff --git a/src/test/ui/issues/issue-39616.rs b/src/test/ui/issues/issue-39616.rs index 9aebb4e2f48..46b5aa334ca 100644 --- a/src/test/ui/issues/issue-39616.rs +++ b/src/test/ui/issues/issue-39616.rs @@ -1,4 +1,3 @@ fn foo(a: [0; 1]) {} //~ ERROR expected type, found `0` -//~| ERROR expected one of `)`, `,`, `->`, `;`, `where`, or `{`, found `]` fn main() {} diff --git a/src/test/ui/issues/issue-39616.stderr b/src/test/ui/issues/issue-39616.stderr index fa8ef50a073..393d1f2e2ce 100644 --- a/src/test/ui/issues/issue-39616.stderr +++ b/src/test/ui/issues/issue-39616.stderr @@ -4,11 +4,5 @@ error: expected type, found `0` LL | fn foo(a: [0; 1]) {} | ^ expected type -error: expected one of `)`, `,`, `->`, `;`, `where`, or `{`, found `]` - --> $DIR/issue-39616.rs:1:16 - | -LL | fn foo(a: [0; 1]) {} - | ^ expected one of `)`, `,`, `->`, `;`, `where`, or `{` - -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/src/test/ui/parser/issue-24780.rs b/src/test/ui/parser/issue-24780.rs index 568a6c5f05c..20665b549d2 100644 --- a/src/test/ui/parser/issue-24780.rs +++ b/src/test/ui/parser/issue-24780.rs @@ -1,8 +1,9 @@ // Verify that '>' is not both expected and found at the same time, as it used // to happen in #24780. For example, following should be an error: -// expected one of ..., `>`, ... found `>` +// expected one of ..., `>`, ... found `>`. No longer exactly this, but keeping for posterity. -fn foo() -> Vec> { - //~^ ERROR expected one of `!`, `+`, `::`, `;`, `where`, or `{`, found `>` +fn foo() -> Vec> { //~ ERROR unmatched angle bracket Vec::new() } + +fn main() {} diff --git a/src/test/ui/parser/issue-24780.stderr b/src/test/ui/parser/issue-24780.stderr index bdd089bb7a1..d12b13d35f8 100644 --- a/src/test/ui/parser/issue-24780.stderr +++ b/src/test/ui/parser/issue-24780.stderr @@ -1,8 +1,8 @@ -error: expected one of `!`, `+`, `::`, `;`, `where`, or `{`, found `>` +error: unmatched angle bracket --> $DIR/issue-24780.rs:5:23 | LL | fn foo() -> Vec> { - | ^ expected one of `!`, `+`, `::`, `;`, `where`, or `{` + | ^^ help: remove extra angle bracket error: aborting due to previous error diff --git a/src/test/ui/parser/issue-6610.stderr b/src/test/ui/parser/issue-6610.stderr index acbecfded4b..4a3bc752553 100644 --- a/src/test/ui/parser/issue-6610.stderr +++ b/src/test/ui/parser/issue-6610.stderr @@ -2,11 +2,9 @@ error: expected one of `->`, `;`, `where`, or `{`, found `}` --> $DIR/issue-6610.rs:1:20 | LL | trait Foo { fn a() } - | - ^ - | | | - | | expected one of `->`, `;`, `where`, or `{` - | | the item list ends here - | while parsing this item list starting here + | - ^ expected one of `->`, `;`, `where`, or `{` + | | + | while parsing this `fn` error: aborting due to previous error