Change:
Newcomers might attempt to write a “generic” function pointer like . In such case, the compiler should provide a better error message. Inspired by a recent Reddit thread I sadly cannot find anymore. Given the following code: The compiler should mention that function pointers may not be generic or […] may not have generic parameters, suggest to move the lifetime(s) to a parameter list (this should be machine-applicable in my judgement) and be able to recover: For later reference, the current output is: Given the following code: The compiler should mention that function pointers may not be generic or […] may not have generic parameters and be able to recover emitting two diagnostics, one for each line. The compiler could also suggest moving the generic parameter list further up to the owning function, struct, etc. if available. Note that this might not be what the user intended (since there is a semantic difference between hypothetical higher-ranked generics and normal ones) and therefore shouldn't be machine-applicable. For later reference, the current output is: label A-parser A-suggestion-diagnostics D-newcomer-roadblock <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
claim

Discussion:
parser_double_colon_in_bound = expected `:` followed by trait or lifetime .suggestion = use single colon <ins> 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 </ins>

#[suggestion(code = ": ", applicability = "machine-applicable")] pub between: Span, } <ins> #[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, } </ins>

#![feature(array_windows)] #![feature(box_patterns)] #![feature(if_let_guard)] <ins> #![feature(iter_intersperse)] </ins> #![feature(let_chains)] #![feature(never_type)] #![feature(rustc_attrs)]

use super::{Parser, PathStyle, TokenType}; <ins> use crate::errors::{FnPtrWithGenerics, FnPtrWithGenericsSugg}; </ins> use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use rustc_ast::ptr::P;

TyKind::Infer } else if self.check_fn_front_matter(false, Case::Sensitive) { // Function pointer type <del> self.parse_ty_bare_fn(lo, Vec::new(), recover_return_sign)? </del> <ins> self.parse_ty_bare_fn(lo, Vec::new(), None, recover_return_sign)? </ins> } 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) { <del> self.parse_ty_bare_fn(lo, lifetime_defs, recover_return_sign)? </del> <ins> self.parse_ty_bare_fn( lo, lifetime_defs, Some(self.prev_token.span.shrink_to_lo()), recover_return_sign, )? </ins> } else { let path = self.parse_path(PathStyle::Type)?; let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus();

fn parse_ty_bare_fn( &mut self, lo: Span, <del> params: Vec<GenericParam>, </del> <ins> mut params: Vec<GenericParam>, param_insertion_point: Option<Span>, </ins> recover_return_sign: RecoverReturnSign, ) -> PResult<'a, TyKind> { let inherited_vis = rustc_ast::Visibility {

let span_start = self.token.span; let ast::FnHeader { ext, unsafety, constness, asyncness } = self.parse_fn_front_matter(&inherited_vis)?; <ins> if self.may_recover() && self.token.kind == TokenKind::Lt { self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?; } </ins> 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 {

Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl, decl_span }))) } <ins> /// 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(()) } </ins> /// 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))

<ins> 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 `>` } </ins>

<ins> 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`. </ins>