@@ -856,36 +856,100 @@ fn elision_suggestions(
856
856
. filter ( |param| !param. is_elided_lifetime ( ) && !param. is_impl_trait ( ) )
857
857
. collect :: < Vec < _ > > ( ) ;
858
858
859
- 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 ( ) {
860
869
// if all the params are elided remove the whole generic block
861
870
//
862
871
// fn x<'a>() {}
863
872
// ^^^^
864
873
vec ! [ ( generics. span, String :: new( ) ) ]
865
874
} 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 )
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()`" )
876
881
} 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 < _ > > > ( ) ?
882
+ unreachable ! ( "handled by `elidable_lts.is_empty()`" )
883
+ }
884
+ } ,
885
+ [ first, second] => {
886
+ // NOTE: we could theoretically remove this case for the next one,
887
+ // but the latter likely has quite a lot of overhead, so don't
888
+ match (
889
+ elidable_lts. contains ( & first. def_id ) ,
890
+ elidable_lts. contains ( & second. def_id ) ,
891
+ ) {
892
+ ( false , false ) => unreachable ! ( "handled by `elidable_lts.is_empty()`" ) ,
893
+ ( true , true ) => unreachable ! ( "handled by `elidable_lts.len() == explicit_params.len()`" ) ,
894
+
895
+ // <'a, 'b> -> <'a, 'b>
896
+ // ^^ ^^^^
897
+ ( false , true ) => vec ! [ ( second. span. with_lo( first. span. hi( ) ) , String :: new( ) ) ] ,
898
+ // <'a, 'b> -> <'a, 'b>
899
+ // ^^ ^^^^
900
+ ( true , false ) => vec ! [ ( first. span. until( second. span) , String :: new( ) ) ] ,
901
+ }
902
+ } ,
903
+ // Time for the big guns
904
+ [ _, _, ..] => {
905
+ // Given a list like `<'a, 'b, 'c, 'd, ..>`,
906
+ //
907
+ // If there is a cluster of elidable lifetimes at the beginning, say `'a` and `'b`, we should
908
+ // suggest removing them _and_ the trailing comma. The span for that is `a.span.until(c.span)`:
909
+ // <'a, 'b, 'c, 'd, ..> => <'a, 'b, 'c, 'd, ..>
910
+ // ^^ ^^ ^^^^^^^^
911
+ //
912
+ // And since we know that `'c` isn't elidable--otherwise it would've been in the cluster--we can go
913
+ // over all the lifetimes after it, and for each elidable one, add a suggestion spanning the
914
+ // lifetime itself and the comma before, because each individual suggestion is guaranteed to leave
915
+ // the list valid:
916
+ // <.., 'c, 'd, 'e, 'f, 'g, ..> => <.., 'c, 'd, 'e, 'f, 'g, ..>
917
+ // ^^ ^^ ^^ ^^^^ ^^^^^^^^
918
+ //
919
+ // In case there is no such starting cluster, we only need to do the second part of the algorithm:
920
+ // <'a, 'b, 'c, 'd, 'e, 'f, 'g, ..> => <'a, 'b , 'c, 'd, 'e, 'f, 'g, ..>
921
+ // ^^ ^^ ^^ ^^ ^^^^^^^^^ ^^^^^^^^
922
+
923
+ // Split off the starting cluster
924
+ if let Some ( split_pos) = explicit_params
925
+ . iter ( )
926
+ . position ( |param| !elidable_lts. contains ( & param. def_id ) )
927
+ {
928
+ match explicit_params
929
+ . split_at_checked ( split_pos)
930
+ . expect ( "got `split_pos` from `position` on the same Vec" )
931
+ {
932
+ ( [ ..] , [ ] ) => unreachable ! ( "handled by `elidable_lts.len() == explicit_params.len()`" ) ,
933
+ ( [ ] , [ _] ) => unreachable ! ( "handled by `explicit_params.len() == 1`" ) ,
934
+ ( cluster, rest @ [ rest_first, ..] ) => {
935
+ // the span for the cluster
936
+ ( cluster. first ( ) . map ( |fw| fw. span . until ( rest_first. span ) ) . into_iter ( ) )
937
+ // the span for the remaining lifetimes (calculations independent of the cluster)
938
+ . chain (
939
+ rest. array_windows ( )
940
+ . filter ( |[ _, curr] | elidable_lts. contains ( & curr. def_id ) )
941
+ . map ( |[ prev, curr] | curr. span . with_lo ( prev. span . hi ( ) ) ) ,
942
+ )
943
+ . map ( |sp| ( sp, String :: new ( ) ) )
944
+ . collect ( )
945
+ } ,
946
+ }
947
+ } else {
948
+ // there were no lifetime param that couldn't be elided
949
+ unreachable ! ( "handled by `elidable_lts.len() == explicit_params.len()`" )
950
+ }
951
+ } ,
952
+ }
889
953
} ;
890
954
891
955
suggestions. extend ( usages. iter ( ) . map ( |& usage| {
0 commit comments