11use hir:: GenericParamKind ;
2+ use rustc_data_structures:: fx:: FxHashSet ;
23use rustc_errors:: {
34 codes:: * , Applicability , Diag , DiagMessage , DiagStyledString , EmissionGuarantee , IntoDiagArg ,
45 MultiSpan , SubdiagMessageOp , Subdiagnostic ,
56} ;
67use rustc_hir as hir;
8+ use rustc_hir:: intravisit:: { walk_ty, Visitor } ;
79use rustc_hir:: FnRetTy ;
810use rustc_macros:: { Diagnostic , Subdiagnostic } ;
911use rustc_middle:: ty:: print:: TraitRefPrintOnlyTraitPath ;
@@ -355,31 +357,33 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
355357 _f : & F ,
356358 ) {
357359 let mut mk_suggestion = || {
358- let (
359- hir:: Ty { kind : hir:: TyKind :: Ref ( lifetime_sub, _) , .. } ,
360- hir:: Ty { kind : hir:: TyKind :: Ref ( lifetime_sup, _) , .. } ,
361- ) = ( self . ty_sub , self . ty_sup )
362- else {
363- return false ;
364- } ;
365-
366- if !lifetime_sub. is_anonymous ( ) || !lifetime_sup. is_anonymous ( ) {
367- return false ;
368- } ;
369-
370360 let Some ( anon_reg) = self . tcx . is_suitable_region ( self . sub ) else {
371361 return false ;
372362 } ;
373363
374364 let node = self . tcx . hir_node_by_def_id ( anon_reg. def_id ) ;
375365 let is_impl = matches ! ( & node, hir:: Node :: ImplItem ( _) ) ;
376- let generics = match node {
366+ let ( generics, parent_generics ) = match node {
377367 hir:: Node :: Item ( & hir:: Item {
378368 kind : hir:: ItemKind :: Fn ( _, ref generics, ..) ,
379369 ..
380370 } )
381371 | hir:: Node :: TraitItem ( & hir:: TraitItem { ref generics, .. } )
382- | hir:: Node :: ImplItem ( & hir:: ImplItem { ref generics, .. } ) => generics,
372+ | hir:: Node :: ImplItem ( & hir:: ImplItem { ref generics, .. } ) => (
373+ generics,
374+ match self . tcx . parent_hir_node ( self . tcx . local_def_id_to_hir_id ( anon_reg. def_id ) )
375+ {
376+ hir:: Node :: Item ( hir:: Item {
377+ kind : hir:: ItemKind :: Trait ( _, _, ref generics, ..) ,
378+ ..
379+ } )
380+ | hir:: Node :: Item ( hir:: Item {
381+ kind : hir:: ItemKind :: Impl ( hir:: Impl { ref generics, .. } ) ,
382+ ..
383+ } ) => Some ( generics) ,
384+ _ => None ,
385+ } ,
386+ ) ,
383387 _ => return false ,
384388 } ;
385389
@@ -390,24 +394,112 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
390394 . map ( |p| p. name . ident ( ) . name )
391395 . find ( |i| * i != kw:: UnderscoreLifetime ) ;
392396 let introduce_new = suggestion_param_name. is_none ( ) ;
397+
398+ let mut default = "'a" . to_string ( ) ;
399+ if let Some ( parent_generics) = parent_generics {
400+ let used: FxHashSet < _ > = parent_generics
401+ . params
402+ . iter ( )
403+ . filter ( |p| matches ! ( p. kind, GenericParamKind :: Lifetime { .. } ) )
404+ . map ( |p| p. name . ident ( ) . name )
405+ . filter ( |i| * i != kw:: UnderscoreLifetime )
406+ . map ( |l| l. to_string ( ) )
407+ . collect ( ) ;
408+ if let Some ( lt) =
409+ ( 'a' ..='z' ) . map ( |it| format ! ( "'{it}" ) ) . find ( |it| !used. contains ( it) )
410+ {
411+ // We want a lifetime that *isn't* present in the `trait` or `impl` that assoc
412+ // `fn` belongs to. We could suggest reusing one of their lifetimes, but it is
413+ // likely to be an over-constraining lifetime requirement, so we always add a
414+ // lifetime to the `fn`.
415+ default = lt;
416+ }
417+ }
393418 let suggestion_param_name =
394- suggestion_param_name. map ( |n| n. to_string ( ) ) . unwrap_or_else ( || "'a" . to_owned ( ) ) ;
395-
396- debug ! ( ?lifetime_sup. ident. span) ;
397- debug ! ( ?lifetime_sub. ident. span) ;
398- let make_suggestion = |ident : Ident | {
399- let sugg = if ident. name == kw:: Empty {
400- format ! ( "{suggestion_param_name}, " )
401- } else if ident. name == kw:: UnderscoreLifetime && ident. span . is_empty ( ) {
402- format ! ( "{suggestion_param_name} " )
403- } else {
404- suggestion_param_name. clone ( )
405- } ;
406- ( ident. span , sugg)
407- } ;
408- let mut suggestions =
409- vec ! [ make_suggestion( lifetime_sub. ident) , make_suggestion( lifetime_sup. ident) ] ;
419+ suggestion_param_name. map ( |n| n. to_string ( ) ) . unwrap_or_else ( || default) ;
420+
421+ struct ImplicitLifetimeFinder {
422+ suggestions : Vec < ( Span , String ) > ,
423+ suggestion_param_name : String ,
424+ }
410425
426+ impl < ' v > Visitor < ' v > for ImplicitLifetimeFinder {
427+ fn visit_ty ( & mut self , ty : & ' v hir:: Ty < ' v > ) {
428+ let make_suggestion = |ident : Ident | {
429+ if ident. name == kw:: Empty && ident. span . is_empty ( ) {
430+ format ! ( "{}, " , self . suggestion_param_name)
431+ } else if ident. name == kw:: UnderscoreLifetime && ident. span . is_empty ( ) {
432+ format ! ( "{} " , self . suggestion_param_name)
433+ } else {
434+ self . suggestion_param_name . clone ( )
435+ }
436+ } ;
437+ match ty. kind {
438+ hir:: TyKind :: Path ( hir:: QPath :: Resolved ( _, path) ) => {
439+ for segment in path. segments {
440+ if let Some ( args) = segment. args {
441+ if args. args . iter ( ) . all ( |arg| {
442+ matches ! (
443+ arg,
444+ hir:: GenericArg :: Lifetime ( lifetime)
445+ if lifetime. ident. name == kw:: Empty
446+ )
447+ } ) {
448+ self . suggestions . push ( (
449+ segment. ident . span . shrink_to_hi ( ) ,
450+ format ! (
451+ "<{}>" ,
452+ args. args
453+ . iter( )
454+ . map( |_| self . suggestion_param_name. clone( ) )
455+ . collect:: <Vec <_>>( )
456+ . join( ", " )
457+ ) ,
458+ ) ) ;
459+ } else {
460+ for arg in args. args {
461+ if let hir:: GenericArg :: Lifetime ( lifetime) = arg
462+ && lifetime. is_anonymous ( )
463+ {
464+ self . suggestions . push ( (
465+ lifetime. ident . span ,
466+ make_suggestion ( lifetime. ident ) ,
467+ ) ) ;
468+ }
469+ }
470+ }
471+ }
472+ }
473+ }
474+ hir:: TyKind :: Ref ( lifetime, ..) if lifetime. is_anonymous ( ) => {
475+ self . suggestions
476+ . push ( ( lifetime. ident . span , make_suggestion ( lifetime. ident ) ) ) ;
477+ }
478+ _ => { }
479+ }
480+ walk_ty ( self , ty) ;
481+ }
482+ }
483+ let mut visitor = ImplicitLifetimeFinder {
484+ suggestions : vec ! [ ] ,
485+ suggestion_param_name : suggestion_param_name. clone ( ) ,
486+ } ;
487+ if let Some ( fn_decl) = node. fn_decl ( )
488+ && let hir:: FnRetTy :: Return ( ty) = fn_decl. output
489+ {
490+ visitor. visit_ty ( ty) ;
491+ }
492+ if visitor. suggestions . is_empty ( ) {
493+ // Do not suggest constraining the `&self` param, but rather the return type.
494+ // If that is wrong (because it is not sufficient), a follow up error will tell the
495+ // user to fix it. This way we lower the chances of *over* constraining, but still
496+ // get the cake of "correctly" contrained in two steps.
497+ visitor. visit_ty ( self . ty_sup ) ;
498+ }
499+ visitor. visit_ty ( self . ty_sub ) ;
500+ if visitor. suggestions . is_empty ( ) {
501+ return false ;
502+ }
411503 if introduce_new {
412504 let new_param_suggestion = if let Some ( first) =
413505 generics. params . iter ( ) . find ( |p| !p. name . ident ( ) . span . is_empty ( ) )
@@ -417,15 +509,16 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
417509 ( generics. span , format ! ( "<{suggestion_param_name}>" ) )
418510 } ;
419511
420- suggestions. push ( new_param_suggestion) ;
512+ visitor . suggestions . push ( new_param_suggestion) ;
421513 }
422-
423- diag. multipart_suggestion (
514+ diag. multipart_suggestion_verbose (
424515 fluent:: infer_lifetime_param_suggestion,
425- suggestions,
516+ visitor . suggestions ,
426517 Applicability :: MaybeIncorrect ,
427518 ) ;
428519 diag. arg ( "is_impl" , is_impl) ;
520+ diag. arg ( "is_reuse" , !introduce_new) ;
521+
429522 true
430523 } ;
431524 if mk_suggestion ( ) && self . add_note {
0 commit comments