@@ -6,6 +6,7 @@ use rustc::hir::intravisit::{Visitor, walk_ty, walk_ty_param_bound, walk_fn_decl
6
6
use std:: collections:: { HashSet , HashMap } ;
7
7
use syntax:: codemap:: Span ;
8
8
use utils:: { in_external_macro, span_lint, last_path_segment} ;
9
+ use syntax:: symbol:: keywords;
9
10
10
11
/// **What it does:** Checks for lifetime annotations which can be removed by
11
12
/// relying on lifetime elision.
@@ -58,20 +59,24 @@ impl LintPass for LifetimePass {
58
59
59
60
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for LifetimePass {
60
61
fn check_item ( & mut self , cx : & LateContext < ' a , ' tcx > , item : & ' tcx Item ) {
61
- if let ItemFn ( ref decl, _, _, _, ref generics, _ ) = item. node {
62
- check_fn_inner ( cx, decl, generics, item. span ) ;
62
+ if let ItemFn ( ref decl, _, _, _, ref generics, id ) = item. node {
63
+ check_fn_inner ( cx, decl, Some ( id ) , generics, item. span ) ;
63
64
}
64
65
}
65
66
66
67
fn check_impl_item ( & mut self , cx : & LateContext < ' a , ' tcx > , item : & ' tcx ImplItem ) {
67
- if let ImplItemKind :: Method ( ref sig, _ ) = item. node {
68
- check_fn_inner ( cx, & sig. decl , & sig. generics , item. span ) ;
68
+ if let ImplItemKind :: Method ( ref sig, id ) = item. node {
69
+ check_fn_inner ( cx, & sig. decl , Some ( id ) , & sig. generics , item. span ) ;
69
70
}
70
71
}
71
72
72
73
fn check_trait_item ( & mut self , cx : & LateContext < ' a , ' tcx > , item : & ' tcx TraitItem ) {
73
- if let TraitItemKind :: Method ( ref sig, _) = item. node {
74
- check_fn_inner ( cx, & sig. decl , & sig. generics , item. span ) ;
74
+ if let TraitItemKind :: Method ( ref sig, ref body) = item. node {
75
+ let body = match * body {
76
+ TraitMethod :: Required ( _) => None ,
77
+ TraitMethod :: Provided ( id) => Some ( id) ,
78
+ } ;
79
+ check_fn_inner ( cx, & sig. decl , body, & sig. generics , item. span ) ;
75
80
}
76
81
}
77
82
}
@@ -84,30 +89,32 @@ enum RefLt {
84
89
Named ( Name ) ,
85
90
}
86
91
87
- fn bound_lifetimes ( bound : & TyParamBound ) -> HirVec < & Lifetime > {
88
- if let TraitTyParamBound ( ref trait_ref, _) = * bound {
89
- trait_ref. trait_ref
90
- . path
91
- . segments
92
- . last ( )
93
- . expect ( "a path must have at least one segment" )
94
- . parameters
95
- . lifetimes ( )
96
- } else {
97
- HirVec :: new ( )
98
- }
99
- }
100
-
101
- fn check_fn_inner < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , decl : & ' tcx FnDecl , generics : & ' tcx Generics , span : Span ) {
92
+ fn check_fn_inner < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , decl : & ' tcx FnDecl , body : Option < BodyId > , generics : & ' tcx Generics , span : Span ) {
102
93
if in_external_macro ( cx, span) || has_where_lifetimes ( cx, & generics. where_clause ) {
103
94
return ;
104
95
}
105
96
106
- let bounds_lts = generics. ty_params
107
- . iter ( )
108
- . flat_map ( |typ| typ. bounds . iter ( ) . flat_map ( bound_lifetimes) ) ;
109
-
110
- if could_use_elision ( cx, decl, & generics. lifetimes , bounds_lts) {
97
+ let mut bounds_lts = Vec :: new ( ) ;
98
+ for typ in & generics. ty_params {
99
+ for bound in & typ. bounds {
100
+ if let TraitTyParamBound ( ref trait_ref, _) = * bound {
101
+ let bounds = trait_ref. trait_ref
102
+ . path
103
+ . segments
104
+ . last ( )
105
+ . expect ( "a path must have at least one segment" )
106
+ . parameters
107
+ . lifetimes ( ) ;
108
+ for bound in bounds {
109
+ if bound. name != "'static" && !bound. is_elided ( ) {
110
+ return ;
111
+ }
112
+ bounds_lts. push ( bound) ;
113
+ }
114
+ }
115
+ }
116
+ }
117
+ if could_use_elision ( cx, decl, body, & generics. lifetimes , bounds_lts) {
111
118
span_lint ( cx,
112
119
NEEDLESS_LIFETIMES ,
113
120
span,
@@ -116,11 +123,12 @@ fn check_fn_inner<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl, gene
116
123
report_extra_lifetimes ( cx, decl, generics) ;
117
124
}
118
125
119
- fn could_use_elision < ' a , ' tcx : ' a , T : Iterator < Item = & ' tcx Lifetime > > (
126
+ fn could_use_elision < ' a , ' tcx : ' a > (
120
127
cx : & LateContext < ' a , ' tcx > ,
121
128
func : & ' tcx FnDecl ,
129
+ body : Option < BodyId > ,
122
130
named_lts : & ' tcx [ LifetimeDef ] ,
123
- bounds_lts : T
131
+ bounds_lts : Vec < & ' tcx Lifetime > ,
124
132
) -> bool {
125
133
// There are two scenarios where elision works:
126
134
// * no output references, all input references have different LT
@@ -144,8 +152,22 @@ fn could_use_elision<'a, 'tcx: 'a, T: Iterator<Item = &'tcx Lifetime>>(
144
152
output_visitor. visit_ty ( ty) ;
145
153
}
146
154
147
- let input_lts = lts_from_bounds ( input_visitor. into_vec ( ) , bounds_lts) ;
148
- let output_lts = output_visitor. into_vec ( ) ;
155
+ let input_lts = match input_visitor. into_vec ( ) {
156
+ Some ( lts) => lts_from_bounds ( lts, bounds_lts. into_iter ( ) ) ,
157
+ None => return false ,
158
+ } ;
159
+ let output_lts = match output_visitor. into_vec ( ) {
160
+ Some ( val) => val,
161
+ None => return false ,
162
+ } ;
163
+
164
+ if let Some ( body_id) = body {
165
+ let mut checker = BodyLifetimeChecker { lifetimes_used_in_body : false } ;
166
+ checker. visit_expr ( & cx. tcx . hir . body ( body_id) . value ) ;
167
+ if checker. lifetimes_used_in_body {
168
+ return false ;
169
+ }
170
+ }
149
171
150
172
// check for lifetimes from higher scopes
151
173
for lt in input_lts. iter ( ) . chain ( output_lts. iter ( ) ) {
@@ -216,13 +238,15 @@ fn unique_lifetimes(lts: &[RefLt]) -> usize {
216
238
struct RefVisitor < ' a , ' tcx : ' a > {
217
239
cx : & ' a LateContext < ' a , ' tcx > ,
218
240
lts : Vec < RefLt > ,
241
+ abort : bool ,
219
242
}
220
243
221
244
impl < ' v , ' t > RefVisitor < ' v , ' t > {
222
245
fn new ( cx : & ' v LateContext < ' v , ' t > ) -> RefVisitor < ' v , ' t > {
223
246
RefVisitor {
224
247
cx : cx,
225
248
lts : Vec :: new ( ) ,
249
+ abort : false ,
226
250
}
227
251
}
228
252
@@ -240,8 +264,12 @@ impl<'v, 't> RefVisitor<'v, 't> {
240
264
}
241
265
}
242
266
243
- fn into_vec ( self ) -> Vec < RefLt > {
244
- self . lts
267
+ fn into_vec ( self ) -> Option < Vec < RefLt > > {
268
+ if self . abort {
269
+ None
270
+ } else {
271
+ Some ( self . lts )
272
+ }
245
273
}
246
274
247
275
fn collect_anonymous_lifetimes ( & mut self , qpath : & QPath , ty : & Ty ) {
@@ -292,7 +320,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
292
320
} ,
293
321
TyTraitObject ( ref bounds, ref lt) => {
294
322
if !lt. is_elided ( ) {
295
- self . record ( & Some ( * lt ) ) ;
323
+ self . abort = true ;
296
324
}
297
325
for bound in bounds {
298
326
self . visit_poly_trait_ref ( bound, TraitBoundModifier :: None ) ;
@@ -329,10 +357,13 @@ fn has_where_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, where_clause: &
329
357
walk_ty_param_bound ( & mut visitor, bound) ;
330
358
}
331
359
// and check that all lifetimes are allowed
332
- for lt in visitor. into_vec ( ) {
333
- if !allowed_lts. contains ( & lt) {
334
- return true ;
335
- }
360
+ match visitor. into_vec ( ) {
361
+ None => return false ,
362
+ Some ( lts) => for lt in lts {
363
+ if !allowed_lts. contains ( & lt) {
364
+ return true ;
365
+ }
366
+ } ,
336
367
}
337
368
} ,
338
369
WherePredicate :: EqPredicate ( ref pred) => {
@@ -384,3 +415,20 @@ fn report_extra_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, func: &'tcx
384
415
span_lint ( cx, UNUSED_LIFETIMES , v, "this lifetime isn't used in the function definition" ) ;
385
416
}
386
417
}
418
+
419
+ struct BodyLifetimeChecker {
420
+ lifetimes_used_in_body : bool ,
421
+ }
422
+
423
+ impl < ' tcx > Visitor < ' tcx > for BodyLifetimeChecker {
424
+ // for lifetimes as parameters of generics
425
+ fn visit_lifetime ( & mut self , lifetime : & ' tcx Lifetime ) {
426
+ if lifetime. name != keywords:: Invalid . name ( ) && lifetime. name != "'static" {
427
+ self . lifetimes_used_in_body = true ;
428
+ }
429
+ }
430
+
431
+ fn nested_visit_map < ' this > ( & ' this mut self ) -> NestedVisitorMap < ' this , ' tcx > {
432
+ NestedVisitorMap :: None
433
+ }
434
+ }
0 commit comments