@@ -61,6 +61,40 @@ declare_clippy_lint! {
61
61
would allow omitting them"
62
62
}
63
63
64
+ declare_clippy_lint ! {
65
+ /// ### What it does
66
+ /// Checks for lifetime annotations which can be replaced with anonymous lifetimes (`'_`).
67
+ ///
68
+ /// ### Why is this bad?
69
+ /// The additional lifetimes can make the code look more complicated.
70
+ ///
71
+ /// ### Known problems
72
+ /// - We bail out if the function has a `where` clause where lifetimes
73
+ /// are mentioned due to potential false positives.
74
+ ///
75
+ /// ### Example
76
+ /// ```no_run
77
+ /// use std::str::Chars;
78
+ ///
79
+ /// fn f<'a>(x: &'a str) -> Chars<'a> {
80
+ /// x.chars()
81
+ /// }
82
+ /// ```
83
+ ///
84
+ /// Use instead:
85
+ /// ```no_run
86
+ /// use std::str::Chars;
87
+ ///
88
+ /// fn f(x: &str) -> Chars<'_> {
89
+ /// x.chars()
90
+ /// }
91
+ /// ```
92
+ #[ clippy:: version = "1.84.0" ]
93
+ pub ELIDABLE_LIFETIME_NAMES ,
94
+ pedantic,
95
+ "lifetime name that can be replaced with the anonymous lifetime"
96
+ }
97
+
64
98
declare_clippy_lint ! {
65
99
/// ### What it does
66
100
/// Checks for lifetimes in generics that are never used
@@ -91,7 +125,11 @@ declare_clippy_lint! {
91
125
"unused lifetimes in function definitions"
92
126
}
93
127
94
- declare_lint_pass ! ( Lifetimes => [ NEEDLESS_LIFETIMES , EXTRA_UNUSED_LIFETIMES ] ) ;
128
+ declare_lint_pass ! ( Lifetimes => [
129
+ NEEDLESS_LIFETIMES ,
130
+ ELIDABLE_LIFETIME_NAMES ,
131
+ EXTRA_UNUSED_LIFETIMES ,
132
+ ] ) ;
95
133
96
134
impl < ' tcx > LateLintPass < ' tcx > for Lifetimes {
97
135
fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' _ > ) {
@@ -721,6 +759,15 @@ fn report_elidable_impl_lifetimes<'tcx>(
721
759
report_elidable_lifetimes ( cx, impl_. generics , & elidable_lts, & usages, true ) ;
722
760
}
723
761
762
+ #[ derive( Copy , Clone ) ]
763
+ enum ElidableUsage {
764
+ /// Used in a ref (`&'a T`), can be removed
765
+ Ref ( Span ) ,
766
+ /// Used as a generic param (`T<'a>`) or an impl lifetime (`impl T + 'a`), can be replaced
767
+ /// with `'_`
768
+ Other ( Span ) ,
769
+ }
770
+
724
771
/// Generate diagnostic messages for elidable lifetimes.
725
772
fn report_elidable_lifetimes (
726
773
cx : & LateContext < ' _ > ,
@@ -738,9 +785,29 @@ fn report_elidable_lifetimes(
738
785
. collect :: < Vec < _ > > ( )
739
786
. join ( ", " ) ;
740
787
788
+ let elidable_usages: Vec < ElidableUsage > = usages
789
+ . iter ( )
790
+ . filter ( |usage| named_lifetime ( usage) . is_some_and ( |id| elidable_lts. contains ( & id) ) )
791
+ . map ( |usage| match cx. tcx . parent_hir_node ( usage. hir_id ) {
792
+ Node :: Ty ( Ty {
793
+ kind : TyKind :: Ref ( ..) , ..
794
+ } ) => ElidableUsage :: Ref ( usage. ident . span ) ,
795
+ _ => ElidableUsage :: Other ( usage. ident . span ) ,
796
+ } )
797
+ . collect ( ) ;
798
+
799
+ let lint = if elidable_usages
800
+ . iter ( )
801
+ . any ( |usage| matches ! ( usage, ElidableUsage :: Other ( _) ) )
802
+ {
803
+ ELIDABLE_LIFETIME_NAMES
804
+ } else {
805
+ NEEDLESS_LIFETIMES
806
+ } ;
807
+
741
808
span_lint_and_then (
742
809
cx,
743
- NEEDLESS_LIFETIMES ,
810
+ lint ,
744
811
elidable_lts
745
812
. iter ( )
746
813
. map ( |& lt| cx. tcx . def_span ( lt) )
@@ -760,7 +827,7 @@ fn report_elidable_lifetimes(
760
827
return ;
761
828
} ;
762
829
763
- if let Some ( suggestions) = elision_suggestions ( cx, generics, elidable_lts, usages ) {
830
+ if let Some ( suggestions) = elision_suggestions ( cx, generics, elidable_lts, & elidable_usages ) {
764
831
diag. multipart_suggestion ( "elide the lifetimes" , suggestions, Applicability :: MachineApplicable ) ;
765
832
}
766
833
} ,
@@ -771,7 +838,7 @@ fn elision_suggestions(
771
838
cx : & LateContext < ' _ > ,
772
839
generics : & Generics < ' _ > ,
773
840
elidable_lts : & [ LocalDefId ] ,
774
- usages : & [ Lifetime ] ,
841
+ usages : & [ ElidableUsage ] ,
775
842
) -> Option < Vec < ( Span , String ) > > {
776
843
let explicit_params = generics
777
844
. params
@@ -811,26 +878,21 @@ fn elision_suggestions(
811
878
. collect :: < Option < Vec < _ > > > ( ) ?
812
879
} ;
813
880
814
- suggestions. extend (
815
- usages
816
- . iter ( )
817
- . filter ( |usage| named_lifetime ( usage) . is_some_and ( |id| elidable_lts. contains ( & id) ) )
818
- . map ( |usage| {
819
- match cx. tcx . parent_hir_node ( usage. hir_id ) {
820
- Node :: Ty ( Ty {
821
- kind : TyKind :: Ref ( ..) , ..
822
- } ) => {
823
- // expand `&'a T` to `&'a T`
824
- // ^^ ^^^
825
- let span = cx. sess ( ) . source_map ( ) . span_extend_while_whitespace ( usage. ident . span ) ;
826
-
827
- ( span, String :: new ( ) )
828
- } ,
829
- // `T<'a>` and `impl Foo + 'a` should be replaced by `'_`
830
- _ => ( usage. ident . span , String :: from ( "'_" ) ) ,
831
- }
832
- } ) ,
833
- ) ;
881
+ suggestions. extend ( usages. iter ( ) . map ( |& usage| {
882
+ match usage {
883
+ ElidableUsage :: Ref ( span) => {
884
+ // expand `&'a T` to `&'a T`
885
+ // ^^ ^^^
886
+ let span = cx. sess ( ) . source_map ( ) . span_extend_while_whitespace ( span) ;
887
+
888
+ ( span, String :: new ( ) )
889
+ } ,
890
+ ElidableUsage :: Other ( span) => {
891
+ // `T<'a>` and `impl Foo + 'a` should be replaced by `'_`
892
+ ( span, String :: from ( "'_" ) )
893
+ } ,
894
+ }
895
+ } ) ) ;
834
896
835
897
Some ( suggestions)
836
898
}
0 commit comments