@@ -17,6 +17,7 @@ use rustc_ast::{
1717} ;
1818use rustc_ast_pretty:: pprust:: where_bound_predicate_to_string;
1919use rustc_data_structures:: fx:: FxHashSet ;
20+ use rustc_data_structures:: fx:: FxIndexSet ;
2021use rustc_errors:: {
2122 codes:: * , pluralize, struct_span_code_err, Applicability , Diag , ErrorGuaranteed , MultiSpan ,
2223 SuggestionStyle ,
@@ -31,7 +32,7 @@ use rustc_span::edit_distance::find_best_match_for_name;
3132use rustc_span:: edition:: Edition ;
3233use rustc_span:: hygiene:: MacroKind ;
3334use rustc_span:: symbol:: { kw, sym, Ident , Symbol } ;
34- use rustc_span:: Span ;
35+ use rustc_span:: { Span , DUMMY_SP } ;
3536
3637use rustc_middle:: ty;
3738
@@ -2714,8 +2715,17 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
27142715 self . suggest_introducing_lifetime (
27152716 & mut err,
27162717 Some ( lifetime_ref. ident . name . as_str ( ) ) ,
2717- |err, _, span, message, suggestion| {
2718- err. span_suggestion ( span, message, suggestion, Applicability :: MaybeIncorrect ) ;
2718+ |err, _, span, message, suggestion, span_suggs| {
2719+ err. multipart_suggestion_with_style (
2720+ message,
2721+ std:: iter:: once ( ( span, suggestion) ) . chain ( span_suggs. clone ( ) ) . collect ( ) ,
2722+ Applicability :: MaybeIncorrect ,
2723+ if span_suggs. is_empty ( ) {
2724+ SuggestionStyle :: ShowCode
2725+ } else {
2726+ SuggestionStyle :: ShowAlways
2727+ } ,
2728+ ) ;
27192729 true
27202730 } ,
27212731 ) ;
@@ -2726,13 +2736,20 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
27262736 & self ,
27272737 err : & mut Diag < ' _ > ,
27282738 name : Option < & str > ,
2729- suggest : impl Fn ( & mut Diag < ' _ > , bool , Span , Cow < ' static , str > , String ) -> bool ,
2739+ suggest : impl Fn (
2740+ & mut Diag < ' _ > ,
2741+ bool ,
2742+ Span ,
2743+ Cow < ' static , str > ,
2744+ String ,
2745+ Vec < ( Span , String ) > ,
2746+ ) -> bool ,
27302747 ) {
27312748 let mut suggest_note = true ;
27322749 for rib in self . lifetime_ribs . iter ( ) . rev ( ) {
27332750 let mut should_continue = true ;
27342751 match rib. kind {
2735- LifetimeRibKind :: Generics { binder : _ , span, kind } => {
2752+ LifetimeRibKind :: Generics { binder, span, kind } => {
27362753 // Avoid suggesting placing lifetime parameters on constant items unless the relevant
27372754 // feature is enabled. Suggest the parent item as a possible location if applicable.
27382755 if let LifetimeBinderKind :: ConstItem = kind
@@ -2761,11 +2778,53 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
27612778 | LifetimeBinderKind :: PolyTrait
27622779 | LifetimeBinderKind :: WhereBound
27632780 ) ;
2781+
2782+ let mut rm_inner_binders: FxIndexSet < Span > = Default :: default ( ) ;
27642783 let ( span, sugg) = if span. is_empty ( ) {
2784+ let mut binder_idents: FxIndexSet < Ident > = Default :: default ( ) ;
2785+ binder_idents. insert ( Ident :: from_str ( name. unwrap_or ( "'a" ) ) ) ;
2786+
2787+ // We need to special case binders in the following situation:
2788+ // Change `T: for<'a> Trait<T> + 'b` to `for<'a, 'b> T: Trait<T> + 'b`
2789+ // T: for<'a> Trait<T> + 'b
2790+ // ^^^^^^^ remove existing inner binder `for<'a>`
2791+ // for<'a, 'b> T: Trait<T> + 'b
2792+ // ^^^^^^^^^^^ suggest outer binder `for<'a, 'b>`
2793+ if let LifetimeBinderKind :: WhereBound = kind
2794+ && let Some ( ast:: WherePredicate :: BoundPredicate (
2795+ ast:: WhereBoundPredicate { bounded_ty, bounds, .. } ,
2796+ ) ) = self . diag_metadata . current_where_predicate
2797+ && bounded_ty. id == binder
2798+ {
2799+ for bound in bounds {
2800+ if let ast:: GenericBound :: Trait ( poly_trait_ref, _) = bound
2801+ && let span = poly_trait_ref
2802+ . span
2803+ . with_hi ( poly_trait_ref. trait_ref . path . span . lo ( ) )
2804+ && !span. is_empty ( )
2805+ {
2806+ rm_inner_binders. insert ( span) ;
2807+ poly_trait_ref. bound_generic_params . iter ( ) . for_each ( |v| {
2808+ binder_idents. insert ( v. ident ) ;
2809+ } ) ;
2810+ }
2811+ }
2812+ }
2813+
2814+ let binders_sugg = binder_idents. into_iter ( ) . enumerate ( ) . fold (
2815+ "" . to_string ( ) ,
2816+ |mut binders, ( i, x) | {
2817+ if i != 0 {
2818+ binders += ", " ;
2819+ }
2820+ binders += x. as_str ( ) ;
2821+ binders
2822+ } ,
2823+ ) ;
27652824 let sugg = format ! (
27662825 "{}<{}>{}" ,
27672826 if higher_ranked { "for" } else { "" } ,
2768- name . unwrap_or ( "'a" ) ,
2827+ binders_sugg ,
27692828 if higher_ranked { " " } else { "" } ,
27702829 ) ;
27712830 ( span, sugg)
@@ -2780,24 +2839,39 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
27802839 let sugg = format ! ( "{}, " , name. unwrap_or( "'a" ) ) ;
27812840 ( span, sugg)
27822841 } ;
2842+
27832843 if higher_ranked {
27842844 let message = Cow :: from ( format ! (
27852845 "consider making the {} lifetime-generic with a new `{}` lifetime" ,
27862846 kind. descr( ) ,
27872847 name. unwrap_or( "'a" ) ,
27882848 ) ) ;
2789- should_continue = suggest ( err, true , span, message, sugg) ;
2849+ should_continue = suggest (
2850+ err,
2851+ true ,
2852+ span,
2853+ message,
2854+ sugg,
2855+ if !rm_inner_binders. is_empty ( ) {
2856+ rm_inner_binders
2857+ . into_iter ( )
2858+ . map ( |v| ( v, "" . to_string ( ) ) )
2859+ . collect :: < Vec < _ > > ( )
2860+ } else {
2861+ vec ! [ ]
2862+ } ,
2863+ ) ;
27902864 err. note_once (
27912865 "for more information on higher-ranked polymorphism, visit \
27922866 https://doc.rust-lang.org/nomicon/hrtb.html",
27932867 ) ;
27942868 } else if let Some ( name) = name {
27952869 let message =
27962870 Cow :: from ( format ! ( "consider introducing lifetime `{name}` here" ) ) ;
2797- should_continue = suggest ( err, false , span, message, sugg) ;
2871+ should_continue = suggest ( err, false , span, message, sugg, vec ! [ ] ) ;
27982872 } else {
27992873 let message = Cow :: from ( "consider introducing a named lifetime parameter" ) ;
2800- should_continue = suggest ( err, false , span, message, sugg) ;
2874+ should_continue = suggest ( err, false , span, message, sugg, vec ! [ ] ) ;
28012875 }
28022876 }
28032877 LifetimeRibKind :: Item | LifetimeRibKind :: ConstParamTy => break ,
@@ -3033,11 +3107,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
30333107 self . suggest_introducing_lifetime (
30343108 err,
30353109 None ,
3036- |err, higher_ranked, span, message, intro_sugg| {
3110+ |err, higher_ranked, span, message, intro_sugg, _ | {
30373111 err. multipart_suggestion_verbose (
30383112 message,
30393113 std:: iter:: once ( ( span, intro_sugg) )
3040- . chain ( spans_suggs. iter ( ) . cloned ( ) )
3114+ . chain ( spans_suggs. clone ( ) )
30413115 . collect ( ) ,
30423116 Applicability :: MaybeIncorrect ,
30433117 ) ;
@@ -3161,11 +3235,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
31613235 self . suggest_introducing_lifetime (
31623236 err,
31633237 None ,
3164- |err, higher_ranked, span, message, intro_sugg| {
3238+ |err, higher_ranked, span, message, intro_sugg, _ | {
31653239 err. multipart_suggestion_verbose (
31663240 message,
31673241 std:: iter:: once ( ( span, intro_sugg) )
3168- . chain ( spans_suggs. iter ( ) . cloned ( ) )
3242+ . chain ( spans_suggs. clone ( ) )
31693243 . collect ( ) ,
31703244 Applicability :: MaybeIncorrect ,
31713245 ) ;
@@ -3309,7 +3383,6 @@ fn mk_where_bound_predicate(
33093383 poly_trait_ref : & ast:: PolyTraitRef ,
33103384 ty : & Ty ,
33113385) -> Option < ast:: WhereBoundPredicate > {
3312- use rustc_span:: DUMMY_SP ;
33133386 let modified_segments = {
33143387 let mut segments = path. segments . clone ( ) ;
33153388 let [ preceding @ .., second_last, last] = segments. as_mut_slice ( ) else {
0 commit comments