@@ -856,36 +856,93 @@ fn elision_suggestions(
856856 . filter ( |param| !param. is_elided_lifetime ( ) && !param. is_impl_trait ( ) )
857857 . collect :: < Vec < _ > > ( ) ;
858858
859- let mut suggestions = if elidable_lts. len ( ) == explicit_params. len ( ) {
859+ let mut suggestions = if elidable_lts. is_empty ( ) {
860+ vec ! [ ]
861+ } else if elidable_lts. len ( ) == explicit_params. len ( ) {
860862 // if all the params are elided remove the whole generic block
861863 //
862864 // fn x<'a>() {}
863865 // ^^^^
864866 vec ! [ ( generics. span, String :: new( ) ) ]
865867 } else {
866- elidable_lts
867- . iter ( )
868- . map ( |& id| {
869- let pos = explicit_params. iter ( ) . position ( |param| param. def_id == id) ?;
870- let param = explicit_params. get ( pos) ?;
871-
872- let span = if let Some ( next) = explicit_params. get ( pos + 1 ) {
873- // fn x<'prev, 'a, 'next>() {}
874- // ^^^^
875- param. span . until ( next. span )
868+ match & explicit_params[ ..] {
869+ // no params, nothing to elide
870+ [ ] => unreachable ! ( "handled by `elidable_lts.is_empty()`" ) ,
871+ [ param] => {
872+ if elidable_lts. contains ( & param. def_id ) {
873+ unreachable ! ( "handled by `elidable_lts.len() == explicit_params.len()`" )
876874 } else {
877- // `pos` should be at least 1 here, because the param in position 0 would either have a `next`
878- // param or would have taken the `elidable_lts.len() == explicit_params.len()` branch.
879- let prev = explicit_params. get ( pos - 1 ) ?;
880-
881- // fn x<'prev, 'a>() {}
882- // ^^^^
883- param. span . with_lo ( prev. span . hi ( ) )
884- } ;
885-
886- Some ( ( span, String :: new ( ) ) )
887- } )
888- . collect :: < Option < Vec < _ > > > ( ) ?
875+ unreachable ! ( "handled by `elidable_lts.is_empty()`" )
876+ }
877+ } ,
878+ [ first, second] => {
879+ // NOTE: we could theoretically remove this case for the next one,
880+ // but the latter likely has quite a lot of overhead, so don't
881+ match (
882+ elidable_lts. contains ( & first. def_id ) ,
883+ elidable_lts. contains ( & second. def_id ) ,
884+ ) {
885+ ( false , false ) => unreachable ! ( "handled by `elidable_lts.is_empty()`" ) ,
886+ ( true , true ) => unreachable ! ( "handled by `elidable_lts.len() == explicit_params.len()`" ) ,
887+
888+ // <'a, 'b> -> <'a, 'b>
889+ // ^^ ^^^^
890+ ( false , true ) => vec ! [ ( second. span. with_lo( first. span. hi( ) ) , String :: new( ) ) ] ,
891+ // <'a, 'b> -> <'a, 'b>
892+ // ^^ ^^^^
893+ ( true , false ) => vec ! [ ( first. span. until( second. span) , String :: new( ) ) ] ,
894+ }
895+ } ,
896+ // Time for the big guns
897+ [ _, _, ..] => {
898+ // Given a list like `<'a, 'b, 'c, 'd, ..>`,
899+ //
900+ // If there is a cluster of elidable lifetimes at the beginning, say `'a` and `'b`, we should
901+ // suggest removing them _and_ the trailing comma. The span for that is `a.span.until(c.span)`:
902+ // <'a, 'b, 'c, 'd, ..> => <'a, 'b, 'c, 'd, ..>
903+ // ^^ ^^ ^^^^^^^^
904+ //
905+ // And since we know that `'c` isn't elidable--otherwise it would've been in the cluster--we can go
906+ // over all the lifetimes after it, and for each elidable one, add a suggestion spanning the
907+ // lifetime itself and the comma before, because each individual suggestion is guaranteed to leave
908+ // the list valid:
909+ // <.., 'c, 'd, 'e, 'f, 'g, ..> => <.., 'c, 'd, 'e, 'f, 'g, ..>
910+ // ^^ ^^ ^^ ^^^^ ^^^^^^^^
911+ //
912+ // In case there is no such starting cluster, we only need to do the second part of the algorithm:
913+ // <'a, 'b, 'c, 'd, 'e, 'f, 'g, ..> => <'a, 'b , 'c, 'd, 'e, 'f, 'g, ..>
914+ // ^^ ^^ ^^ ^^ ^^^^^^^^^ ^^^^^^^^
915+
916+ // Split off the starting cluster
917+ if let Some ( split_pos) = explicit_params
918+ . iter ( )
919+ . position ( |param| !elidable_lts. contains ( & param. def_id ) )
920+ {
921+ match explicit_params
922+ . split_at_checked ( split_pos)
923+ . expect ( "got `split_pos` from `position` on the same Vec" )
924+ {
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 ( rest. array_windows ( ) . filter_map ( |[ prev, curr] | {
932+ elidable_lts
933+ . contains ( & curr. def_id )
934+ . then ( || curr. span . with_lo ( prev. span . hi ( ) ) )
935+ } ) )
936+ . map ( |sp| ( sp, String :: new ( ) ) )
937+ . collect ( )
938+ } ,
939+ }
940+ } else {
941+ // there were no lifetime param that couldn't be elided
942+ unreachable ! ( "handled by `elidable_lts.len() == explicit_params.len()`" )
943+ }
944+ } ,
945+ }
889946 } ;
890947
891948 suggestions. extend ( usages. iter ( ) . map ( |& usage| {
0 commit comments