1
+ use crate :: utils:: paths;
2
+ use crate :: utils:: { get_trait_def_id, in_macro, span_lint, trait_ref_of_method} ;
1
3
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
2
- use rustc_hir:: def:: { DefKind , Res } ;
3
4
use 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 ,
6
7
} ;
7
8
use rustc_hir:: FnRetTy :: Return ;
8
9
use 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 ,
12
13
} ;
13
14
use rustc_lint:: { LateContext , LateLintPass } ;
14
15
use rustc_middle:: hir:: map:: Map ;
15
16
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
16
17
use rustc_span:: source_map:: Span ;
17
18
use 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 ;
21
20
22
21
declare_clippy_lint ! {
23
22
/// **What it does:** Checks for lifetime annotations which can be removed by
@@ -110,7 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
110
109
}
111
110
112
111
/// The lifetime of a &-reference.
113
- #[ derive( PartialEq , Eq , Hash , Debug ) ]
112
+ #[ derive( PartialEq , Eq , Hash , Debug , Clone ) ]
114
113
enum RefLt {
115
114
Unnamed ,
116
115
Static ,
@@ -129,15 +128,6 @@ fn check_fn_inner<'tcx>(
129
128
return ;
130
129
}
131
130
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 ( ) ;
141
131
let types = generics
142
132
. params
143
133
. iter ( )
@@ -166,13 +156,12 @@ fn check_fn_inner<'tcx>(
166
156
if bound. name != LifetimeName :: Static && !bound. is_elided ( ) {
167
157
return ;
168
158
}
169
- bounds_lts. push ( bound) ;
170
159
}
171
160
}
172
161
}
173
162
}
174
163
}
175
- if could_use_elision ( cx, decl, body, & generics. params , bounds_lts ) {
164
+ if could_use_elision ( cx, decl, body, & generics. params ) {
176
165
span_lint (
177
166
cx,
178
167
NEEDLESS_LIFETIMES ,
@@ -191,7 +180,6 @@ fn could_use_elision<'tcx>(
191
180
func : & ' tcx FnDecl < ' _ > ,
192
181
body : Option < BodyId > ,
193
182
named_generics : & ' tcx [ GenericParam < ' _ > ] ,
194
- bounds_lts : Vec < & ' tcx Lifetime > ,
195
183
) -> bool {
196
184
// There are two scenarios where elision works:
197
185
// * no output references, all input references have different LT
@@ -214,15 +202,31 @@ fn could_use_elision<'tcx>(
214
202
if let Return ( ref ty) = func. output {
215
203
output_visitor. visit_ty ( ty) ;
216
204
}
205
+ for lt in named_generics {
206
+ input_visitor. visit_generic_param ( lt)
207
+ }
217
208
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 ;
226
230
227
231
if let Some ( body_id) = body {
228
232
let mut checker = BodyLifetimeChecker {
@@ -287,35 +291,29 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
287
291
allowed_lts
288
292
}
289
293
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
-
300
294
/// Number of unique lifetimes in the given vector.
301
295
#[ must_use]
302
296
fn unique_lifetimes ( lts : & [ RefLt ] ) -> usize {
303
297
lts. iter ( ) . collect :: < FxHashSet < _ > > ( ) . len ( )
304
298
}
305
299
300
+ const CLOSURE_TRAIT_BOUNDS : [ & [ & str ] ; 3 ] = [ & paths:: FN , & paths:: FN_MUT , & paths:: FN_ONCE ] ;
301
+
306
302
/// A visitor usable for `rustc_front::visit::walk_ty()`.
307
303
struct RefVisitor < ' a , ' tcx > {
308
304
cx : & ' a LateContext < ' tcx > ,
309
305
lts : Vec < RefLt > ,
310
- abort : bool ,
306
+ nested_elision_site_lts : Vec < RefLt > ,
307
+ unelided_trait_object_lifetime : bool ,
311
308
}
312
309
313
310
impl < ' a , ' tcx > RefVisitor < ' a , ' tcx > {
314
311
fn new ( cx : & ' a LateContext < ' tcx > ) -> Self {
315
312
Self {
316
313
cx,
317
314
lts : Vec :: new ( ) ,
318
- abort : false ,
315
+ nested_elision_site_lts : Vec :: new ( ) ,
316
+ unelided_trait_object_lifetime : false ,
319
317
}
320
318
}
321
319
@@ -335,40 +333,16 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
335
333
}
336
334
}
337
335
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 < _ > > ( )
344
342
}
345
343
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
372
346
}
373
347
}
374
348
@@ -380,30 +354,36 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
380
354
self . record ( & Some ( * lifetime) ) ;
381
355
}
382
356
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
+
383
371
fn visit_ty ( & mut self , ty : & ' tcx Ty < ' _ > ) {
384
372
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
- } ,
391
373
TyKind :: OpaqueDef ( item, _) => {
392
374
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) ;
402
377
walk_ty ( self , ty) ;
403
378
} ,
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
+ } ,
404
384
TyKind :: TraitObject ( bounds, ref lt) => {
405
385
if !lt. is_elided ( ) {
406
- self . abort = true ;
386
+ self . unelided_trait_object_lifetime = true ;
407
387
}
408
388
for bound in bounds {
409
389
self . visit_poly_trait_ref ( bound, TraitBoundModifier :: None ) ;
@@ -440,16 +420,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl
440
420
walk_param_bound ( & mut visitor, bound) ;
441
421
}
442
422
// 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) ) ;
453
424
} ,
454
425
WherePredicate :: EqPredicate ( ref pred) => {
455
426
let mut visitor = RefVisitor :: new ( cx) ;
@@ -533,54 +504,3 @@ impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker {
533
504
NestedVisitorMap :: None
534
505
}
535
506
}
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