Skip to content

Commit 5fce361

Browse files
committed
Account for impl Trait in lifetime suggestion
When encountering ```rust fn g(mut x: impl Iterator<Item = &()>) -> Option<&()> { /* */ } ``` Suggest ```rust fn g<'a>(mut x: impl Iterator<Item = &'a ()>) -> Option<&'a ()> { /* */ } ```
1 parent b7a23bc commit 5fce361

File tree

2 files changed

+84
-17
lines changed

2 files changed

+84
-17
lines changed

compiler/rustc_resolve/src/late/diagnostics.rs

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2988,6 +2988,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
29882988
} else if let TyKind::CVarArgs = param.ty.kind {
29892989
// Don't suggest `&...` for ffi fn with varargs
29902990
None
2991+
} else if let TyKind::ImplTrait(..) = &param.ty.kind {
2992+
// We handle these in the next `else if` branch.
2993+
None
29912994
} else {
29922995
Some((param.ty.span.shrink_to_lo(), "&".to_string()))
29932996
}
@@ -3010,6 +3013,64 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
30103013
Applicability::MaybeIncorrect,
30113014
);
30123015
"...or alternatively,"
3016+
} else if let Some((kind, _span)) =
3017+
self.diagnostic_metadata.current_function
3018+
&& let FnKind::Fn(_, _, sig, _, _, _) = kind
3019+
&& let ast::FnRetTy::Ty(ret_ty) = &sig.decl.output
3020+
&& !sig.decl.inputs.is_empty()
3021+
&& let arg_refs = sig
3022+
.decl
3023+
.inputs
3024+
.iter()
3025+
.filter_map(|param| match &param.ty.kind {
3026+
TyKind::ImplTrait(_, bounds) => Some(bounds),
3027+
_ => None,
3028+
})
3029+
.flat_map(|bounds| bounds.into_iter())
3030+
.collect::<Vec<_>>()
3031+
&& !arg_refs.is_empty()
3032+
{
3033+
// We have a situation like
3034+
// fn g(mut x: impl Iterator<Item = &()>) -> Option<&()>
3035+
// So we look at every ref in the trait bound. If there's any, we
3036+
// suggest
3037+
// fn g<'a>(mut x: impl Iterator<Item = &'a ()>) -> Option<&'a ()>
3038+
let mut lt_finder = LifetimeFinder {
3039+
lifetime: lt.span,
3040+
found: None,
3041+
seen: vec![],
3042+
};
3043+
for bound in arg_refs {
3044+
if let ast::GenericBound::Trait(trait_ref, _) = bound {
3045+
lt_finder.visit_trait_ref(&trait_ref.trait_ref);
3046+
}
3047+
}
3048+
lt_finder.visit_ty(ret_ty);
3049+
let spans_suggs: Vec<_> = lt_finder.seen.iter().filter_map(|ty| {
3050+
match &ty.kind {
3051+
TyKind::Ref(_, mut_ty) => {
3052+
let span = ty.span.with_hi(mut_ty.ty.span.lo());
3053+
Some((span, "&'a ".to_string()))
3054+
}
3055+
_ => None
3056+
}
3057+
}).collect();
3058+
self.suggest_introducing_lifetime(
3059+
err,
3060+
None,
3061+
|err, higher_ranked, span, message, intro_sugg| {
3062+
info!(?span, ?message, ?intro_sugg);
3063+
err.multipart_suggestion_verbose(
3064+
message,
3065+
std::iter::once((span, intro_sugg))
3066+
.chain(spans_suggs.iter().cloned())
3067+
.collect(),
3068+
Applicability::MaybeIncorrect,
3069+
);
3070+
higher_ranked
3071+
},
3072+
);
3073+
"...or alternatively,"
30133074
} else {
30143075
"instead, you are more likely"
30153076
};
@@ -3019,7 +3080,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
30193080
&& let FnKind::Fn(_, _, sig, _, _, _) = kind
30203081
&& let ast::FnRetTy::Ty(ty) = &sig.decl.output
30213082
{
3022-
let mut lt_finder = LifetimeFinder { lifetime: lt.span, found: None };
3083+
let mut lt_finder = LifetimeFinder {
3084+
lifetime: lt.span,
3085+
found: None,
3086+
seen: vec![],
3087+
};
30233088
lt_finder.visit_ty(&ty);
30243089

30253090
if let Some(ty) = lt_finder.found {
@@ -3155,14 +3220,16 @@ pub(super) fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: I
31553220
struct LifetimeFinder<'ast> {
31563221
lifetime: Span,
31573222
found: Option<&'ast Ty>,
3223+
seen: Vec<&'ast Ty>,
31583224
}
31593225

31603226
impl<'ast> Visitor<'ast> for LifetimeFinder<'ast> {
31613227
fn visit_ty(&mut self, t: &'ast Ty) {
3162-
if t.span.lo() == self.lifetime.lo()
3163-
&& let TyKind::Ref(_, mut_ty) = &t.kind
3164-
{
3165-
self.found = Some(&mut_ty.ty);
3228+
if let TyKind::Ref(_, mut_ty) = &t.kind {
3229+
self.seen.push(t);
3230+
if t.span.lo() == self.lifetime.lo() {
3231+
self.found = Some(&mut_ty.ty);
3232+
}
31663233
}
31673234
walk_ty(self, t)
31683235
}

tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're
99
|
1010
LL | fn g(mut x: impl Iterator<Item = &()>) -> Option<&'static ()> { x.next() }
1111
| +++++++
12-
help: instead, you are more likely to want to change the argument to be borrowed...
12+
help: consider introducing a named lifetime parameter
1313
|
14-
LL | fn g(mut x: &impl Iterator<Item = &()>) -> Option<&()> { x.next() }
15-
| +
14+
LL | fn g<'a>(mut x: impl Iterator<Item = &'a ()>) -> Option<&'a ()> { x.next() }
15+
| ++++ ~~~ ~~~
1616
help: ...or alternatively, to want to return an owned value
1717
|
1818
LL - fn g(mut x: impl Iterator<Item = &()>) -> Option<&()> { x.next() }
@@ -30,10 +30,10 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're
3030
|
3131
LL | async fn i(mut x: impl Iterator<Item = &()>) -> Option<&'static ()> { x.next() }
3232
| +++++++
33-
help: instead, you are more likely to want to change the argument to be borrowed...
33+
help: consider introducing a named lifetime parameter
3434
|
35-
LL | async fn i(mut x: &impl Iterator<Item = &()>) -> Option<&()> { x.next() }
36-
| +
35+
LL | async fn i<'a>(mut x: impl Iterator<Item = &'a ()>) -> Option<&'a ()> { x.next() }
36+
| ++++ ~~~ ~~~
3737
help: ...or alternatively, to want to return an owned value
3838
|
3939
LL - async fn i(mut x: impl Iterator<Item = &()>) -> Option<&()> { x.next() }
@@ -75,10 +75,10 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're
7575
|
7676
LL | fn g(mut x: impl Foo) -> Option<&'static ()> { x.next() }
7777
| +++++++
78-
help: instead, you are more likely to want to change the argument to be borrowed...
78+
help: consider introducing a named lifetime parameter
7979
|
80-
LL | fn g(mut x: &impl Foo) -> Option<&()> { x.next() }
81-
| +
80+
LL | fn g<'a>(mut x: impl Foo) -> Option<&'a ()> { x.next() }
81+
| ++++ ~~~
8282
help: ...or alternatively, to want to return an owned value
8383
|
8484
LL - fn g(mut x: impl Foo) -> Option<&()> { x.next() }
@@ -96,10 +96,10 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're
9696
|
9797
LL | fn g(mut x: impl Foo<()>) -> Option<&'static ()> { x.next() }
9898
| +++++++
99-
help: instead, you are more likely to want to change the argument to be borrowed...
99+
help: consider introducing a named lifetime parameter
100100
|
101-
LL | fn g(mut x: &impl Foo<()>) -> Option<&()> { x.next() }
102-
| +
101+
LL | fn g<'a>(mut x: impl Foo<()>) -> Option<&'a ()> { x.next() }
102+
| ++++ ~~~
103103
help: ...or alternatively, to want to return an owned value
104104
|
105105
LL - fn g(mut x: impl Foo<()>) -> Option<&()> { x.next() }

0 commit comments

Comments
 (0)