Skip to content

Commit 0d7ef4f

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 116bd92 commit 0d7ef4f

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
@@ -10,6 +10,7 @@ use std::assert_matches::debug_assert_matches;
1010
use std::borrow::Cow;
1111
use std::collections::hash_map::Entry;
1212
use std::mem::{replace, swap, take};
13+
use std::ops::ControlFlow;
1314

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

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)