diff --git a/compiler/rustc_error_messages/locales/en-US/parser.ftl b/compiler/rustc_error_messages/locales/en-US/parser.ftl index 6d42b23fb3a21..815e8f4d3567e 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 d59982f7063f3..a39398950a533 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1280,3 +1280,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, +} + +#[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 3dcadb4c9115f..c78479b098ba0 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 4d78c5bd0e273..d6854f0702518 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, + mut params: Vec, + param_insertion_point: Option, 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, + param_insertion_point: Option, + ) -> 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 0000000000000..31de418be5f70 --- /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; + //~^ 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(); + //~^ 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(); + //~^ 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 0000000000000..1da9c18571b9e --- /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; + | ^^^ + +error: function pointer types may not have generic parameters + --> $DIR/recover-fn-ptr-with-generics.rs:10:14 + | +LL | let _: fn(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider moving the lifetime parameters to a `for` parameter list + | +LL - let _: fn(); +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(); + | ^ 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; + | ^ 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; + | ^ not found in this scope + +error: aborting due to 12 previous errors + +For more information about this error, try `rustc --explain E0412`.