@@ -745,7 +745,7 @@ fn report_elidable_impl_lifetimes<'tcx>(
745745 impl_ : & ' tcx Impl < ' _ > ,
746746 map : & FxIndexMap < LocalDefId , Vec < Usage > > ,
747747) {
748- let single_usages = map
748+ let ( elidable_lts , usages ) : ( Vec < _ > , Vec < _ > ) = map
749749 . iter ( )
750750 . filter_map ( |( def_id, usages) | {
751751 if let [
@@ -762,14 +762,12 @@ fn report_elidable_impl_lifetimes<'tcx>(
762762 None
763763 }
764764 } )
765- . collect :: < Vec < _ > > ( ) ;
765+ . unzip ( ) ;
766766
767- if single_usages . is_empty ( ) {
767+ if elidable_lts . is_empty ( ) {
768768 return ;
769769 }
770770
771- let ( elidable_lts, usages) : ( Vec < _ > , Vec < _ > ) = single_usages. into_iter ( ) . unzip ( ) ;
772-
773771 report_elidable_lifetimes ( cx, impl_. generics , & elidable_lts, & usages, true ) ;
774772}
775773
@@ -795,9 +793,7 @@ fn report_elidable_lifetimes(
795793 // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a
796794 // `Node::GenericParam`.
797795 . filter_map ( |& def_id| cx. tcx . hir_node_by_def_id ( def_id) . ident ( ) )
798- . map ( |ident| ident. to_string ( ) )
799- . collect :: < Vec < _ > > ( )
800- . join ( ", " ) ;
796+ . format ( ", " ) ;
801797
802798 let elidable_usages: Vec < ElidableUsage > = usages
803799 . iter ( )
@@ -860,36 +856,89 @@ fn elision_suggestions(
860856 . filter ( |param| !param. is_elided_lifetime ( ) && !param. is_impl_trait ( ) )
861857 . collect :: < Vec < _ > > ( ) ;
862858
863- let mut suggestions = if elidable_lts. len ( ) == explicit_params. len ( ) {
859+ if !elidable_lts
860+ . iter ( )
861+ . all ( |lt| explicit_params. iter ( ) . any ( |param| param. def_id == * lt) )
862+ {
863+ return None ;
864+ }
865+
866+ let mut suggestions = if elidable_lts. is_empty ( ) {
867+ vec ! [ ]
868+ } else if elidable_lts. len ( ) == explicit_params. len ( ) {
864869 // if all the params are elided remove the whole generic block
865870 //
866871 // fn x<'a>() {}
867872 // ^^^^
868873 vec ! [ ( generics. span, String :: new( ) ) ]
869874 } else {
870- elidable_lts
871- . iter ( )
872- . map ( |& id| {
873- let pos = explicit_params. iter ( ) . position ( |param| param. def_id == id) ?;
874- let param = explicit_params. get ( pos) ?;
875-
876- let span = if let Some ( next) = explicit_params. get ( pos + 1 ) {
877- // fn x<'prev, 'a, 'next>() {}
878- // ^^^^
879- param. span . until ( next. span )
875+ match & explicit_params[ ..] {
876+ // no params, nothing to elide
877+ [ ] => unreachable ! ( "handled by `elidable_lts.is_empty()`" ) ,
878+ [ param] => {
879+ if elidable_lts. contains ( & param. def_id ) {
880+ unreachable ! ( "handled by `elidable_lts.len() == explicit_params.len()`" )
880881 } else {
881- // `pos` should be at least 1 here, because the param in position 0 would either have a `next`
882- // param or would have taken the `elidable_lts.len() == explicit_params.len()` branch.
883- let prev = explicit_params. get ( pos - 1 ) ?;
884-
885- // fn x<'prev, 'a>() {}
886- // ^^^^
887- param. span . with_lo ( prev. span . hi ( ) )
882+ unreachable ! ( "handled by `elidable_lts.is_empty()`" )
883+ }
884+ } ,
885+ [ _, _, ..] => {
886+ // Given a list like `<'a, 'b, 'c, 'd, ..>`,
887+ //
888+ // If there is a cluster of elidable lifetimes at the beginning, say `'a` and `'b`, we should
889+ // suggest removing them _and_ the trailing comma. The span for that is `a.span.until(c.span)`:
890+ // <'a, 'b, 'c, 'd, ..> => <'a, 'b, 'c, 'd, ..>
891+ // ^^ ^^ ^^^^^^^^
892+ //
893+ // And since we know that `'c` isn't elidable--otherwise it would've been in the cluster--we can go
894+ // over all the lifetimes after it, and for each elidable one, add a suggestion spanning the
895+ // lifetime itself and the comma before, because each individual suggestion is guaranteed to leave
896+ // the list valid:
897+ // <.., 'c, 'd, 'e, 'f, 'g, ..> => <.., 'c, 'd, 'e, 'f, 'g, ..>
898+ // ^^ ^^ ^^ ^^^^ ^^^^^^^^
899+ //
900+ // In case there is no such starting cluster, we only need to do the second part of the algorithm:
901+ // <'a, 'b, 'c, 'd, 'e, 'f, 'g, ..> => <'a, 'b , 'c, 'd, 'e, 'f, 'g, ..>
902+ // ^^ ^^ ^^ ^^ ^^^^^^^^^ ^^^^^^^^
903+
904+ // Split off the starting cluster
905+ // TODO: use `slice::split_once` once stabilized (github.com/rust-lang/rust/issues/112811):
906+ // ```
907+ // let Some(split) = explicit_params.split_once(|param| !elidable_lts.contains(¶m.def_id)) else {
908+ // // there were no lifetime param that couldn't be elided
909+ // unreachable!("handled by `elidable_lts.len() == explicit_params.len()`")
910+ // };
911+ // match split { /* .. */ }
912+ // ```
913+ let Some ( split_pos) = explicit_params
914+ . iter ( )
915+ . position ( |param| !elidable_lts. contains ( & param. def_id ) )
916+ else {
917+ // there were no lifetime param that couldn't be elided
918+ unreachable ! ( "handled by `elidable_lts.len() == explicit_params.len()`" )
888919 } ;
889-
890- Some ( ( span, String :: new ( ) ) )
891- } )
892- . collect :: < Option < Vec < _ > > > ( ) ?
920+ let split = explicit_params
921+ . split_at_checked ( split_pos)
922+ . expect ( "got `split_pos` from `position` on the same Vec" ) ;
923+
924+ match split {
925+ ( [ ..] , [ ] ) => unreachable ! ( "handled by `elidable_lts.len() == explicit_params.len()`" ) ,
926+ ( [ ] , [ _] ) => unreachable ! ( "handled by `explicit_params.len() == 1`" ) ,
927+ ( cluster, rest @ [ rest_first, ..] ) => {
928+ // the span for the cluster
929+ ( cluster. first ( ) . map ( |fw| fw. span . until ( rest_first. span ) ) . into_iter ( ) )
930+ // the span for the remaining lifetimes (calculations independent of the cluster)
931+ . chain (
932+ rest. array_windows ( )
933+ . filter ( |[ _, curr] | elidable_lts. contains ( & curr. def_id ) )
934+ . map ( |[ prev, curr] | curr. span . with_lo ( prev. span . hi ( ) ) ) ,
935+ )
936+ . map ( |sp| ( sp, String :: new ( ) ) )
937+ . collect ( )
938+ } ,
939+ }
940+ } ,
941+ }
893942 } ;
894943
895944 suggestions. extend ( usages. iter ( ) . map ( |& usage| {
0 commit comments