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