@@ -387,26 +387,24 @@ fn emit_mismatch_diagnostic<'tcx>(
387387 build_mismatch_suggestion ( info. lifetime . ident . as_str ( ) , & suggest_change_to_explicit_bound)
388388 } ) ;
389389
390- let is_bound_static = bound_lifetime. is_some_and ( |info| info. lifetime . is_static ( ) ) ;
391-
392- tracing:: debug!( ?bound_lifetime, ?explicit_bound_suggestion, ?is_bound_static) ;
390+ tracing:: debug!( ?bound_lifetime, ?explicit_bound_suggestion) ;
393391
394392 let should_suggest_mixed =
395393 // Do we have a mixed case?
396394 ( saw_a_reference && saw_a_path) &&
397395 // Is there anything to change?
398396 ( !suggest_change_to_mixed_implicit. is_empty ( ) ||
399397 !suggest_change_to_mixed_explicit_anonymous. is_empty ( ) ) &&
400- // If we have `'static`, we don't want to remove it .
401- !is_bound_static ;
398+ // If we have a named lifetime, prefer consistent naming .
399+ bound_lifetime . is_none ( ) ;
402400
403401 let mixed_suggestion = should_suggest_mixed. then ( || {
404402 let implicit_suggestions = make_implicit_suggestions ( & suggest_change_to_mixed_implicit) ;
405403
406- let explicit_anonymous_suggestions = suggest_change_to_mixed_explicit_anonymous
407- . iter ( )
408- . map ( |info| info . suggestion ( "'_" ) )
409- . collect ( ) ;
404+ let explicit_anonymous_suggestions = build_mismatch_suggestions_for_lifetime (
405+ "'_" ,
406+ & suggest_change_to_mixed_explicit_anonymous ,
407+ ) ;
410408
411409 lints:: MismatchedLifetimeSyntaxesSuggestion :: Mixed {
412410 implicit_suggestions,
@@ -426,8 +424,8 @@ fn emit_mismatch_diagnostic<'tcx>(
426424 !suggest_change_to_implicit. is_empty ( ) &&
427425 // We never want to hide the lifetime in a path (or similar).
428426 allow_suggesting_implicit &&
429- // If we have `'static`, we don't want to remove it .
430- !is_bound_static ;
427+ // If we have a named lifetime, prefer consistent naming .
428+ bound_lifetime . is_none ( ) ;
431429
432430 let implicit_suggestion = should_suggest_implicit. then ( || {
433431 let suggestions = make_implicit_suggestions ( & suggest_change_to_implicit) ;
@@ -448,8 +446,10 @@ fn emit_mismatch_diagnostic<'tcx>(
448446 let should_suggest_explicit_anonymous =
449447 // Is there anything to change?
450448 !suggest_change_to_explicit_anonymous. is_empty ( ) &&
451- // If we have `'static`, we don't want to remove it.
452- !is_bound_static;
449+ // If we already have a mixed suggestion, avoid overlapping alternatives.
450+ mixed_suggestion. is_none ( ) &&
451+ // If we have a named lifetime, prefer consistent naming.
452+ bound_lifetime. is_none ( ) ;
453453
454454 let explicit_anonymous_suggestion = should_suggest_explicit_anonymous
455455 . then ( || build_mismatch_suggestion ( "'_" , & suggest_change_to_explicit_anonymous) ) ;
@@ -483,7 +483,7 @@ fn build_mismatch_suggestion(
483483) -> lints:: MismatchedLifetimeSyntaxesSuggestion {
484484 let lifetime_name = lifetime_name. to_owned ( ) ;
485485
486- let suggestions = infos . iter ( ) . map ( |info| info . suggestion ( & lifetime_name) ) . collect ( ) ;
486+ let suggestions = build_mismatch_suggestions_for_lifetime ( & lifetime_name, infos ) ;
487487
488488 lints:: MismatchedLifetimeSyntaxesSuggestion :: Explicit {
489489 lifetime_name,
@@ -492,6 +492,57 @@ fn build_mismatch_suggestion(
492492 }
493493}
494494
495+ fn build_mismatch_suggestions_for_lifetime (
496+ lifetime_name : & str ,
497+ infos : & [ & Info < ' _ > ] ,
498+ ) -> Vec < ( Span , String ) > {
499+ use hir:: { AngleBrackets , LifetimeSource , LifetimeSyntax } ;
500+
501+ #[ derive( Clone , Copy , PartialEq , Eq , Hash ) ]
502+ enum PathSuggestionKind {
503+ Missing ,
504+ Empty ,
505+ Full ,
506+ }
507+
508+ let mut suggestions = Vec :: new ( ) ;
509+ let mut path_counts: FxIndexMap < ( hir:: HirId , PathSuggestionKind ) , ( Span , usize ) > =
510+ FxIndexMap :: default ( ) ;
511+
512+ for info in infos {
513+ let lifetime = info. lifetime ;
514+ if matches ! ( lifetime. syntax, LifetimeSyntax :: Implicit ) {
515+ if let LifetimeSource :: Path { angle_brackets } = lifetime. source {
516+ let ( span, kind) = match angle_brackets {
517+ AngleBrackets :: Missing => {
518+ ( lifetime. ident . span . shrink_to_hi ( ) , PathSuggestionKind :: Missing )
519+ }
520+ AngleBrackets :: Empty => ( lifetime. ident . span , PathSuggestionKind :: Empty ) ,
521+ AngleBrackets :: Full => ( lifetime. ident . span , PathSuggestionKind :: Full ) ,
522+ } ;
523+ let entry = path_counts. entry ( ( info. ty . hir_id , kind) ) . or_insert ( ( span, 0 ) ) ;
524+ entry. 1 += 1 ;
525+ continue ;
526+ }
527+ }
528+ suggestions. push ( info. suggestion ( lifetime_name) ) ;
529+ }
530+
531+ for ( ( _ty_hir_id, kind) , ( span, count) ) in path_counts {
532+ let repeated = std:: iter:: repeat ( lifetime_name) . take ( count) . collect :: < Vec < _ > > ( ) . join ( ", " ) ;
533+
534+ let suggestion = match kind {
535+ PathSuggestionKind :: Missing => format ! ( "<{repeated}>" ) ,
536+ PathSuggestionKind :: Empty => repeated,
537+ PathSuggestionKind :: Full => format ! ( "{repeated}, " ) ,
538+ } ;
539+
540+ suggestions. push ( ( span, suggestion) ) ;
541+ }
542+
543+ suggestions
544+ }
545+
495546#[ derive( Debug ) ]
496547struct Info < ' tcx > {
497548 lifetime : & ' tcx hir:: Lifetime ,
0 commit comments