@@ -38,8 +38,8 @@ declare_clippy_lint! {
3838 /// them leads to more readable code.
3939 ///
4040 /// ### 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.
4343 ///
4444 /// ### Example
4545 /// ```no_run
@@ -62,6 +62,38 @@ declare_clippy_lint! {
6262 would allow omitting them"
6363}
6464
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+
6597declare_clippy_lint ! {
6698 /// ### What it does
6799 /// Checks for lifetimes in generics that are never used
@@ -104,7 +136,11 @@ impl Lifetimes {
104136 }
105137}
106138
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+ ] ) ;
108144
109145impl < ' tcx > LateLintPass < ' tcx > for Lifetimes {
110146 fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' _ > ) {
@@ -746,6 +782,15 @@ fn report_elidable_impl_lifetimes<'tcx>(
746782 report_elidable_lifetimes ( cx, impl_. generics , & elidable_lts, & usages, true ) ;
747783}
748784
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+
749794/// Generate diagnostic messages for elidable lifetimes.
750795fn report_elidable_lifetimes (
751796 cx : & LateContext < ' _ > ,
@@ -763,9 +808,29 @@ fn report_elidable_lifetimes(
763808 . collect :: < Vec < _ > > ( )
764809 . join ( ", " ) ;
765810
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+
766831 span_lint_and_then (
767832 cx,
768- NEEDLESS_LIFETIMES ,
833+ lint ,
769834 elidable_lts
770835 . iter ( )
771836 . map ( |& lt| cx. tcx . def_span ( lt) )
@@ -785,7 +850,7 @@ fn report_elidable_lifetimes(
785850 return ;
786851 }
787852
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 ) {
789854 diag. multipart_suggestion ( "elide the lifetimes" , suggestions, Applicability :: MachineApplicable ) ;
790855 }
791856 } ,
@@ -796,7 +861,7 @@ fn elision_suggestions(
796861 cx : & LateContext < ' _ > ,
797862 generics : & Generics < ' _ > ,
798863 elidable_lts : & [ LocalDefId ] ,
799- usages : & [ Lifetime ] ,
864+ usages : & [ ElidableUsage ] ,
800865) -> Option < Vec < ( Span , String ) > > {
801866 let explicit_params = generics
802867 . params
@@ -836,26 +901,21 @@ fn elision_suggestions(
836901 . collect :: < Option < Vec < _ > > > ( ) ?
837902 } ;
838903
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+ } ) ) ;
859919
860920 Some ( suggestions)
861921}
0 commit comments