Skip to content

Commit 41159f6

Browse files
committed
Look at the current impl before suggesting adding a lifetime
Given an associated item that needs a named lifetime, look at the enclosing `impl` item for one. If there is none, look at the self type and the implemented trait to see if either of those has an anonimous lifetime. If so, suggest adding a named lifetime. ``` error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type --> $DIR/missing-lifetime-in-assoc-type-2.rs:5:17 | LL | type Item = &T; | ^ this lifetime must come from the implemented type | help: add a lifetime to the impl block and use it in the self type and associated type | LL ~ impl<'a> IntoIterator for &'a S { LL ~ type Item = &'a T; | ```
1 parent 4b394b2 commit 41159f6

File tree

7 files changed

+122
-15
lines changed

7 files changed

+122
-15
lines changed

compiler/rustc_resolve/src/late.rs

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::borrow::Cow;
1111
use std::collections::BTreeSet;
1212
use std::collections::hash_map::Entry;
1313
use std::mem::{replace, swap, take};
14+
use std::ops::ControlFlow;
1415

1516
use rustc_ast::visit::{
1617
AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, try_visit, visit_opt, walk_list,
@@ -1966,11 +1967,63 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
19661967
);
19671968
}
19681969
} else {
1969-
err.span_label(
1970-
span,
1971-
"you could add a lifetime on the impl block, if the trait or the self type can \
1972-
have one",
1973-
);
1970+
struct AnonRefFinder;
1971+
impl<'ast> Visitor<'ast> for AnonRefFinder {
1972+
type Result = ControlFlow<Span>;
1973+
1974+
fn visit_ty(&mut self, ty: &'ast ast::Ty) -> Self::Result {
1975+
if let ast::TyKind::Ref(None, mut_ty) = &ty.kind {
1976+
return ControlFlow::Break(mut_ty.ty.span.shrink_to_lo());
1977+
}
1978+
visit::walk_ty(self, ty)
1979+
}
1980+
1981+
fn visit_lifetime(
1982+
&mut self,
1983+
lt: &'ast ast::Lifetime,
1984+
_cx: visit::LifetimeCtxt,
1985+
) -> Self::Result {
1986+
if lt.ident.name == kw::UnderscoreLifetime {
1987+
return ControlFlow::Break(lt.ident.span);
1988+
}
1989+
visit::walk_lifetime(self, lt)
1990+
}
1991+
}
1992+
1993+
if let Some(ty) = &self.diag_metadata.current_self_type
1994+
&& let ControlFlow::Break(sp) = AnonRefFinder.visit_ty(ty)
1995+
{
1996+
err.multipart_suggestion_verbose(
1997+
"add a lifetime to the impl block and use it in the self type and associated \
1998+
type",
1999+
vec![
2000+
(span, "<'a>".to_string()),
2001+
(sp, "'a ".to_string()),
2002+
(lifetime.shrink_to_hi(), "'a ".to_string()),
2003+
],
2004+
Applicability::MaybeIncorrect,
2005+
);
2006+
} else if let Some(item) = &self.diag_metadata.current_item
2007+
&& let ItemKind::Impl(impl_) = &item.kind
2008+
&& let Some(of_trait) = &impl_.of_trait
2009+
&& let ControlFlow::Break(sp) = AnonRefFinder.visit_trait_ref(of_trait)
2010+
{
2011+
err.multipart_suggestion_verbose(
2012+
"add a lifetime to the impl block and use it in the trait and associated type",
2013+
vec![
2014+
(span, "<'a>".to_string()),
2015+
(sp, "'a".to_string()),
2016+
(lifetime.shrink_to_hi(), "'a ".to_string()),
2017+
],
2018+
Applicability::MaybeIncorrect,
2019+
);
2020+
} else {
2021+
err.span_label(
2022+
span,
2023+
"you could add a lifetime on the impl block, if the trait or the self type \
2024+
could have one",
2025+
);
2026+
}
19742027
}
19752028
}
19762029

tests/ui/impl-header-lifetime-elision/assoc-type.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@ impl MyTrait for &u32 {
1717
//~^ ERROR `'_` cannot be used here
1818
}
1919

