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_trait_ref , walk_ty , NestedVisitorMap ,
5- Visitor ,
5+ walk_fn_decl, walk_generic_param, walk_generics, walk_item , walk_param_bound , walk_poly_trait_ref , walk_ty ,
6+ NestedVisitorMap , Visitor ,
67} ;
78use rustc_hir:: FnRetTy :: Return ;
89use rustc_hir:: {
9- BodyId , FnDecl , GenericArg , GenericBound , GenericParam , GenericParamKind , Generics , ImplItem , ImplItemKind , Item ,
10- ItemKind , Lifetime , LifetimeName , ParamName , QPath , TraitBoundModifier , TraitFn , TraitItem , TraitItemKind ,
11- TraitRef , Ty , 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 ,
1213} ;
1314use rustc_lint:: { LateContext , LateLintPass } ;
1415use rustc_middle:: hir:: map:: Map ;
1516use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1617use rustc_span:: source_map:: Span ;
1718use rustc_span:: symbol:: { kw, Symbol } ;
18-
19- use crate :: utils:: paths;
20- use crate :: utils:: { get_trait_def_id, in_macro, last_path_segment, span_lint, trait_ref_of_method} ;
19+ use std:: iter:: FromIterator ;
2120
2221declare_clippy_lint ! {
2322 /// **What it does:** Checks for lifetime annotations which can be removed by
@@ -110,7 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
110109}
111110
112111/// The lifetime of a &-reference.
113- #[ derive( PartialEq , Eq , Hash , Debug ) ]
112+ #[ derive( PartialEq , Eq , Hash , Debug , Clone ) ]
114113enum RefLt {
115114 Unnamed ,
116115 Static ,
@@ -129,15 +128,6 @@ fn check_fn_inner<'tcx>(
129128 return ;
130129 }
131130
132- // fn pointers and closure trait bounds are also lifetime elision sites. This lint does not
133- // support nested elision sites in a fn item.
134- if FnPointerOrClosureTraitBoundFinder :: find_in_generics ( cx, generics)
135- || FnPointerOrClosureTraitBoundFinder :: find_in_fn_decl ( cx, decl)
136- {
137- return ;
138- }
139-
140- let mut bounds_lts = Vec :: new ( ) ;
141131 let types = generics
142132 . params
143133 . iter ( )
@@ -166,13 +156,12 @@ fn check_fn_inner<'tcx>(
166156 if bound. name != LifetimeName :: Static && !bound. is_elided ( ) {
167157 return ;
168158 }
169- bounds_lts. push ( bound) ;
170159 }
171160 }
172161 }
173162 }
174163 }
175- if could_use_elision ( cx, decl, body, & generics. params , bounds_lts ) {
164+ if could_use_elision ( cx, decl, body, & generics. params ) {
176165 span_lint (
177166 cx,
178167 NEEDLESS_LIFETIMES ,
@@ -191,7 +180,6 @@ fn could_use_elision<'tcx>(
191180 func : & ' tcx FnDecl < ' _ > ,
192181 body : Option < BodyId > ,
193182 named_generics : & ' tcx [ GenericParam < ' _ > ] ,
194- bounds_lts : Vec < & ' tcx Lifetime > ,
195183) -> bool {
196184 // There are two scenarios where elision works:
197185 // * no output references, all input references have different LT
@@ -214,15 +202,31 @@ fn could_use_elision<'tcx>(
214202 if let Return ( ref ty) = func. output {
215203 output_visitor. visit_ty ( ty) ;
216204 }
205+ for lt in named_generics {
206+ input_visitor. visit_generic_param ( lt)
207+ }
217208
218- let input_lts = match input_visitor. into_vec ( ) {
219- Some ( lts) => lts_from_bounds ( lts, bounds_lts. into_iter ( ) ) ,
220- None => return false ,
221- } ;
222- let output_lts = match output_visitor. into_vec ( ) {
223- Some ( val) => val,
224- None => return false ,
225- } ;
209+ if input_visitor. abort ( ) || output_visitor. abort ( ) {
210+ return false ;
211+ }
212+
213+ if allowed_lts
214+ . intersection ( & FxHashSet :: from_iter (
215+ input_visitor
216+ . nested_elision_site_lts
217+ . iter ( )
218+ . chain ( output_visitor. nested_elision_site_lts . iter ( ) )
219+ . cloned ( )
220+ . filter ( |v| matches ! ( v, RefLt :: Named ( _) ) ) ,
221+ ) )
222+ . next ( )
223+ . is_some ( )
224+ {
225+ return false ;
226+ }
227+
228+ let input_lts = input_visitor. lts ;
229+ let output_lts = output_visitor. lts ;
226230
227231 if let Some ( body_id) = body {
228232 let mut checker = BodyLifetimeChecker {
@@ -287,35 +291,29 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
287291 allowed_lts
288292}
289293
290- fn lts_from_bounds < ' a , T : Iterator < Item = & ' a Lifetime > > ( mut vec : Vec < RefLt > , bounds_lts : T ) -> Vec < RefLt > {
291- for lt in bounds_lts {
292- if lt. name != LifetimeName :: Static {
293- vec. push ( RefLt :: Named ( lt. name . ident ( ) . name ) ) ;
294- }
295- }
296-
297- vec
298- }
299-
300294/// Number of unique lifetimes in the given vector.
301295#[ must_use]
302296fn unique_lifetimes ( lts : & [ RefLt ] ) -> usize {
303297 lts. iter ( ) . collect :: < FxHashSet < _ > > ( ) . len ( )
304298}
305299
300+ const CLOSURE_TRAIT_BOUNDS : [ & [ & str ] ; 3 ] = [ & paths:: FN , & paths:: FN_MUT , & paths:: FN_ONCE ] ;
301+
306302/// A visitor usable for `rustc_front::visit::walk_ty()`.
307303struct RefVisitor < ' a , ' tcx > {
308304 cx : & ' a LateContext < ' tcx > ,
309305 lts : Vec < RefLt > ,
310- abort : bool ,
306+ nested_elision_site_lts : Vec < RefLt > ,
307+ unelided_trait_object_lifetime : bool ,
311308}
312309
313310impl < ' a , ' tcx > RefVisitor < ' a , ' tcx > {
314311 fn new ( cx : & ' a LateContext < ' tcx > ) -> Self {
315312 Self {
316313 cx,
317314 lts : Vec :: new ( ) ,
318- abort : false ,
315+ nested_elision_site_lts : Vec :: new ( ) ,
316+ unelided_trait_object_lifetime : false ,
319317 }
320318 }
321319
@@ -335,40 +333,16 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
335333 }
336334 }
337335
338- fn into_vec ( self ) -> Option < Vec < RefLt > > {
339- if self . abort {
340- None
341- } else {
342- Some ( self . lts )
343- }
336+ fn all_lts ( & self ) -> Vec < RefLt > {
337+ self . lts
338+ . iter ( )
339+ . chain ( self . nested_elision_site_lts . iter ( ) )
340+ . cloned ( )
341+ . collect :: < Vec < _ > > ( )
344342 }
345343
346- fn collect_anonymous_lifetimes ( & mut self , qpath : & QPath < ' _ > , ty : & Ty < ' _ > ) {
347- if let Some ( ref last_path_segment) = last_path_segment ( qpath) . args {
348- if !last_path_segment. parenthesized
349- && !last_path_segment
350- . args
351- . iter ( )
352- . any ( |arg| matches ! ( arg, GenericArg :: Lifetime ( _) ) )
353- {
354- let hir_id = ty. hir_id ;
355- match self . cx . qpath_res ( qpath, hir_id) {
356- Res :: Def ( DefKind :: TyAlias | DefKind :: Struct , def_id) => {
357- let generics = self . cx . tcx . generics_of ( def_id) ;
358- for _ in generics. params . as_slice ( ) {
359- self . record ( & None ) ;
360- }
361- } ,
362- Res :: Def ( DefKind :: Trait , def_id) => {
363- let trait_def = self . cx . tcx . trait_def ( def_id) ;
364- for _ in & self . cx . tcx . generics_of ( trait_def. def_id ) . params {
365- self . record ( & None ) ;
366- }
367- } ,
368- _ => ( ) ,
369- }
370- }
371- }
344+ fn abort ( & self ) -> bool {
345+ self . unelided_trait_object_lifetime
372346 }
373347}
374348
@@ -380,30 +354,36 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
380354 self . record ( & Some ( * lifetime) ) ;
381355 }
382356
357+ fn visit_poly_trait_ref ( & mut self , poly_tref : & ' tcx PolyTraitRef < ' tcx > , tbm : TraitBoundModifier ) {
358+ let trait_ref = & poly_tref. trait_ref ;
359+ if CLOSURE_TRAIT_BOUNDS
360+ . iter ( )
361+ . any ( |trait_path| trait_ref. trait_def_id ( ) == get_trait_def_id ( self . cx , trait_path) )
362+ {
363+ let mut sub_visitor = RefVisitor :: new ( self . cx ) ;
364+ sub_visitor. visit_trait_ref ( trait_ref) ;
365+ self . nested_elision_site_lts . append ( & mut sub_visitor. all_lts ( ) ) ;
366+ } else {
367+ walk_poly_trait_ref ( self , poly_tref, tbm) ;
368+ }
369+ }
370+
383371 fn visit_ty ( & mut self , ty : & ' tcx Ty < ' _ > ) {
384372 match ty. kind {
385- TyKind :: Rptr ( ref lt, _) if lt. is_elided ( ) => {
386- self . record ( & None ) ;
387- } ,
388- TyKind :: Path ( ref path) => {
389- self . collect_anonymous_lifetimes ( path, ty) ;
390- } ,
391373 TyKind :: OpaqueDef ( item, _) => {
392374 let map = self . cx . tcx . hir ( ) ;
393- if let ItemKind :: OpaqueTy ( ref exist_ty) = map. expect_item ( item. id ) . kind {
394- for bound in exist_ty. bounds {
395- if let GenericBound :: Outlives ( _) = * bound {
396- self . record ( & None ) ;
397- }
398- }
399- } else {
400- unreachable ! ( )
401- }
375+ let item = map. expect_item ( item. id ) ;
376+ walk_item ( self , item) ;
402377 walk_ty ( self , ty) ;
403378 } ,
379+ TyKind :: BareFn ( & BareFnTy { decl, .. } ) => {
380+ let mut sub_visitor = RefVisitor :: new ( self . cx ) ;
381+ sub_visitor. visit_fn_decl ( decl) ;
382+ self . nested_elision_site_lts . append ( & mut sub_visitor. all_lts ( ) ) ;
383+ } ,
404384 TyKind :: TraitObject ( bounds, ref lt) => {
405385 if !lt. is_elided ( ) {
406- self . abort = true ;
386+ self . unelided_trait_object_lifetime = true ;
407387 }
408388 for bound in bounds {
409389 self . visit_poly_trait_ref ( bound, TraitBoundModifier :: None ) ;
@@ -440,16 +420,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl
440420 walk_param_bound ( & mut visitor, bound) ;
441421 }
442422 // and check that all lifetimes are allowed
443- match visitor. into_vec ( ) {
444- None => return false ,
445- Some ( lts) => {
446- for lt in lts {
447- if !allowed_lts. contains ( & lt) {
448- return true ;
449- }
450- }
451- } ,
452- }
423+ return visitor. all_lts ( ) . iter ( ) . any ( |it| !allowed_lts. contains ( it) ) ;
453424 } ,
454425 WherePredicate :: EqPredicate ( ref pred) => {
455426 let mut visitor = RefVisitor :: new ( cx) ;
@@ -533,54 +504,3 @@ impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker {
533504 NestedVisitorMap :: None
534505 }
535506}
536-
537- const CLOSURE_TRAIT_BOUNDS : [ & [ & str ] ; 3 ] = [ & paths:: FN , & paths:: FN_MUT , & paths:: FN_ONCE ] ;
538-
539- struct FnPointerOrClosureTraitBoundFinder < ' a , ' tcx > {
540- cx : & ' a LateContext < ' tcx > ,
541- found : bool ,
542- }
543-
544- impl < ' a , ' tcx > FnPointerOrClosureTraitBoundFinder < ' a , ' tcx > {
545- fn find_in_generics ( cx : & ' a LateContext < ' tcx > , generics : & ' tcx Generics < ' tcx > ) -> bool {
546- let mut finder = Self { cx, found : false } ;
547- finder. visit_generics ( generics) ;
548- finder. found
549- }
550-
551- fn find_in_fn_decl ( cx : & ' a LateContext < ' tcx > , generics : & ' tcx FnDecl < ' tcx > ) -> bool {
552- let mut finder = Self { cx, found : false } ;
553- finder. visit_fn_decl ( generics) ;
554- finder. found
555- }
556- }
557-
558- impl < ' a , ' tcx > Visitor < ' tcx > for FnPointerOrClosureTraitBoundFinder < ' a , ' tcx > {
559- type Map = Map < ' tcx > ;
560- fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
561- NestedVisitorMap :: None
562- }
563-
564- fn visit_trait_ref ( & mut self , tref : & ' tcx TraitRef < ' tcx > ) {
565- if CLOSURE_TRAIT_BOUNDS
566- . iter ( )
567- . any ( |trait_path| tref. trait_def_id ( ) == get_trait_def_id ( self . cx , trait_path) )
568- {
569- self . found = true ;
570- }
571- walk_trait_ref ( self , tref) ;
572- }
573-
574- fn visit_ty ( & mut self , ty : & ' tcx Ty < ' tcx > ) {
575- match ty. kind {
576- TyKind :: BareFn ( ..) => self . found = true ,
577- TyKind :: OpaqueDef ( item_id, _) => {
578- let map = self . cx . tcx . hir ( ) ;
579- let item = map. expect_item ( item_id. id ) ;
580- self . visit_item ( item) ;
581- } ,
582- _ => ( ) ,
583- }
584- walk_ty ( self , ty) ;
585- }
586- }
0 commit comments