Skip to content

Commit d1b1f3e

Browse files
committed
Only focus in function return type, ignore struct generics
1 parent fd4f98d commit d1b1f3e

File tree

3 files changed

+165
-78
lines changed

3 files changed

+165
-78
lines changed

clippy_lints/src/functions/hidden_static_lifetime.rs

+120-47
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
11
use crate::rustc_lint::LintContext;
22
use clippy_utils::diagnostics::span_lint_and_help;
33
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,
87
};
98
use rustc_lint::LateContext;
10-
use rustc_middle::{hir::map::Map, lint::in_external_macro};
9+
use rustc_middle::lint::in_external_macro;
1110
use rustc_span::Span;
1211

1312
use super::HIDDEN_STATIC_LIFETIME;
1413

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+
1523
pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, span: Span) {
1624
if !in_external_macro(cx.sess(), span) && let FnKind::ItemFn(_, generics, _) =
1725
kind { let mut lifetime_is_used;
@@ -57,18 +65,16 @@ kind { let mut lifetime_is_used;
5765
for ty_binding in gen_args.bindings {
5866
if let TypeBindingKind::Equality { .. } = ty_binding.kind {
5967
lifetime_is_used = true;
60-
}
61-
}
62-
}
63-
}
68+
};
69+
};
70+
};
71+
};
6472
} else {
6573
span_lint_and_help(cx,
6674
HIDDEN_STATIC_LIFETIME,
6775
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()));
7278
};
7379
};
7480
};
@@ -83,18 +89,16 @@ generic.name.ident().as_str()), );
8389
region_predicate.span,
8490
"this lifetime can be changed to `'static`",
8591
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()));
8893
};
8994
};
9095
};
9196
};
9297

9398
// Check again.
9499
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) {
98102
span_lint_and_help(cx,
99103
HIDDEN_STATIC_LIFETIME,
100104
generic.span,
@@ -103,7 +107,7 @@ generic.name.ident().as_str()), );
103107
&format!(
104108
"try removing the lifetime parameter `{}` and changing references to `'static`",
105109
generic.name.ident().as_str()),
106-
);
110+
);
107111
};
108112
};
109113
};
@@ -171,37 +175,106 @@ fn check_validness(ret_ty: &Ty<'_>, generic: &GenericParam<'_>, generics: &Gener
171175
true
172176
}
173177

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;
196189
}
190+
} else if let GenericArg::Type(ty) = arg {
191+
return classify_ty(ty, generic);
197192
}
198193
}
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;
201240
};
202-
// Don't lint if struct isn't local.
203-
return false
204241
}
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
207280
}

tests/ui/hidden_static_lifetime.rs

