Skip to content

Commit d1de8c2

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 74d7b13 commit d1de8c2

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
@@ -2789,6 +2789,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
27892789
} else if let TyKind::CVarArgs = param.ty.kind {
27902790
// Don't suggest `&...` for ffi fn with varargs
27912791
None
2792+
} else if let TyKind::ImplTrait(..) = &param.ty.kind {
2793+
// We handle these in the next `else if` branch.
2794+
None
27922795
} else {
27932796
Some((param.ty.span.shrink_to_lo(), "&".to_string()))
27942797
}
@@ -2811,6 +2814,64 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
28112814
Applicability::MaybeIncorrect,
28122815
);
28132816
"...or alternatively,"
2817+
} else if let Some((kind, _span)) =
2818+
self.diagnostic_metadata.current_function
2819+
&& let FnKind::Fn(_, _, sig, _, _, _) = kind
2820+
&& let ast::FnRetTy::Ty(ret_ty) = &sig.decl.output
2821+
&& !sig.decl.inputs.is_empty()
2822+
&& let arg_refs = sig
2823+
.decl
2824+
.inputs
2825+
.iter()
2826+
.filter_map(|param| match &param.ty.kind {
2827+
TyKind::ImplTrait(_, bounds) => Some(bounds),
2828+
_ => None,
2829+
})
2830+
.flat_map(|bounds| bounds.into_iter())
2831+
.collect::<Vec<_>>()
2832+
&& !arg_refs.is_empty()
2833+
{
2834+
// We have a situation like
2835+
// fn g(mut x: impl Iterator<Item = &()>) -> Option<&()>
2836+
// So we look at every ref in the trait bound. If there's any, we
2837+
// suggest
2838+
// fn g<'a>(mut x: impl Iterator<Item = &'a ()>) -> Option<&'a ()>
2839+
let mut lt_finder = LifetimeFinder {
2840+
lifetime: lt.span,
2841+
found: None,
2842+
seen: vec![],
2843+
};
2844+
for bound in arg_refs {
2845+
if let ast::GenericBound::Trait(trait_ref, _) = bound {
2846+
lt_finder.visit_trait_ref(&trait_ref.trait_ref);
2847+
}
2848+
}
2849+
lt_finder.visit_ty(ret_ty);
2850+
let spans_suggs: Vec<_> = lt_finder.seen.iter().filter_map(|ty| {
2851+
match &ty.kind {
2852+
TyKind::Ref(_, mut_ty) => {
2853+
let span = ty.span.with_hi(mut_ty.ty.span.lo());
2854+
Some((span, "&'a ".to_string()))
2855+
}
2856+
_ => None
2857+
}
2858+
}).collect();
2859+
self.suggest_introducing_lifetime(
2860+
err,
2861+
None,
2862+
|err, higher_ranked, span, message, intro_sugg| {
2863+
info!(?span, ?message, ?intro_sugg);
2864+
err.multipart_suggestion_verbose(
2865+
message,
2866+
std::iter::once((span, intro_sugg))
2867+
.chain(spans_suggs.iter().cloned())
2868+
.collect(),
2869+
Applicability::MaybeIncorrect,
2870+
);
2871+
higher_ranked
2872+
},
2873+
);
2874+
"...or alternatively,"
28142875
} else {
28152876
"instead, you are more likely"
28162877
};
@@ -2820,7 +2881,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
28202881
&& let FnKind::Fn(_, _, sig, _, _, _) = kind
28212882
&& let ast::FnRetTy::Ty(ty) = &sig.decl.output
28222883
{
2823-
let mut lt_finder = LifetimeFinder { lifetime: lt.span, found: None };
2884+
let mut lt_finder = LifetimeFinder {
2885+
lifetime: lt.span,
2886+
found: None,
2887+
seen: vec![],
2888+
};
28242889
lt_finder.visit_ty(&ty);
28252890

28262891
if let Some(ty) = lt_finder.found {
@@ -2956,14 +3021,16 @@ pub(super) fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: I
29563021
struct LifetimeFinder<'ast> {
29573022
lifetime: Span,
29583023
found: Option<&'ast Ty>,
3024+
seen: Vec<&'ast Ty>,
29593025
}
29603026

29613027
impl<'ast> Visitor<'ast> for LifetimeFinder<'ast> {
29623028
fn visit_ty(&mut self, t: &'ast Ty) {
2963-
if t.span.lo() == self.lifetime.lo()
2964-
&& let TyKind::Ref(_, mut_ty) = &t.kind
2965-
{
2966-
self.found = Some(&mut_ty.ty);
3029+
if let TyKind::Ref(_, mut_ty) = &t.kind {
3030+
self.seen.push(t);
3031+
if t.span.lo() == self.lifetime.lo() {
3032+
self.found = Some(&mut_ty.ty);
3033+
}
29673034
}
29683035
walk_ty(self, t)
29693036
}

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)