Skip to content

Commit 4756c5d

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 119ea42 commit 4756c5d

File tree

7 files changed

+123
-16
lines changed

7 files changed

+123
-16
lines changed

compiler/rustc_resolve/src/late.rs

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

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

+14-1
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,27 @@ trait MyTrait {
99

1010
impl MyTrait for &i32 {
1111
type Output = &i32;
12-
//~^ ERROR 11:19: 11:20: 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
12+
//~^ 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
1313
}
1414

1515
impl MyTrait for &u32 {
1616
type Output = &'_ i32;
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;
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

+6-2
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

+6-2
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

+6-2
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 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

+1-1
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)