1+ use crate :: utils:: paths;
2+ use crate :: utils:: { get_trait_def_id, in_macro, span_lint, trait_ref_of_method} ;
13use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
2- use rustc_hir:: def:: { DefKind , Res } ;
34use rustc_hir:: intravisit:: {
4- walk_fn_decl, walk_generic_param, walk_generics, walk_param_bound, walk_ty, NestedVisitorMap , Visitor ,
5+ walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty,
6+ NestedVisitorMap , Visitor ,
57} ;
68use rustc_hir:: FnRetTy :: Return ;
79use rustc_hir:: {
8- BodyId , FnDecl , GenericArg , GenericBound , GenericParam , GenericParamKind , Generics , ImplItem , ImplItemKind , Item ,
9- ItemKind , Lifetime , LifetimeName , ParamName , QPath , TraitBoundModifier , TraitFn , TraitItem , TraitItemKind , Ty ,
10- TyKind , WhereClause , WherePredicate ,
10+ BareFnTy , BodyId , FnDecl , GenericArg , GenericBound , GenericParam , GenericParamKind , Generics , ImplItem ,
11+ ImplItemKind , Item , ItemKind , Lifetime , LifetimeName , ParamName , PolyTraitRef , TraitBoundModifier , TraitFn ,
12+ TraitItem , TraitItemKind , Ty , TyKind , WhereClause , WherePredicate ,
1113} ;
1214use rustc_lint:: { LateContext , LateLintPass } ;
1315use rustc_middle:: hir:: map:: Map ;
1416use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1517use rustc_span:: source_map:: Span ;
1618use rustc_span:: symbol:: { kw, Symbol } ;
17-
18- use crate :: utils:: { in_macro, last_path_segment, span_lint, trait_ref_of_method} ;
19+ use std:: iter:: FromIterator ;
1920
2021declare_clippy_lint ! {
2122 /// **What it does:** Checks for lifetime annotations which can be removed by
@@ -25,8 +26,11 @@ declare_clippy_lint! {
2526 /// complicated, while there is nothing out of the ordinary going on. Removing
2627 /// them leads to more readable code.
2728 ///
28- /// **Known problems:** Potential false negatives: we bail out if the function
29- /// has a `where` clause where lifetimes are mentioned.
29+ /// **Known problems:**
30+ /// - We bail out if the function has a `where` clause where lifetimes
31+ /// are mentioned due to potenial false positives.
32+ /// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the
33+ /// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`.
3034 ///
3135 /// **Example:**
3236 /// ```rust
@@ -108,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
108112}
109113
110114/// The lifetime of a &-reference.
111- #[ derive( PartialEq , Eq , Hash , Debug ) ]
115+ #[ derive( PartialEq , Eq , Hash , Debug , Clone ) ]
112116enum RefLt {
113117 Unnamed ,
114118 Static ,
@@ -127,7 +131,6 @@ fn check_fn_inner<'tcx>(
127131 return ;
128132 }
129133
130- let mut bounds_lts = Vec :: new ( ) ;
131134 let types = generics
132135 . params
133136 . iter ( )
@@ -156,13 +159,12 @@ fn check_fn_inner<'tcx>(
156159 if bound. name != LifetimeName :: Static && !bound. is_elided ( ) {
157160 return ;
158161 }
159- bounds_lts. push ( bound) ;
160162 }
161163 }
162164 }
163165 }
164166 }
165- if could_use_elision ( cx, decl, body, & generics. params , bounds_lts ) {
167+ if could_use_elision ( cx, decl, body, & generics. params ) {
166168 span_lint (
167169 cx,
168170 NEEDLESS_LIFETIMES ,
@@ -181,7 +183,6 @@ fn could_use_elision<'tcx>(
181183 func : & ' tcx FnDecl < ' _ > ,
182184 body : Option < BodyId > ,
183185 named_generics : & ' tcx [ GenericParam < ' _ > ] ,
184- bounds_lts : Vec < & ' tcx Lifetime > ,
185186) -> bool {
186187 // There are two scenarios where elision works:
187188 // * no output references, all input references have different LT
@@ -204,15 +205,31 @@ fn could_use_elision<'tcx>(
204205 if let Return ( ref ty) = func. output {
205206 output_visitor. visit_ty ( ty) ;
206207 }
208+ for lt in named_generics {
209+ input_visitor. visit_generic_param ( lt)
210+ }
211+
212+ if input_visitor. abort ( ) || output_visitor. abort ( ) {
213+ return false ;
214+ }
207215
208- let input_lts = match input_visitor. into_vec ( ) {
209- Some ( lts) => lts_from_bounds ( lts, bounds_lts. into_iter ( ) ) ,
210- None => return false ,
211- } ;
212- let output_lts = match output_visitor. into_vec ( ) {
213- Some ( val) => val,
214- None => return false ,
215- } ;
216+ if allowed_lts
217+ . intersection ( & FxHashSet :: from_iter (
218+ input_visitor
219+ . nested_elision_site_lts
220+ . iter ( )
221+ . chain ( output_visitor. nested_elision_site_lts . iter ( ) )
222+ . cloned ( )
223+ . filter ( |v| matches ! ( v, RefLt :: Named ( _) ) ) ,
224+ ) )
225+ . next ( )
226+ . is_some ( )
227+ {
228+ return false ;
229+ }
230+
231+ let input_lts = input_visitor. lts ;
232+ let output_lts = output_visitor. lts ;
216233
217234 if let Some ( body_id) = body {
218235 let mut checker = BodyLifetimeChecker {
@@ -277,35 +294,29 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
277294 allowed_lts
278295}
279296
280- fn lts_from_bounds < ' a , T : Iterator < Item = & ' a Lifetime > > ( mut vec : Vec < RefLt > , bounds_lts : T ) -> Vec < RefLt > {
281- for lt in bounds_lts {
282- if lt. name != LifetimeName :: Static {
283- vec. push ( RefLt :: Named ( lt. name . ident ( ) . name ) ) ;
284- }
285- }
286-
287- vec
288- }
289-
290297/// Number of unique lifetimes in the given vector.
291298#[ must_use]
292299fn unique_lifetimes ( lts : & [ RefLt ] ) -> usize {
293300 lts. iter ( ) . collect :: < FxHashSet < _ > > ( ) . len ( )
294301}
295302
303+ const CLOSURE_TRAIT_BOUNDS : [ & [ & str ] ; 3 ] = [ & paths:: FN , & paths:: FN_MUT , & paths:: FN_ONCE ] ;
304+
296305/// A visitor usable for `rustc_front::visit::walk_ty()`.
297306struct RefVisitor < ' a , ' tcx > {
298307 cx : & ' a LateContext < ' tcx > ,
299308 lts : Vec < RefLt > ,
300- abort : bool ,
309+ nested_elision_site_lts : Vec < RefLt > ,
310+ unelided_trait_object_lifetime : bool ,
301311}
302312
303313impl < ' a , ' tcx > RefVisitor < ' a , ' tcx > {
304314 fn new ( cx : & ' a LateContext < ' tcx > ) -> Self {
305315 Self {
306316 cx,
307317 lts : Vec :: new ( ) ,
308- abort : false ,
318+ nested_elision_site_lts : Vec :: new ( ) ,
319+ unelided_trait_object_lifetime : false ,
309320 }
310321 }
311322
@@ -325,40 +336,16 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
325336 }
326337 }
327338
328- fn into_vec ( self ) -> Option < Vec < RefLt > > {
329- if self . abort {
330- None
331- } else {
332- Some ( self . lts )
333- }
339+ fn all_lts ( & self ) -> Vec < RefLt > {
340+ self . lts
341+ . iter ( )
342+ . chain ( self . nested_elision_site_lts . iter ( ) )
343+ . cloned ( )
344+ . collect :: < Vec < _ > > ( )
334345 }
335346
336- fn collect_anonymous_lifetimes ( & mut self , qpath : & QPath < ' _ > , ty : & Ty < ' _ > ) {
337- if let Some ( ref last_path_segment) = last_path_segment ( qpath) . args {
338- if !last_path_segment. parenthesized
339- && !last_path_segment
340- . args
341- . iter ( )
342- . any ( |arg| matches ! ( arg, GenericArg :: Lifetime ( _) ) )
343- {
344- let hir_id = ty. hir_id ;
345- match self . cx . qpath_res ( qpath, hir_id) {
346- Res :: Def ( DefKind :: TyAlias | DefKind :: Struct , def_id) => {
347- let generics = self . cx . tcx . generics_of ( def_id) ;
348- for _ in generics. params . as_slice ( ) {
349- self . record ( & None ) ;
350- }
351- } ,
352- Res :: Def ( DefKind :: Trait , def_id) => {
353- let trait_def = self . cx . tcx . trait_def ( def_id) ;
354- for _ in & self . cx . tcx . generics_of ( trait_def. def_id ) . params {
355- self . record ( & None ) ;
356- }
357- } ,
358- _ => ( ) ,
359- }
360- }
361- }
347+ fn abort ( & self ) -> bool {
348+ self . unelided_trait_object_lifetime
362349 }
363350}
364351
@@ -370,30 +357,37 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
370357 self . record ( & Some ( * lifetime) ) ;
371358 }
372359
360+ fn visit_poly_trait_ref ( & mut self , poly_tref : & ' tcx PolyTraitRef < ' tcx > , tbm : TraitBoundModifier ) {
361+ let trait_ref = & poly_tref. trait_ref ;
362+ if CLOSURE_TRAIT_BOUNDS
363+ . iter ( )
364+ . any ( |trait_path| trait_ref. trait_def_id ( ) == get_trait_def_id ( self . cx , trait_path) )
365+ {
366+ let mut sub_visitor = RefVisitor :: new ( self . cx ) ;
367+ sub_visitor. visit_trait_ref ( trait_ref) ;
368+ self . nested_elision_site_lts . append ( & mut sub_visitor. all_lts ( ) ) ;
369+ } else {
370+ walk_poly_trait_ref ( self , poly_tref, tbm) ;
371+ }
372+ }
373+
373374 fn visit_ty ( & mut self , ty : & ' tcx Ty < ' _ > ) {
374375 match ty. kind {
375- TyKind :: Rptr ( ref lt, _) if lt. is_elided ( ) => {
376- self . record ( & None ) ;
377- } ,
378- TyKind :: Path ( ref path) => {
379- self . collect_anonymous_lifetimes ( path, ty) ;
380- } ,
381376 TyKind :: OpaqueDef ( item, _) => {
382377 let map = self . cx . tcx . hir ( ) ;
383- if let ItemKind :: OpaqueTy ( ref exist_ty) = map. expect_item ( item. id ) . kind {
384- for bound in exist_ty. bounds {
385- if let GenericBound :: Outlives ( _) = * bound {
386- self . record ( & None ) ;
387- }
388- }
389- } else {
390- unreachable ! ( )
391- }
378+ let item = map. expect_item ( item. id ) ;
379+ walk_item ( self , item) ;
392380 walk_ty ( self , ty) ;
393381 } ,
382+ TyKind :: BareFn ( & BareFnTy { decl, .. } ) => {
383+ let mut sub_visitor = RefVisitor :: new ( self . cx ) ;
384+ sub_visitor. visit_fn_decl ( decl) ;
385+ self . nested_elision_site_lts . append ( & mut sub_visitor. all_lts ( ) ) ;
386+ return ;
387+ } ,
394388 TyKind :: TraitObject ( bounds, ref lt) => {
395389 if !lt. is_elided ( ) {
396- self . abort = true ;
390+ self . unelided_trait_object_lifetime = true ;
397391 }
398392 for bound in bounds {
399393 self . visit_poly_trait_ref ( bound, TraitBoundModifier :: None ) ;
@@ -430,16 +424,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl
430424 walk_param_bound ( & mut visitor, bound) ;
431425 }
432426 // and check that all lifetimes are allowed
433- match visitor. into_vec ( ) {
434- None => return false ,
435- Some ( lts) => {
436- for lt in lts {
437- if !allowed_lts. contains ( & lt) {
438- return true ;
439- }
440- }
441- } ,
442- }
427+ return visitor. all_lts ( ) . iter ( ) . any ( |it| !allowed_lts. contains ( it) ) ;
443428 } ,
444429 WherePredicate :: EqPredicate ( ref pred) => {
445430 let mut visitor = RefVisitor :: new ( cx) ;
0 commit comments