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_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 ,
5
7
} ;
6
8
use rustc_hir:: FnRetTy :: Return ;
7
9
use 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 ,
11
13
} ;
12
14
use rustc_lint:: { LateContext , LateLintPass } ;
13
15
use rustc_middle:: hir:: map:: Map ;
14
16
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
15
17
use rustc_span:: source_map:: Span ;
16
18
use 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 ;
19
20
20
21
declare_clippy_lint ! {
21
22
/// **What it does:** Checks for lifetime annotations which can be removed by
@@ -25,8 +26,11 @@ declare_clippy_lint! {
25
26
/// complicated, while there is nothing out of the ordinary going on. Removing
26
27
/// them leads to more readable code.
27
28
///
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`.
30
34
///
31
35
/// **Example:**
32
36
/// ```rust
@@ -108,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
108
112
}
109
113
110
114
/// The lifetime of a &-reference.
111
- #[ derive( PartialEq , Eq , Hash , Debug ) ]
115
+ #[ derive( PartialEq , Eq , Hash , Debug , Clone ) ]
112
116
enum RefLt {
113
117
Unnamed ,
114
118
Static ,
@@ -127,7 +131,6 @@ fn check_fn_inner<'tcx>(
127
131
return ;
128
132
}
129
133
130
- let mut bounds_lts = Vec :: new ( ) ;
131
134
let types = generics
132
135
. params
133
136
. iter ( )
@@ -156,13 +159,12 @@ fn check_fn_inner<'tcx>(
156
159
if bound. name != LifetimeName :: Static && !bound. is_elided ( ) {
157
160
return ;
158
161
}
159
- bounds_lts. push ( bound) ;
160
162
}
161
163
}
162
164
}
163
165
}
164
166
}
165
- if could_use_elision ( cx, decl, body, & generics. params , bounds_lts ) {
167
+ if could_use_elision ( cx, decl, body, & generics. params ) {
166
168
span_lint (
167
169
cx,
168
170
NEEDLESS_LIFETIMES ,
@@ -181,7 +183,6 @@ fn could_use_elision<'tcx>(
181
183
func : & ' tcx FnDecl < ' _ > ,
182
184
body : Option < BodyId > ,
183
185
named_generics : & ' tcx [ GenericParam < ' _ > ] ,
184
- bounds_lts : Vec < & ' tcx Lifetime > ,
185
186
) -> bool {
186
187
// There are two scenarios where elision works:
187
188
// * no output references, all input references have different LT
@@ -204,15 +205,31 @@ fn could_use_elision<'tcx>(
204
205
if let Return ( ref ty) = func. output {
205
206
output_visitor. visit_ty ( ty) ;
206
207
}
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
+ }
207
215
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 ;
216
233
217
234
if let Some ( body_id) = body {
218
235
let mut checker = BodyLifetimeChecker {
@@ -277,35 +294,29 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
277
294
allowed_lts
278
295
}
279
296
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
-
290
297
/// Number of unique lifetimes in the given vector.
291
298
#[ must_use]
292
299
fn unique_lifetimes ( lts : & [ RefLt ] ) -> usize {
293
300
lts. iter ( ) . collect :: < FxHashSet < _ > > ( ) . len ( )
294
301
}
295
302
303
+ const CLOSURE_TRAIT_BOUNDS : [ & [ & str ] ; 3 ] = [ & paths:: FN , & paths:: FN_MUT , & paths:: FN_ONCE ] ;
304
+
296
305
/// A visitor usable for `rustc_front::visit::walk_ty()`.
297
306
struct RefVisitor < ' a , ' tcx > {
298
307
cx : & ' a LateContext < ' tcx > ,
299
308
lts : Vec < RefLt > ,
300
- abort : bool ,
309
+ nested_elision_site_lts : Vec < RefLt > ,
310
+ unelided_trait_object_lifetime : bool ,
301
311
}
302
312
303
313
impl < ' a , ' tcx > RefVisitor < ' a , ' tcx > {
304
314
fn new ( cx : & ' a LateContext < ' tcx > ) -> Self {
305
315
Self {
306
316
cx,
307
317
lts : Vec :: new ( ) ,
308
- abort : false ,
318
+ nested_elision_site_lts : Vec :: new ( ) ,
319
+ unelided_trait_object_lifetime : false ,
309
320
}
310
321
}
311
322
@@ -325,40 +336,16 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
325
336
}
326
337
}
327
338
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 < _ > > ( )
334
345
}
335
346
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
362
349
}
363
350
}
364
351
@@ -370,30 +357,37 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
370
357
self . record ( & Some ( * lifetime) ) ;
371
358
}
372
359
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
+
373
374
fn visit_ty ( & mut self , ty : & ' tcx Ty < ' _ > ) {
374
375
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
- } ,
381
376
TyKind :: OpaqueDef ( item, _) => {
382
377
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) ;
392
380
walk_ty ( self , ty) ;
393
381
} ,
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
+ } ,
394
388
TyKind :: TraitObject ( bounds, ref lt) => {
395
389
if !lt. is_elided ( ) {
396
- self . abort = true ;
390
+ self . unelided_trait_object_lifetime = true ;
397
391
}
398
392
for bound in bounds {
399
393
self . visit_poly_trait_ref ( bound, TraitBoundModifier :: None ) ;
@@ -430,16 +424,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl
430
424
walk_param_bound ( & mut visitor, bound) ;
431
425
}
432
426
// 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) ) ;
443
428
} ,
444
429
WherePredicate :: EqPredicate ( ref pred) => {
445
430
let mut visitor = RefVisitor :: new ( cx) ;
0 commit comments