diff options
| -rw-r--r-- | compiler/rustc_error_messages/locales/en-US/parser.ftl | 9 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/errors.rs | 21 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/lib.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/ty.rs | 58 | ||||
| -rw-r--r-- | src/test/ui/parser/recover-fn-ptr-with-generics.rs | 31 | ||||
| -rw-r--r-- | src/test/ui/parser/recover-fn-ptr-with-generics.stderr | 111 |
6 files changed, 228 insertions, 3 deletions
diff --git a/compiler/rustc_error_messages/locales/en-US/parser.ftl b/compiler/rustc_error_messages/locales/en-US/parser.ftl index 6d42b23fb3a..815e8f4d356 100644 --- a/compiler/rustc_error_messages/locales/en-US/parser.ftl +++ b/compiler/rustc_error_messages/locales/en-US/parser.ftl @@ -375,3 +375,12 @@ parser_async_move_order_incorrect = the order of `move` and `async` is incorrect parser_double_colon_in_bound = expected `:` followed by trait or lifetime .suggestion = use single colon + +parser_fn_ptr_with_generics = function pointer types may not have generic parameters + .suggestion = consider moving the lifetime {$arity -> + [one] parameter + *[other] parameters + } to {$for_param_list_exists -> + [true] the + *[false] a + } `for` parameter list diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index e3acc11811f..69fe805e9cd 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1278,3 +1278,24 @@ pub(crate) struct DoubleColonInBound { #[suggestion(code = ": ", applicability = "machine-applicable")] pub between: Span, } + +#[derive(Diagnostic)] +#[diag(parser_fn_ptr_with_generics)] +pub(crate) struct FnPtrWithGenerics { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub sugg: Option<FnPtrWithGenericsSugg>, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(suggestion, applicability = "maybe-incorrect")] +pub(crate) struct FnPtrWithGenericsSugg { + #[suggestion_part(code = "{snippet}")] + pub left: Span, + pub snippet: String, + #[suggestion_part(code = "")] + pub right: Span, + pub arity: usize, + pub for_param_list_exists: bool, +} diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 3dcadb4c911..c78479b098b 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -3,6 +3,7 @@ #![feature(array_windows)] #![feature(box_patterns)] #![feature(if_let_guard)] +#![feature(iter_intersperse)] #![feature(let_chains)] #![feature(never_type)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 4d78c5bd0e2..d6854f07025 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -1,5 +1,6 @@ use super::{Parser, PathStyle, TokenType}; +use crate::errors::{FnPtrWithGenerics, FnPtrWithGenericsSugg}; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use rustc_ast::ptr::P; @@ -270,14 +271,19 @@ impl<'a> Parser<'a> { TyKind::Infer } else if self.check_fn_front_matter(false, Case::Sensitive) { // Function pointer type - self.parse_ty_bare_fn(lo, Vec::new(), recover_return_sign)? + self.parse_ty_bare_fn(lo, Vec::new(), None, recover_return_sign)? } 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.check_fn_front_matter(false, Case::Sensitive) { - self.parse_ty_bare_fn(lo, lifetime_defs, recover_return_sign)? + self.parse_ty_bare_fn( + lo, + lifetime_defs, + Some(self.prev_token.span.shrink_to_lo()), + recover_return_sign, + )? } else { let path = self.parse_path(PathStyle::Type)?; let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus(); @@ -519,7 +525,8 @@ impl<'a> Parser<'a> { fn parse_ty_bare_fn( &mut self, lo: Span, - params: Vec<GenericParam>, + mut params: Vec<GenericParam>, + param_insertion_point: Option<Span>, recover_return_sign: RecoverReturnSign, ) -> PResult<'a, TyKind> { let inherited_vis = rustc_ast::Visibility { @@ -530,6 +537,9 @@ impl<'a> Parser<'a> { let span_start = self.token.span; let ast::FnHeader { ext, unsafety, constness, asyncness } = self.parse_fn_front_matter(&inherited_vis)?; + if self.may_recover() && self.token.kind == TokenKind::Lt { + self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?; + } let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?; let whole_span = lo.to(self.prev_token.span); if let ast::Const::Yes(span) = constness { @@ -545,6 +555,48 @@ impl<'a> Parser<'a> { Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl, decl_span }))) } + /// Recover from function pointer types with a generic parameter list (e.g. `fn<'a>(&'a str)`). + fn recover_fn_ptr_with_generics( + &mut self, + lo: Span, + params: &mut Vec<GenericParam>, + param_insertion_point: Option<Span>, + ) -> PResult<'a, ()> { + let generics = self.parse_generics()?; + let arity = generics.params.len(); + + let mut lifetimes: Vec<_> = generics + .params + .into_iter() + .filter(|param| matches!(param.kind, ast::GenericParamKind::Lifetime)) + .collect(); + + let sugg = if !lifetimes.is_empty() { + let snippet = + lifetimes.iter().map(|param| param.ident.as_str()).intersperse(", ").collect(); + + let (left, snippet) = if let Some(span) = param_insertion_point { + (span, if params.is_empty() { snippet } else { format!(", {snippet}") }) + } else { + (lo.shrink_to_lo(), format!("for<{snippet}> ")) + }; + + Some(FnPtrWithGenericsSugg { + left, + snippet, + right: generics.span, + arity, + for_param_list_exists: param_insertion_point.is_some(), + }) + } else { + None + }; + + self.sess.emit_err(FnPtrWithGenerics { span: generics.span, sugg }); + params.append(&mut lifetimes); + Ok(()) + } + /// 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)) diff --git a/src/test/ui/parser/recover-fn-ptr-with-generics.rs b/src/test/ui/parser/recover-fn-ptr-with-generics.rs new file mode 100644 index 00000000000..31de418be5f --- /dev/null +++ b/src/test/ui/parser/recover-fn-ptr-with-generics.rs @@ -0,0 +1,31 @@ +fn main() { + type Predicate = fn<'a>(&'a str) -> bool; + //~^ ERROR function pointer types may not have generic parameters + + type Identity = fn<T>(T) -> T; + //~^ ERROR function pointer types may not have generic parameters + //~| ERROR cannot find type `T` in this scope + //~| ERROR cannot find type `T` in this scope + + let _: fn<const N: usize, 'e, Q, 'f>(); + //~^ ERROR function pointer types may not have generic parameters + + let _: for<'outer> fn<'inner>(); + //~^ ERROR function pointer types may not have generic parameters + + let _: for<> fn<'r>(); + //~^ ERROR function pointer types may not have generic parameters + + type Hmm = fn<>(); + //~^ ERROR function pointer types may not have generic parameters + + let _: extern fn<'a: 'static>(); + //~^ ERROR function pointer types may not have generic parameters + //~| ERROR lifetime bounds cannot be used in this context + + let _: for<'any> extern "C" fn<'u>(); + //~^ ERROR function pointer types may not have generic parameters + + type QuiteBroken = fn<const>(); + //~^ ERROR expected identifier, found `>` +} diff --git a/src/test/ui/parser/recover-fn-ptr-with-generics.stderr b/src/test/ui/parser/recover-fn-ptr-with-generics.stderr new file mode 100644 index 00000000000..1da9c18571b --- /dev/null +++ b/src/test/ui/parser/recover-fn-ptr-with-generics.stderr @@ -0,0 +1,111 @@ +error: function pointer types may not have generic parameters + --> $DIR/recover-fn-ptr-with-generics.rs:2:24 + | +LL | type Predicate = fn<'a>(&'a str) -> bool; + | ^^^^ + | +help: consider moving the lifetime parameter to a `for` parameter list + | +LL - type Predicate = fn<'a>(&'a str) -> bool; +LL + type Predicate = for<'a> fn(&'a str) -> bool; + | + +error: function pointer types may not have generic parameters + --> $DIR/recover-fn-ptr-with-generics.rs:5:23 + | +LL | type Identity = fn<T>(T) -> T; + | ^^^ + +error: function pointer types may not have generic parameters + --> $DIR/recover-fn-ptr-with-generics.rs:10:14 + | +LL | let _: fn<const N: usize, 'e, Q, 'f>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider moving the lifetime parameters to a `for` parameter list + | +LL - let _: fn<const N: usize, 'e, Q, 'f>(); +LL + let _: for<'e, 'f> fn(); + | + +error: function pointer types may not have generic parameters + --> $DIR/recover-fn-ptr-with-generics.rs:13:26 + | +LL | let _: for<'outer> fn<'inner>(); + | ^^^^^^^^ + | +help: consider moving the lifetime parameter to the `for` parameter list + | +LL - let _: for<'outer> fn<'inner>(); +LL + let _: for<'outer, 'inner> fn(); + | + +error: function pointer types may not have generic parameters + --> $DIR/recover-fn-ptr-with-generics.rs:16:20 + | +LL | let _: for<> fn<'r>(); + | ^^^^ + | +help: consider moving the lifetime parameter to the `for` parameter list + | +LL - let _: for<> fn<'r>(); +LL + let _: for<'r> fn(); + | + +error: function pointer types may not have generic parameters + --> $DIR/recover-fn-ptr-with-generics.rs:19:18 + | +LL | type Hmm = fn<>(); + | ^^ + +error: function pointer types may not have generic parameters + --> $DIR/recover-fn-ptr-with-generics.rs:22:21 + | +LL | let _: extern fn<'a: 'static>(); + | ^^^^^^^^^^^^^ + | +help: consider moving the lifetime parameter to a `for` parameter list + | +LL - let _: extern fn<'a: 'static>(); +LL + let _: for<'a> extern fn(); + | + +error: function pointer types may not have generic parameters + --> $DIR/recover-fn-ptr-with-generics.rs:26:35 + | +LL | let _: for<'any> extern "C" fn<'u>(); + | ^^^^ + | +help: consider moving the lifetime parameter to the `for` parameter list + | +LL - let _: for<'any> extern "C" fn<'u>(); +LL + let _: for<'any, 'u> extern "C" fn(); + | + +error: expected identifier, found `>` + --> $DIR/recover-fn-ptr-with-generics.rs:29:32 + | +LL | type QuiteBroken = fn<const>(); + | ^ expected identifier + +error: lifetime bounds cannot be used in this context + --> $DIR/recover-fn-ptr-with-generics.rs:22:26 + | +LL | let _: extern fn<'a: 'static>(); + | ^^^^^^^ + +error[E0412]: cannot find type `T` in this scope + --> $DIR/recover-fn-ptr-with-generics.rs:5:27 + | +LL | type Identity = fn<T>(T) -> T; + | ^ not found in this scope + +error[E0412]: cannot find type `T` in this scope + --> $DIR/recover-fn-ptr-with-generics.rs:5:33 + | +LL | type Identity = fn<T>(T) -> T; + | ^ not found in this scope + +error: aborting due to 12 previous errors + +For more information about this error, try `rustc --explain E0412`. |
