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