Skip to content

Commit b5eb8c5

Browse files
author
Ype Kingma
committed
Squashed commit for issue 100051
1 parent 34cf47a commit b5eb8c5

File tree

3 files changed

+87
-6
lines changed

3 files changed

+87
-6
lines changed

clippy_lints/src/lifetimes_bound_nested_ref.rs

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,20 @@ use std::collections::BTreeSet;
2626

2727
use clippy_utils::diagnostics::span_lint;
2828
use rustc_hir::intravisit::FnKind;
29-
use rustc_hir::{GenericArg, GenericBound, Generics, Item, ItemKind, WherePredicate};
29+
use rustc_hir::{
30+
GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Item, ItemKind, LifetimeParamKind,
31+
WherePredicate,
32+
};
3033
use rustc_hir_analysis::hir_ty_to_ty;
3134
use rustc_lint::{LateContext, LateLintPass};
3235
use rustc_middle::ty::ty_kind::TyKind;
3336
use rustc_middle::ty::{BoundRegionKind, RegionKind, Ty};
3437
use rustc_session::impl_lint_pass;
3538
use rustc_span::Symbol;
3639

40+
extern crate rustc_type_ir;
41+
use rustc_type_ir::AliasKind;
42+
3743
declare_clippy_lint! {
3844
/// ### What it does
3945
/// Checks function arguments and return values that have a nested reference type with lifetimes,
@@ -119,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for LifetimesBoundNestedRef {
119125
let FnKind::ItemFn(_ident, generics, _fn_header) = fn_kind else {
120126
return;
121127
};
122-
if generics.params.is_empty() {
128+
if !at_least_2_explicit_lifetimes(generics.params) {
123129
return;
124130
}
125131
let declared_bounds = get_declared_bounds(generics);
@@ -140,12 +146,12 @@ impl<'tcx> LateLintPass<'tcx> for LifetimesBoundNestedRef {
140146
let ItemKind::Impl(impl_item) = item.kind else {
141147
return;
142148
};
143-
if impl_item.generics.params.is_empty() {
144-
return;
145-
}
146149
let Some(of_trait_ref) = impl_item.of_trait else {
147150
return;
148151
};
152+
if !at_least_2_explicit_lifetimes(impl_item.generics.params) {
153+
return;
154+
}
149155
let declared_bounds = get_declared_bounds(impl_item.generics);
150156
let mut implied_bounds = BTreeSet::new();
151157
for path_segment in of_trait_ref.path.segments {
@@ -159,6 +165,8 @@ impl<'tcx> LateLintPass<'tcx> for LifetimesBoundNestedRef {
159165
}
160166
}
161167
}
168+
let for_clause_ty = hir_ty_to_ty(cx.tcx, impl_item.self_ty); // impl ... for for_clause_type {}
169+
collect_nested_ref_implied_bounds(for_clause_ty, None, &mut implied_bounds);
162170
report_lints(cx, impl_item.generics.span, &declared_bounds, &implied_bounds);
163171
}
164172
}
@@ -206,6 +214,19 @@ impl Ord for BoundLftPair {
206214
}
207215
}
208216

217+
fn at_least_2_explicit_lifetimes<'a>(generic_params: &'a [GenericParam<'a>]) -> bool {
218+
generic_params
219+
.iter()
220+
.filter(|gp| match gp.kind {
221+
GenericParamKind::Lifetime {
222+
kind: LifetimeParamKind::Explicit,
223+
} => true,
224+
_ => false,
225+
})
226+
.enumerate()
227+
.any(|(i, _)| i >= 1)
228+
}
229+
209230
fn get_declared_bounds(generics: &Generics<'_>) -> BTreeSet<BoundLftPair> {
210231
let mut declared_bounds = BTreeSet::new();
211232
for where_predicate in generics.predicates {
@@ -269,6 +290,15 @@ fn collect_nested_ref_implied_bounds(
269290
TyKind::Slice(element_ty) => {
270291
collect_nested_ref_implied_bounds(element_ty, outlived_lft_sym_opt, implied_bounds);
271292
},
293+
TyKind::Alias(AliasKind::Projection, alias_ty) => {
294+
// a for clause in: impl ... for ... {}
295+
for alias_generic_arg in alias_ty.args {
296+
let Some(alias_ty) = alias_generic_arg.as_type() else {
297+
continue;
298+
};
299+
collect_nested_ref_implied_bounds(alias_ty, outlived_lft_sym_opt, implied_bounds);
300+
}
301+
},
272302
_ => {},
273303
}
274304
}

tests/ui/lifetimes_bound_nested_ref.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,45 @@ trait Subtrait2<'a, 'b, R>: Supertrait<'a, 'b> {}
3535
// issue 84591, with bound:
3636
impl<'a: 'b, 'b> Subtrait2<'a, 'b, &'b &'a ()> for MyStruct {}
3737

38+
// helper declarations for issue 100051
39+
trait Trait1 {
40+
type Type1;
41+
}
42+
43+
impl<T1> Trait1 for T1 {
44+
type Type1 = ();
45+
}
46+
47+
trait Extend1<'a, 'b> {
48+
fn extend(self, s: &'a str) -> &'b str;
49+
}
50+
51+
// issue 100051, without explicit lifetimes bound
52+
impl<'a, 'b> Extend1<'a, 'b> for <&'b &'a () as Trait1>::Type1 {
53+
fn extend(self, s: &'a str) -> &'b str {
54+
s
55+
}
56+
}
57+
58+
trait Trait2 {
59+
type Type2;
60+
}
61+
62+
impl<T2> Trait2 for T2 {
63+
type Type2 = ();
64+
}
65+
66+
trait Extend2<'a, 'b> {
67+
fn extend(self, s: &'a str) -> &'b str;
68+
}
69+
70+
// issue 100051, with explicit lifetimes bound
71+
impl<'a: 'b, 'b> Extend2<'a, 'b> for <&'b &'a () as Trait2>::Type2 {
72+
fn extend(self, s: &'a str) -> &'b str {
73+
s
74+
}
75+
}
76+
3877
fn main() {
3978
// test code goes here
4079
}

tests/ui/lifetimes_bound_nested_ref.stderr

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,17 @@ error: declared lifetime bound is implied: 'a: 'b
2828
LL | impl<'a: 'b, 'b> Subtrait2<'a, 'b, &'b &'a ()> for MyStruct {}
2929
| ^^^^^^^^^^^^
3030

31-
error: aborting due to 4 previous errors
31+
error: missing lifetime bound declation: 'a: 'b
32+
--> tests/ui/lifetimes_bound_nested_ref.rs:52:5
33+
|
34+
LL | impl<'a, 'b> Extend1<'a, 'b> for <&'b &'a () as Trait1>::Type1 {
35+
| ^^^^^^^^
36+
37+
error: declared lifetime bound is implied: 'a: 'b
38+
--> tests/ui/lifetimes_bound_nested_ref.rs:71:5
39+
|
40+
LL | impl<'a: 'b, 'b> Extend2<'a, 'b> for <&'b &'a () as Trait2>::Type2 {
41+
| ^^^^^^^^^^^^
42+
43+
error: aborting due to 6 previous errors
3244

0 commit comments

Comments
 (0)