20+
impl<'a> MyTrait for &f64 {
21+
type Output = &f64;
22+
//~^ ERROR in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
23+
}
24+
25+
trait OtherTrait<'a> {
26+
type Output;
27+
}
28+
impl OtherTrait<'_> for f64 {
29+
type Output = &f64;
30+
//~^ ERROR in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
31+
}
32+
2033
// This is what you have to do:
2134
impl<'a> MyTrait for &'a f32 {
2235
type Output = &'a f32;
Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,46 @@
11
error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
22
--> $DIR/assoc-type.rs:11:19
33
|
4-
LL | impl MyTrait for &i32 {
5-
| - you could add a lifetime on the impl block, if the trait or the self type can have one
64
LL | type Output = &i32;
75
| ^ this lifetime must come from the implemented type
6+
|
7+
help: add a lifetime to the impl block and use it in the self type and associated type
8+
|
9+
LL ~ impl<'a> MyTrait for &'a i32 {
10+
LL ~ type Output = &'a i32;
11+
|
812

913
error[E0637]: `'_` cannot be used here
1014
--> $DIR/assoc-type.rs:16:20
1115
|
1216
LL | type Output = &'_ i32;
1317
| ^^ `'_` is a reserved lifetime name
1418

15-
error: aborting due to 2 previous errors
19+
error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
20+
--> $DIR/assoc-type.rs:21:19
21+
|
22+
LL | impl<'a> MyTrait for &f64 {
23+
| ---- there is a named lifetime specified on the impl block you could use
24+
LL | type Output = &f64;
25+
| ^ this lifetime must come from the implemented type
26+
|
27+
help: consider using the lifetime from the impl block
28+
|
29+
LL | type Output = &'a f64;
30+
| ++
31+
32+
error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
33+
--> $DIR/assoc-type.rs:29:19
34+
|
35+
LL | type Output = &f64;
36+
| ^ this lifetime must come from the implemented type
37+
|
38+
help: add a lifetime to the impl block and use it in the trait and associated type
39+
|
40+
LL ~ impl<'a> OtherTrait<'a> for f64 {
41+
LL ~ type Output = &'a f64;
42+
|
43+
44+
error: aborting due to 4 previous errors
1645

1746
For more information about this error, try `rustc --explain E0637`.

tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
22
--> $DIR/missing-lifetime-in-assoc-type-2.rs:5:17
33
|
4-
LL | impl IntoIterator for &S {
5-
| - you could add a lifetime on the impl block, if the trait or the self type can have one
64
LL | type Item = &T;
75
| ^ this lifetime must come from the implemented type
6+
|
7+
help: add a lifetime to the impl block and use it in the self type and associated type
8+
|
9+
LL ~ impl<'a> IntoIterator for &'a S {
10+
LL ~ type Item = &'a T;
11+
|
812

913
error[E0261]: use of undeclared lifetime name `'a`
1014
--> $DIR/missing-lifetime-in-assoc-type-2.rs:7:57

tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
22
--> $DIR/missing-lifetime-in-assoc-type-3.rs:5:17
33
|
4-
LL | impl IntoIterator for &S {
5-
| - you could add a lifetime on the impl block, if the trait or the self type can have one
64
LL | type Item = &T;
75
| ^ this lifetime must come from the implemented type
6+
|
7+
help: add a lifetime to the impl block and use it in the self type and associated type
8+
|
9+
LL ~ impl<'a> IntoIterator for &'a S {
10+
LL ~ type Item = &'a T;
11+
|
812

913
error[E0106]: missing lifetime specifier
1014
--> $DIR/missing-lifetime-in-assoc-type-3.rs:7:56

tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.stderr

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
22
--> $DIR/missing-lifetime-in-assoc-type-4.rs:5:17
33
|
4-
LL | impl IntoIterator for &S {
5-
| - you could add a lifetime on the impl block, if the trait or the self type can have one
64
LL | type Item = &T;
75
| ^ this lifetime must come from the implemented type
6+
|
7+
help: add a lifetime to the impl block and use it in the self type and associated type
8+
|
9+
LL ~ impl<'a> IntoIterator for &'a S {
10+
LL ~ type Item = &'a T;
11+
|
812

913
error[E0195]: lifetime parameters or bounds on associated type `IntoIter` do not match the trait declaration
1014
--> $DIR/missing-lifetime-in-assoc-type-4.rs:7:18

tests/ui/lifetimes/no_lending_iterators.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ error: in the trait associated type is declared without lifetime parameters, so
1414
--> $DIR/no_lending_iterators.rs:18:17
1515
|
1616
LL | impl Bar for usize {
17-
| - you could add a lifetime on the impl block, if the trait or the self type can have one
17+
| - you could add a lifetime on the impl block, if the trait or the self type could have one
1818
LL | type Item = &usize;
1919
| ^ this lifetime must come from the implemented type
2020

0 commit comments

Comments
 (0)