@@ -63,6 +63,40 @@ declare_clippy_lint! {
63
63
would allow omitting them"
64
64
}
65
65
66
+ declare_clippy_lint ! {
67
+ /// ### What it does
68
+ /// Checks for lifetime annotations which can be replaced with anonymous lifetimes (`'_`).
69
+ ///
70
+ /// ### Why is this bad?
71
+ /// The additional lifetimes can make the code look more complicated.
72
+ ///
73
+ /// ### Known problems
74
+ /// - We bail out if the function has a `where` clause where lifetimes
75
+ /// are mentioned due to potential false positives.
76
+ ///
77
+ /// ### Example
78
+ /// ```no_run
79
+ /// use std::str::Chars;
80
+ ///
81
+ /// fn f<'a>(x: &'a str) -> Chars<'a> {
82
+ /// x.chars()
83
+ /// }
84
+ /// ```
85
+ ///
86
+ /// Use instead:
87
+ /// ```no_run
88
+ /// use std::str::Chars;
89
+ ///
90
+ /// fn f(x: &str) -> Chars<'_> {
91
+ /// x.chars()
92
+ /// }
93
+ /// ```
94
+ #[ clippy:: version = "1.84.0" ]
95
+ pub ELIDABLE_LIFETIME_NAMES ,
96
+ pedantic,
97
+ "lifetime name that can be replaced with the anonymous lifetime"
98
+ }
99
+
66
100
declare_clippy_lint ! {
67
101
/// ### What it does
68
102
/// Checks for lifetimes in generics that are never used
@@ -105,7 +139,11 @@ impl Lifetimes {
105
139
}
106
140
}
107
141
108
- impl_lint_pass ! ( Lifetimes => [ NEEDLESS_LIFETIMES , EXTRA_UNUSED_LIFETIMES ] ) ;
142
+ impl_lint_pass ! ( Lifetimes => [
143
+ NEEDLESS_LIFETIMES ,
144
+ ELIDABLE_LIFETIME_NAMES ,
145
+ EXTRA_UNUSED_LIFETIMES ,
146
+ ] ) ;
109
147
110
148
impl < ' tcx > LateLintPass < ' tcx > for Lifetimes {
111
149
fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' _ > ) {
@@ -747,6 +785,15 @@ fn report_elidable_impl_lifetimes<'tcx>(
747
785
report_elidable_lifetimes ( cx, impl_. generics , & elidable_lts, & usages, true ) ;
748
786
}
749
787
788
+ #[ derive( Copy , Clone ) ]
789
+ enum ElidableUsage {
790
+ /// Used in a ref (`&'a T`), can be removed
791
+ Ref ( Span ) ,
792
+ /// Used as a generic param (`T<'a>`) or an impl lifetime (`impl T + 'a`), can be replaced
793
+ /// with `'_`
794
+ Other ( Span ) ,
795
+ }
796
+
750
797
/// Generate diagnostic messages for elidable lifetimes.
751
798
fn report_elidable_lifetimes (
752
799
cx : & LateContext < ' _ > ,
@@ -764,9 +811,29 @@ fn report_elidable_lifetimes(
764
811
. collect :: < Vec < _ > > ( )
765
812
. join ( ", " ) ;
766
813
814
+ let elidable_usages: Vec < ElidableUsage > = usages
815
+ . iter ( )
816
+ . filter ( |usage| named_lifetime ( usage) . is_some_and ( |id| elidable_lts. contains ( & id) ) )
817
+ . map ( |usage| match cx. tcx . parent_hir_node ( usage. hir_id ) {
818
+ Node :: Ty ( Ty {
819
+ kind : TyKind :: Ref ( ..) , ..
820
+ } ) => ElidableUsage :: Ref ( usage. ident . span ) ,
821
+ _ => ElidableUsage :: Other ( usage. ident . span ) ,
822
+ } )
823
+ . collect ( ) ;
824
+
825
+ let lint = if elidable_usages
826
+ . iter ( )
827
+ . any ( |usage| matches ! ( usage, ElidableUsage :: Other ( _) ) )
828
+ {
829
+ ELIDABLE_LIFETIME_NAMES
830
+ } else {
831
+ NEEDLESS_LIFETIMES
832
+ } ;
833
+
767
834
span_lint_and_then (
768
835
cx,
769
- NEEDLESS_LIFETIMES ,
836
+ lint ,
770
837
elidable_lts
771
838
. iter ( )
772
839
. map ( |& lt| cx. tcx . def_span ( lt) )
@@ -786,7 +853,7 @@ fn report_elidable_lifetimes(
786
853
return ;
787
854
} ;
788
855
789
- if let Some ( suggestions) = elision_suggestions ( cx, generics, elidable_lts, usages ) {
856
+ if let Some ( suggestions) = elision_suggestions ( cx, generics, elidable_lts, & elidable_usages ) {
790
857
diag. multipart_suggestion ( "elide the lifetimes" , suggestions, Applicability :: MachineApplicable ) ;
791
858
}
792
859
} ,
@@ -797,7 +864,7 @@ fn elision_suggestions(
797
864
cx : & LateContext < ' _ > ,
798
865
generics : & Generics < ' _ > ,
799
866
elidable_lts : & [ LocalDefId ] ,
800
- usages : & [ Lifetime ] ,
867
+ usages : & [ ElidableUsage ] ,
801
868
) -> Option < Vec < ( Span , String ) > > {
802
869
let explicit_params = generics
803
870
. params
@@ -837,26 +904,21 @@ fn elision_suggestions(
837
904
. collect :: < Option < Vec < _ > > > ( ) ?
838
905
} ;
839
906
840
- suggestions. extend (
841
- usages
842
- . iter ( )
843
- . filter ( |usage| named_lifetime ( usage) . is_some_and ( |id| elidable_lts. contains ( & id) ) )
844
- . map ( |usage| {
845
- match cx. tcx . parent_hir_node ( usage. hir_id ) {
846
- Node :: Ty ( Ty {
847
- kind : TyKind :: Ref ( ..) , ..
848
- } ) => {
849
- // expand `&'a T` to `&'a T`
850
- // ^^ ^^^
851
- let span = cx. sess ( ) . source_map ( ) . span_extend_while_whitespace ( usage. ident . span ) ;
852
-
853
- ( span, String :: new ( ) )
854
- } ,
855
- // `T<'a>` and `impl Foo + 'a` should be replaced by `'_`
856
- _ => ( usage. ident . span , String :: from ( "'_" ) ) ,
857
- }
858
- } ) ,
859
- ) ;
907
+ suggestions. extend ( usages. iter ( ) . map ( |& usage| {
908
+ match usage {
909
+ ElidableUsage :: Ref ( span) => {
910
+ // expand `&'a T` to `&'a T`
911
+ // ^^ ^^^
912
+ let span = cx. sess ( ) . source_map ( ) . span_extend_while_whitespace ( span) ;
913
+
914
+ ( span, String :: new ( ) )
915
+ } ,
916
+ ElidableUsage :: Other ( span) => {
917
+ // `T<'a>` and `impl Foo + 'a` should be replaced by `'_`
918
+ ( span, String :: from ( "'_" ) )
919
+ } ,
920
+ }
921
+ } ) ) ;
860
922
861
923
Some ( suggestions)
862
924
}
0 commit comments