11use super :: { Parser , PathStyle , TokenType } ;
22
3+ use crate :: errors:: { FnPtrWithGenerics , FnPtrWithGenericsSugg } ;
34use crate :: { maybe_recover_from_interpolated_ty_qpath, maybe_whole} ;
45
56use rustc_ast:: ptr:: P ;
@@ -270,14 +271,19 @@ impl<'a> Parser<'a> {
270271 TyKind :: Infer
271272 } else if self . check_fn_front_matter ( false , Case :: Sensitive ) {
272273 // Function pointer type
273- self . parse_ty_bare_fn ( lo, Vec :: new ( ) , recover_return_sign) ?
274+ self . parse_ty_bare_fn ( lo, Vec :: new ( ) , None , recover_return_sign) ?
274275 } else if self . check_keyword ( kw:: For ) {
275276 // Function pointer type or bound list (trait object type) starting with a poly-trait.
276277 // `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T`
277278 // `for<'lt> Trait1<'lt> + Trait2 + 'a`
278279 let lifetime_defs = self . parse_late_bound_lifetime_defs ( ) ?;
279280 if self . check_fn_front_matter ( false , Case :: Sensitive ) {
280- self . parse_ty_bare_fn ( lo, lifetime_defs, recover_return_sign) ?
281+ self . parse_ty_bare_fn (
282+ lo,
283+ lifetime_defs,
284+ Some ( self . prev_token . span . shrink_to_lo ( ) ) ,
285+ recover_return_sign,
286+ ) ?
281287 } else {
282288 let path = self . parse_path ( PathStyle :: Type ) ?;
283289 let parse_plus = allow_plus == AllowPlus :: Yes && self . check_plus ( ) ;
@@ -519,7 +525,8 @@ impl<'a> Parser<'a> {
519525 fn parse_ty_bare_fn (
520526 & mut self ,
521527 lo : Span ,
522- params : Vec < GenericParam > ,
528+ mut params : Vec < GenericParam > ,
529+ param_insertion_point : Option < Span > ,
523530 recover_return_sign : RecoverReturnSign ,
524531 ) -> PResult < ' a , TyKind > {
525532 let inherited_vis = rustc_ast:: Visibility {
@@ -530,6 +537,9 @@ impl<'a> Parser<'a> {
530537 let span_start = self . token . span ;
531538 let ast:: FnHeader { ext, unsafety, constness, asyncness } =
532539 self . parse_fn_front_matter ( & inherited_vis) ?;
540+ if self . may_recover ( ) && self . token . kind == TokenKind :: Lt {
541+ self . recover_fn_ptr_with_generics ( lo, & mut params, param_insertion_point) ?;
542+ }
533543 let decl = self . parse_fn_decl ( |_| false , AllowPlus :: No , recover_return_sign) ?;
534544 let whole_span = lo. to ( self . prev_token . span ) ;
535545 if let ast:: Const :: Yes ( span) = constness {
@@ -545,6 +555,48 @@ impl<'a> Parser<'a> {
545555 Ok ( TyKind :: BareFn ( P ( BareFnTy { ext, unsafety, generic_params : params, decl, decl_span } ) ) )
546556 }
547557
558+ /// Recover from function pointer types with a generic parameter list (e.g. `fn<'a>(&'a str)`).
559+ fn recover_fn_ptr_with_generics (
560+ & mut self ,
561+ lo : Span ,
562+ params : & mut Vec < GenericParam > ,
563+ param_insertion_point : Option < Span > ,
564+ ) -> PResult < ' a , ( ) > {
565+ let generics = self . parse_generics ( ) ?;
566+ let arity = generics. params . len ( ) ;
567+
568+ let mut lifetimes: Vec < _ > = generics
569+ . params
570+ . into_iter ( )
571+ . filter ( |param| matches ! ( param. kind, ast:: GenericParamKind :: Lifetime ) )
572+ . collect ( ) ;
573+
574+ let sugg = if !lifetimes. is_empty ( ) {
575+ let snippet =
576+ lifetimes. iter ( ) . map ( |param| param. ident . as_str ( ) ) . intersperse ( ", " ) . collect ( ) ;
577+
578+ let ( left, snippet) = if let Some ( span) = param_insertion_point {
579+ ( span, if params. is_empty ( ) { snippet } else { format ! ( ", {snippet}" ) } )
580+ } else {
581+ ( lo. shrink_to_lo ( ) , format ! ( "for<{snippet}> " ) )
582+ } ;
583+
584+ Some ( FnPtrWithGenericsSugg {
585+ left,
586+ snippet,
587+ right : generics. span ,
588+ arity,
589+ for_param_list_exists : param_insertion_point. is_some ( ) ,
590+ } )
591+ } else {
592+ None
593+ } ;
594+
595+ self . sess . emit_err ( FnPtrWithGenerics { span : generics. span , sugg } ) ;
596+ params. append ( & mut lifetimes) ;
597+ Ok ( ( ) )
598+ }
599+
548600 /// Emit an error for the given bad function pointer qualifier.
549601 fn error_fn_ptr_bad_qualifier ( & self , span : Span , qual_span : Span , qual : & str ) {
550602 self . struct_span_err ( span, & format ! ( "an `fn` pointer type cannot be `{}`" , qual) )
0 commit comments