1
1
use crate :: rustc_lint:: LintContext ;
2
2
use clippy_utils:: diagnostics:: span_lint_and_help;
3
3
use rustc_hir:: {
4
- def:: { DefKind , Res } ,
5
- intravisit:: FnKind ,
6
- FnDecl , FnRetTy , GenericArg , GenericBound , GenericParam , GenericParamKind , Generics , ItemKind , LifetimeParamKind ,
7
- Node , ParamName , QPath , Ty , TyKind , TypeBindingKind , WherePredicate ,
4
+ intravisit:: FnKind , BareFnTy , FnDecl , FnRetTy , GenericArg , GenericArgs , GenericBound , GenericParam ,
5
+ GenericParamKind , Generics , Lifetime , LifetimeParamKind , MutTy , ParamName , PolyTraitRef , QPath , Ty , TyKind ,
6
+ TypeBindingKind , WherePredicate ,
8
7
} ;
9
8
use rustc_lint:: LateContext ;
10
- use rustc_middle:: { hir :: map :: Map , lint:: in_external_macro} ;
9
+ use rustc_middle:: lint:: in_external_macro;
11
10
use rustc_span:: Span ;
12
11
13
12
use super :: HIDDEN_STATIC_LIFETIME ;
14
13
14
+ // As a summary:
15
+ //
16
+ // A lifetime can only be changed if:
17
+ // * Used in immutable references.
18
+ // * Not behind a mutable reference.
19
+ // * Not used in function types
20
+ //
21
+ // NOTE: Struct's fields follow the same rules as types
22
+
15
23
pub ( super ) fn check_fn < ' tcx > ( cx : & LateContext < ' _ > , kind : FnKind < ' tcx > , decl : & ' tcx FnDecl < ' _ > , span : Span ) {
16
24
if !in_external_macro ( cx. sess ( ) , span) && let FnKind :: ItemFn ( _, generics, _) =
17
25
kind { let mut lifetime_is_used;
@@ -57,18 +65,16 @@ kind { let mut lifetime_is_used;
57
65
for ty_binding in gen_args. bindings {
58
66
if let TypeBindingKind :: Equality { .. } = ty_binding. kind {
59
67
lifetime_is_used = true ;
60
- }
61
- }
62
- }
63
- }
68
+ } ;
69
+ } ;
70
+ } ;
71
+ } ;
64
72
} else {
65
73
span_lint_and_help ( cx,
66
74
HIDDEN_STATIC_LIFETIME ,
67
75
bound. span ( ) ,
68
- "this lifetime can be changed to `'static`" ,
69
- None ,
70
- & format ! ( "try removing the lifetime parameter `{}` and changing references to `'static`" ,
71
- generic. name. ident( ) . as_str( ) ) , ) ;
76
+ "this lifetime can be changed to `'static`" , None ,
77
+ & format ! ( "try removing the lifetime parameter `{}` and changing references to `'static`" , generic. name. ident( ) . as_str( ) ) ) ;
72
78
} ;
73
79
} ;
74
80
} ;
@@ -83,18 +89,16 @@ generic.name.ident().as_str()), );
83
89
region_predicate. span ,
84
90
"this lifetime can be changed to `'static`" ,
85
91
None ,
86
- & format ! ( "try removing the lifetime parameter `{}` and changing references to `'static`" ,
87
- generic. name. ident( ) . as_str( ) ) , ) ;
92
+ & format ! ( "try removing the lifetime parameter `{}` and changing references to `'static`" , generic. name. ident( ) . as_str( ) ) ) ;
88
93
} ;
89
94
} ;
90
95
} ;
91
96
} ;
92
97
93
98
// Check again.
94
99
if !lifetime_is_used &&
95
- // Check validness
96
- check_validness ( ret_ty, generic, generics)
97
- && check_mut_fields ( cx. tcx . hir ( ) , & ret_ty. peel_refs ( ) . kind ) {
100
+ check_validness ( ret_ty, generic, generics) &&
101
+ lifetime_not_used_in_ty ( ret_ty, generic) {
98
102
span_lint_and_help ( cx,
99
103
HIDDEN_STATIC_LIFETIME ,
100
104
generic. span ,
@@ -103,7 +107,7 @@ generic.name.ident().as_str()), );
103
107
& format ! (
104
108
"try removing the lifetime parameter `{}` and changing references to `'static`" ,
105
109
generic. name. ident( ) . as_str( ) ) ,
106
- ) ;
110
+ ) ;
107
111
} ;
108
112
} ;
109
113
} ;
@@ -171,37 +175,106 @@ fn check_validness(ret_ty: &Ty<'_>, generic: &GenericParam<'_>, generics: &Gener
171
175
true
172
176
}
173
177
174
- // true = no mut fields
175
- fn check_mut_fields ( map : Map < ' _ > , tykind : & TyKind < ' _ > ) -> bool {
176
- if_chain ! {
177
- if let TyKind :: Path ( qpath) = tykind;
178
- if let QPath :: Resolved ( _, path) = qpath;
179
- if let Res :: Def ( defkind, def_id) = path. res;
180
- if let DefKind :: Struct = defkind;
181
- then {
182
- if let Some ( node) = map. get_if_local( def_id) {
183
- if let Node :: Item ( item) = node {
184
- if let ItemKind :: Struct ( variant_data, _) = & item. kind {
185
- for field in variant_data. fields( ) {
186
- if let TyKind :: Ref ( _, mut_ty) = & field. ty. kind {
187
- if mut_ty. mutbl. is_mut( ) {
188
- return false ;
189
- } ;
190
- return true ;
191
- } else if let TyKind :: Ptr ( mut_ty) = & field. ty. kind {
192
- if mut_ty. mutbl. is_mut( ) {
193
- return false ;
194
- } ;
195
- return true ;
178
+ // true = lifetime isn't used (success)
179
+ fn lifetime_not_used_in_ty ( ret_ty : & Ty < ' _ > , generic : & GenericParam < ' _ > ) -> bool {
180
+ fn ty_uses_lifetime ( ty : & Ty < ' _ > , generic : & GenericParam < ' _ > ) -> bool {
181
+ if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = ty. kind {
182
+ for segment in path. segments {
183
+ if let Some ( GenericArgs { args, .. } ) = segment. args {
184
+ for arg in args. iter ( ) {
185
+ if let GenericArg :: Lifetime ( lifetime) = arg {
186
+ if lifetime. ident . name == generic. name . ident ( ) . name {
187
+ // generic is used
188
+ return false ;
196
189
}
190
+ } else if let GenericArg :: Type ( ty) = arg {
191
+ return classify_ty ( ty, generic) ;
197
192
}
198
193
}
199
- } ;
200
- return true ;
194
+ }
195
+ }
196
+ }
197
+ true
198
+ }
199
+
200
+ #[ inline]
201
+ fn barefn_uses_lifetime ( barefn : & BareFnTy < ' _ > , generic : & GenericParam < ' _ > ) -> bool {
202
+ for param in barefn. generic_params {
203
+ if param. def_id == generic. def_id {
204
+ return false ;
205
+ }
206
+ }
207
+ true
208
+ }
209
+
210
+ #[ inline]
211
+ fn tuple_uses_lifetime ( tuple : & [ Ty < ' _ > ] , generic : & GenericParam < ' _ > ) -> bool {
212
+ tuple. iter ( ) . any ( |ty| classify_ty ( ty, generic) )
213
+ }
214
+
215
+ fn opaquedef_uses_lifetime ( args : & [ GenericArg < ' _ > ] , generic : & GenericParam < ' _ > ) -> bool {
216
+ for arg in args. iter ( ) {
217
+ if let GenericArg :: Lifetime ( lifetime) = arg {
218
+ if lifetime. ident . name == generic. name . ident ( ) . name {
219
+ // generic is used
220
+ return false ;
221
+ }
222
+ } else if let GenericArg :: Type ( ty) = arg {
223
+ return classify_ty ( ty, generic) ;
224
+ }
225
+ }
226
+ true
227
+ }
228
+
229
+ #[ inline]
230
+ fn traitobject_uses_lifetime ( lifetime : & Lifetime , traits : & [ PolyTraitRef < ' _ > ] , generic : & GenericParam < ' _ > ) -> bool {
231
+ if lifetime. ident . name == generic. name . ident ( ) . name {
232
+ return true ;
233
+ }
234
+ for PolyTraitRef {
235
+ bound_generic_params, ..
236
+ } in traits
237
+ {
238
+ if bound_generic_params. iter ( ) . any ( |param| param. def_id == generic. def_id ) {
239
+ return false ;
201
240
} ;
202
- // Don't lint if struct isn't local.
203
- return false
204
241
}
205
- } ;
206
- true
242
+ true
243
+ }
244
+
245
+ #[ inline]
246
+ fn classify_ty ( ty : & Ty < ' _ > , generic : & GenericParam < ' _ > ) -> bool {
247
+ match & ty. kind {
248
+ TyKind :: Slice ( ty) | TyKind :: Array ( ty, _) => ty_uses_lifetime ( ty, generic) ,
249
+ TyKind :: Ptr ( mut_ty) => ty_uses_lifetime ( mut_ty. ty , generic) ,
250
+ TyKind :: BareFn ( barefnty) => barefn_uses_lifetime ( barefnty, generic) ,
251
+ TyKind :: Tup ( tuple) => tuple_uses_lifetime ( tuple, generic) ,
252
+ TyKind :: Path ( _) => ty_uses_lifetime ( ty, generic) ,
253
+ TyKind :: OpaqueDef ( _, genericargs, _) => opaquedef_uses_lifetime ( genericargs, generic) ,
254
+ TyKind :: TraitObject ( poly_trait_ref, lifetime, _) =>
255
+ traitobject_uses_lifetime ( lifetime, poly_trait_ref, generic) ,
256
+ TyKind :: Typeof ( _) // This is unused for now, this needs revising when Typeof is used.
257
+ | TyKind :: Err
258
+ | TyKind :: Ref ( _, _) /* This will never be the case*/
259
+ | TyKind :: Never => true ,
260
+ TyKind :: Infer => false ,
261
+ }
262
+ }
263
+
264
+ // Separate refs from ty.
265
+ let mut can_change = false ;
266
+ let mut final_ty = ret_ty;
267
+ while let TyKind :: Ref ( lifetime, MutTy { ty, .. } ) = & final_ty. kind {
268
+ if lifetime. ident . name == generic. name . ident ( ) . name {
269
+ can_change = true ;
270
+ } ;
271
+ final_ty = ty;
272
+ }
273
+
274
+ // Now final_ty is equivalent to ret_ty.peel_refs
275
+
276
+ if can_change {
277
+ return classify_ty ( final_ty, generic) ;
278
+ }
279
+ false
207
280
}
0 commit comments