From af1146bd23634d34d953255114b1235e131f2c80 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Thu, 26 Mar 2020 09:31:29 +0100 Subject: [PATCH] parse: recover on `const fn()` / `async fn()`. --- src/librustc_parse/parser/item.rs | 4 +- src/librustc_parse/parser/ty.rs | 44 +++-- .../keyword-extern-as-identifier-type.rs | 2 +- .../keyword-extern-as-identifier-type.stderr | 6 +- src/test/ui/parser/issue-63116.stderr | 2 +- .../ui/parser/recover-const-async-fn-ptr.rs | 25 +++ .../parser/recover-const-async-fn-ptr.stderr | 155 ++++++++++++++++++ 7 files changed, 215 insertions(+), 23 deletions(-) create mode 100644 src/test/ui/parser/recover-const-async-fn-ptr.rs create mode 100644 src/test/ui/parser/recover-const-async-fn-ptr.stderr diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs index 7a4f76804157f..a0e82b7ca1e5f 100644 --- a/src/librustc_parse/parser/item.rs +++ b/src/librustc_parse/parser/item.rs @@ -1494,7 +1494,7 @@ impl<'a> Parser<'a> { } /// Is the current token the start of an `FnHeader` / not a valid parse? - fn check_fn_front_matter(&mut self) -> bool { + pub(super) fn check_fn_front_matter(&mut self) -> bool { // We use an over-approximation here. // `const const`, `fn const` won't parse, but we're not stepping over other syntax either. const QUALS: [Symbol; 4] = [kw::Const, kw::Async, kw::Unsafe, kw::Extern]; @@ -1521,7 +1521,7 @@ impl<'a> Parser<'a> { /// FnQual = "const"? "async"? "unsafe"? Extern? ; /// FnFrontMatter = FnQual? "fn" ; /// ``` - fn parse_fn_front_matter(&mut self) -> PResult<'a, FnHeader> { + pub(super) fn parse_fn_front_matter(&mut self) -> PResult<'a, FnHeader> { let constness = self.parse_constness(); let asyncness = self.parse_asyncness(); let unsafety = self.parse_unsafety(); diff --git a/src/librustc_parse/parser/ty.rs b/src/librustc_parse/parser/ty.rs index c21ac8d04f194..a6015504a3287 100644 --- a/src/librustc_parse/parser/ty.rs +++ b/src/librustc_parse/parser/ty.rs @@ -127,16 +127,16 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(kw::Underscore) { // A type to be inferred `_` TyKind::Infer - } else if self.token_is_bare_fn_keyword() { + } else if self.check_fn_front_matter() { // Function pointer type - self.parse_ty_bare_fn(Vec::new())? + self.parse_ty_bare_fn(lo, Vec::new())? } else if self.check_keyword(kw::For) { // Function pointer type or bound list (trait object type) starting with a poly-trait. // `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T` // `for<'lt> Trait1<'lt> + Trait2 + 'a` let lifetime_defs = self.parse_late_bound_lifetime_defs()?; - if self.token_is_bare_fn_keyword() { - self.parse_ty_bare_fn(lifetime_defs)? + if self.check_fn_front_matter() { + self.parse_ty_bare_fn(lo, lifetime_defs)? } else { let path = self.parse_path(PathStyle::Type)?; let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus(); @@ -291,13 +291,6 @@ impl<'a> Parser<'a> { Ok(TyKind::Typeof(expr)) } - /// Is the current token one of the keywords that signals a bare function type? - fn token_is_bare_fn_keyword(&mut self) -> bool { - self.check_keyword(kw::Fn) - || self.check_keyword(kw::Unsafe) - || self.check_keyword(kw::Extern) - } - /// Parses a function pointer type (`TyKind::BareFn`). /// ``` /// [unsafe] [extern "ABI"] fn (S) -> T @@ -306,12 +299,31 @@ impl<'a> Parser<'a> { /// | | | Return type /// Function Style ABI Parameter types /// ``` - fn parse_ty_bare_fn(&mut self, generic_params: Vec) -> PResult<'a, TyKind> { - let unsafety = self.parse_unsafety(); - let ext = self.parse_extern()?; - self.expect_keyword(kw::Fn)?; + /// We actually parse `FnHeader FnDecl`, but we error on `const` and `async` qualifiers. + fn parse_ty_bare_fn(&mut self, lo: Span, params: Vec) -> PResult<'a, TyKind> { + let ast::FnHeader { ext, unsafety, constness, asyncness } = self.parse_fn_front_matter()?; let decl = self.parse_fn_decl(|_| false, AllowPlus::No)?; - Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params, decl }))) + let whole_span = lo.to(self.prev_token.span); + if let ast::Const::Yes(span) = constness { + self.error_fn_ptr_bad_qualifier(whole_span, span, "const"); + } + if let ast::Async::Yes { span, .. } = asyncness { + self.error_fn_ptr_bad_qualifier(whole_span, span, "async"); + } + Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl }))) + } + + /// Emit an error for the given bad function pointer qualifier. + fn error_fn_ptr_bad_qualifier(&self, span: Span, qual_span: Span, qual: &str) { + self.struct_span_err(span, &format!("an `fn` pointer type cannot be `{}`", qual)) + .span_label(qual_span, format!("`{}` because of this", qual)) + .span_suggestion_short( + qual_span, + &format!("remove the `{}` qualifier", qual), + String::new(), + Applicability::MaybeIncorrect, + ) + .emit(); } /// Parses an `impl B0 + ... + Bn` type. diff --git a/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.rs b/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.rs index 3845a9aa017ce..12aa059766b06 100644 --- a/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.rs +++ b/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.rs @@ -1,3 +1,3 @@ -type A = extern::foo::bar; //~ ERROR expected `fn`, found `::` +type A = extern::foo::bar; //~ ERROR expected type, found keyword `extern` fn main() {} diff --git a/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.stderr b/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.stderr index 48c2f556f1dd9..20ecf6bac764c 100644 --- a/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.stderr +++ b/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.stderr @@ -1,8 +1,8 @@ -error: expected `fn`, found `::` - --> $DIR/keyword-extern-as-identifier-type.rs:1:16 +error: expected type, found keyword `extern` + --> $DIR/keyword-extern-as-identifier-type.rs:1:10 | LL | type A = extern::foo::bar; - | ^^ expected `fn` + | ^^^^^^ expected type error: aborting due to previous error diff --git a/src/test/ui/parser/issue-63116.stderr b/src/test/ui/parser/issue-63116.stderr index 15cd3df860b0d..80a450dbd3691 100644 --- a/src/test/ui/parser/issue-63116.stderr +++ b/src/test/ui/parser/issue-63116.stderr @@ -12,7 +12,7 @@ error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `;` LL | impl W `, `...`, `::`, `<`, `>`, `?`, `[`, `_`, `dyn`, `extern`, `fn`, `for`, `impl`, `unsafe`, lifetime, or path, found `;` +error: expected one of `!`, `&&`, `&`, `(`, `)`, `*`, `+`, `,`, `->`, `...`, `::`, `<`, `>`, `?`, `[`, `_`, `async`, `const`, `dyn`, `extern`, `fn`, `for`, `impl`, `unsafe`, lifetime, or path, found `;` --> $DIR/issue-63116.rs:3:15 | LL | impl W const fn(); //~ ERROR an `fn` pointer type cannot be `const` +type FT1 = for<'a> const extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` +type FT2 = for<'a> const unsafe extern fn(); //~ ERROR an `fn` pointer type cannot be `const` +type FT3 = for<'a> async fn(); //~ ERROR an `fn` pointer type cannot be `async` +type FT4 = for<'a> async extern fn(); //~ ERROR an `fn` pointer type cannot be `async` +type FT5 = for<'a> async unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` +type FT6 = for<'a> const async unsafe extern "C" fn(); +//~^ ERROR an `fn` pointer type cannot be `const` +//~| ERROR an `fn` pointer type cannot be `async` + +fn main() { + let _recovery_witness: () = 0; //~ ERROR mismatched types +} diff --git a/src/test/ui/parser/recover-const-async-fn-ptr.stderr b/src/test/ui/parser/recover-const-async-fn-ptr.stderr new file mode 100644 index 0000000000000..7012096b64450 --- /dev/null +++ b/src/test/ui/parser/recover-const-async-fn-ptr.stderr @@ -0,0 +1,155 @@ +error: an `fn` pointer type cannot be `const` + --> $DIR/recover-const-async-fn-ptr.rs:3:11 + | +LL | type T0 = const fn(); + | -----^^^^^ + | | + | `const` because of this + | help: remove the `const` qualifier + +error: an `fn` pointer type cannot be `const` + --> $DIR/recover-const-async-fn-ptr.rs:4:11 + | +LL | type T1 = const extern "C" fn(); + | -----^^^^^^^^^^^^^^^^ + | | + | `const` because of this + | help: remove the `const` qualifier + +error: an `fn` pointer type cannot be `const` + --> $DIR/recover-const-async-fn-ptr.rs:5:11 + | +LL | type T2 = const unsafe extern fn(); + | -----^^^^^^^^^^^^^^^^^^^ + | | + | `const` because of this + | help: remove the `const` qualifier + +error: an `fn` pointer type cannot be `async` + --> $DIR/recover-const-async-fn-ptr.rs:6:11 + | +LL | type T3 = async fn(); + | -----^^^^^ + | | + | `async` because of this + | help: remove the `async` qualifier + +error: an `fn` pointer type cannot be `async` + --> $DIR/recover-const-async-fn-ptr.rs:7:11 + | +LL | type T4 = async extern fn(); + | -----^^^^^^^^^^^^ + | | + | `async` because of this + | help: remove the `async` qualifier + +error: an `fn` pointer type cannot be `async` + --> $DIR/recover-const-async-fn-ptr.rs:8:11 + | +LL | type T5 = async unsafe extern "C" fn(); + | -----^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `async` because of this + | help: remove the `async` qualifier + +error: an `fn` pointer type cannot be `const` + --> $DIR/recover-const-async-fn-ptr.rs:9:11 + | +LL | type T6 = const async unsafe extern "C" fn(); + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `const` because of this + | help: remove the `const` qualifier + +error: an `fn` pointer type cannot be `async` + --> $DIR/recover-const-async-fn-ptr.rs:9:11 + | +LL | type T6 = const async unsafe extern "C" fn(); + | ^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `async` because of this + | help: remove the `async` qualifier + +error: an `fn` pointer type cannot be `const` + --> $DIR/recover-const-async-fn-ptr.rs:13:12 + | +LL | type FT0 = for<'a> const fn(); + | ^^^^^^^^-----^^^^^ + | | + | `const` because of this + | help: remove the `const` qualifier + +error: an `fn` pointer type cannot be `const` + --> $DIR/recover-const-async-fn-ptr.rs:14:12 + | +LL | type FT1 = for<'a> const extern "C" fn(); + | ^^^^^^^^-----^^^^^^^^^^^^^^^^ + | | + | `const` because of this + | help: remove the `const` qualifier + +error: an `fn` pointer type cannot be `const` + --> $DIR/recover-const-async-fn-ptr.rs:15:12 + | +LL | type FT2 = for<'a> const unsafe extern fn(); + | ^^^^^^^^-----^^^^^^^^^^^^^^^^^^^ + | | + | `const` because of this + | help: remove the `const` qualifier + +error: an `fn` pointer type cannot be `async` + --> $DIR/recover-const-async-fn-ptr.rs:16:12 + | +LL | type FT3 = for<'a> async fn(); + | ^^^^^^^^-----^^^^^ + | | + | `async` because of this + | help: remove the `async` qualifier + +error: an `fn` pointer type cannot be `async` + --> $DIR/recover-const-async-fn-ptr.rs:17:12 + | +LL | type FT4 = for<'a> async extern fn(); + | ^^^^^^^^-----^^^^^^^^^^^^ + | | + | `async` because of this + | help: remove the `async` qualifier + +error: an `fn` pointer type cannot be `async` + --> $DIR/recover-const-async-fn-ptr.rs:18:12 + | +LL | type FT5 = for<'a> async unsafe extern "C" fn(); + | ^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `async` because of this + | help: remove the `async` qualifier + +error: an `fn` pointer type cannot be `const` + --> $DIR/recover-const-async-fn-ptr.rs:19:12 + | +LL | type FT6 = for<'a> const async unsafe extern "C" fn(); + | ^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `const` because of this + | help: remove the `const` qualifier + +error: an `fn` pointer type cannot be `async` + --> $DIR/recover-const-async-fn-ptr.rs:19:12 + | +LL | type FT6 = for<'a> const async unsafe extern "C" fn(); + | ^^^^^^^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `async` because of this + | help: remove the `async` qualifier + +error[E0308]: mismatched types + --> $DIR/recover-const-async-fn-ptr.rs:24:33 + | +LL | let _recovery_witness: () = 0; + | -- ^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 17 previous errors + +For more information about this error, try `rustc --explain E0308`.