+35-5
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,19 @@ mod module {
1414

1515
struct SMut<'a>(&'a mut i32);
1616
struct STuple<'a>(&'a i32);
17+
struct SGeneric<T>(T);
18+
struct SRef<'a, T: 'a>(&'a T);
19+
struct Both<'a, 'b, T> {
20+
owned: T,
21+
borrow: &'a T,
22+
mut_borrow: &'b mut T,
23+
}
1724

1825
// ============= Should warn =============
1926

2027
fn a<'a>() -> &'a str {
2128
""
2229
}
23-
fn h<'h>() -> S<'h> {
24-
S { s: &1 }
25-
}
2630

2731
// Valid
2832
fn o<'o, T>() -> &'o mut T
@@ -46,6 +50,11 @@ fn q<'q>() -> STuple<'q> {
4650
STuple(&1)
4751
}
4852

53+
// Only 'r1
54+
fn r<'r1, 'r2>() -> &'r1 SMut<'r2> {
55+
unsafe { std::ptr::null::<&SMut<'r2>>().read() }
56+
}
57+
4958
// ============= Should not warn =============
5059
fn b<'b>(_: &'b str) -> &'b str {
5160
""
@@ -54,6 +63,11 @@ fn d<'d>(_: &'d str) {}
5463
fn e<'e>(_: &'e str) -> &'e str {
5564
""
5665
}
66+
67+
fn h<'h>() -> S<'h> {
68+
S { s: &1 }
69+
}
70+
5771
fn f<'f, F>(_: F) -> F
5872
where
5973
F: 'f,
@@ -94,8 +108,24 @@ fn p<'p>() -> SMut<'p> {
94108
unsafe { std::ptr::null::<SMut<'p>>().read() }
95109
}
96110

97-
fn r<'r1, 'r2>() -> &'r1 SMut<'r2> {
98-
unsafe { std::ptr::null::<&SMut<'r2>>().read() }
111+
fn t<'t1, 't2>() -> SGeneric<&'t1 mut SRef<'t2, u32>> {
112+
unsafe { std::ptr::null::<SGeneric<&mut SRef<'t2, u32>>>().read() }
113+
}
114+
115+
fn u<'u1, 'u2>() -> &'u1 Both<'u1, 'u2, &'u1 str> {
116+
unsafe { std::ptr::null::<&Both<'u1, 'u2, &'u1 str>>().read() }
117+
}
118+
119+
fn v<'v1, 'v2, 'v3>() -> &'v1 Both<'v1, 'v2, &'v3 str> {
120+
unsafe { std::ptr::null::<&Both<'v1, 'v2, &'v3 str>>().read() }
121+
}
122+
123+
fn w<'w1, 'w2>() -> &'w1 Both<'w1, 'w2, &'static str> {
124+
unsafe { std::ptr::null::<&Both<'w1, 'w2, &'static str>>().read() }
125+
}
126+
127+
fn x<'x>() -> SRef<'x, fn(SGeneric<SRef<'x, u32>>)> {
128+
unsafe { std::ptr::null::<SRef<'x, fn(SGeneric<SRef<'x, u32>>)>>().read() }
99129
}
100130

101131
fn main() {}
+10-26
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: this lifetime can be changed to `'static`
2-
--> $DIR/hidden_static_lifetime.rs:20:6
2+
--> $DIR/hidden_static_lifetime.rs:27:6
33
|
44
LL | fn a<'a>() -> &'a str {
55
| ^^
@@ -8,60 +8,44 @@ LL | fn a<'a>() -> &'a str {
88
= note: `-D clippy::hidden-static-lifetime` implied by `-D warnings`
99

1010
error: this lifetime can be changed to `'static`
11-
--> $DIR/hidden_static_lifetime.rs:23:6
12-
|
13-
LL | fn h<'h>() -> S<'h> {
14-
| ^^
15-
|
16-
= help: try removing the lifetime parameter `'h` and changing references to `'static`
17-
18-
error: this lifetime can be changed to `'static`
19-
--> $DIR/hidden_static_lifetime.rs:28:6
11+
--> $DIR/hidden_static_lifetime.rs:32:6
2012
|
2113
LL | fn o<'o, T>() -> &'o mut T
2214
| ^^
2315
|
2416
= help: try removing the lifetime parameter `'o` and changing references to `'static`
2517

2618
error: this lifetime can be changed to `'static`
27-
--> $DIR/hidden_static_lifetime.rs:36:6
19+
--> $DIR/hidden_static_lifetime.rs:40:6
2820
|
2921
LL | fn n<'m1, 'm2, T>() -> &'m1 fn(&'m2 T) {
3022
| ^^^
3123
|
3224
= help: try removing the lifetime parameter `'m1` and changing references to `'static`
3325

3426
error: this lifetime can be changed to `'static`
35-
--> $DIR/hidden_static_lifetime.rs:41:6
27+
--> $DIR/hidden_static_lifetime.rs:45:6
3628
|
3729
LL | fn s<'s1, 's2>() -> &'s1 STuple<'s2> {
3830
| ^^^
3931
|
4032
= help: try removing the lifetime parameter `'s1` and changing references to `'static`
4133

4234
error: this lifetime can be changed to `'static`
43-
--> $DIR/hidden_static_lifetime.rs:41:11
44-
|
45-
LL | fn s<'s1, 's2>() -> &'s1 STuple<'s2> {
46-
| ^^^
47-
|
48-
= help: try removing the lifetime parameter `'s2` and changing references to `'static`
49-
50-
error: this lifetime can be changed to `'static`
51-
--> $DIR/hidden_static_lifetime.rs:45:6
35+
--> $DIR/hidden_static_lifetime.rs:54:6
5236
|
53-
LL | fn q<'q>() -> STuple<'q> {
54-
| ^^
37+
LL | fn r<'r1, 'r2>() -> &'r1 SMut<'r2> {
38+
| ^^^
5539
|
56-
= help: try removing the lifetime parameter `'q` and changing references to `'static`
40+
= help: try removing the lifetime parameter `'r1` and changing references to `'static`
5741

5842
error: this lifetime can be changed to `'static`
59-
--> $DIR/hidden_static_lifetime.rs:86:6
43+
--> $DIR/hidden_static_lifetime.rs:100:6
6044
|
6145
LL | fn l<'l, T>() -> &'l mut T
6246
| ^^
6347
|
6448
= help: try removing the lifetime parameter `'l` and changing references to `'static`
6549

66-
error: aborting due to 8 previous errors
50+
error: aborting due to 6 previous errors
6751

0 commit comments

Comments
 (0)