Skip to content

Commit 3155687

Browse files
committed
Detect case of missing lifetime in assoc type
When an associated type is missing a lifetime, point at its enclosing `impl`, whether it has or doesn't have lifetimes defined. If it does have a lifetime, suggest using it. ``` 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-1.rs:8:17 | LL | impl<'a> IntoIterator for &S { | ---- there is a named lifetime specified on the impl block you could use ... LL | type Item = &T; | ^ this lifetime must come from the implemented type | help: consider using the lifetime from the impl block | LL | type Item = &'a T; | ++ ``` ``` 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 | impl IntoIterator for &S { | - you could add a lifetime on the impl block, if the trait or the self type can have one LL | type Item = &T; | ^ this lifetime must come from the implemented type ```
1 parent 364475b commit 3155687

9 files changed

+74
-8
lines changed

compiler/rustc_resolve/src/late.rs

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
2020
use rustc_data_structures::unord::{UnordMap, UnordSet};
2121
use rustc_errors::codes::*;
2222
use rustc_errors::{
23-
Applicability, DiagArgValue, ErrorGuaranteed, IntoDiagArg, StashKey, Suggestions,
23+
Applicability, Diag, DiagArgValue, ErrorGuaranteed, IntoDiagArg, StashKey, Suggestions,
24+
pluralize,
2425
};
2526
use rustc_hir::def::Namespace::{self, *};
2627
use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS};
@@ -1887,9 +1888,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
18871888
ty: ty.span,
18881889
});
18891890
} else {
1890-
self.r.dcx().emit_err(errors::AnonymousLifetimeNonGatReportError {
1891-
lifetime: lifetime.ident.span,
1892-
});
1891+
let mut err = self.r.dcx().create_err(
1892+
errors::AnonymousLifetimeNonGatReportError {
1893+
lifetime: lifetime.ident.span,
1894+
},
1895+
);
1896+
self.point_at_impl_lifetimes(&mut err, i, lifetime.ident.span);
1897+
err.emit();
18931898
}
18941899
} else {
18951900
self.r.dcx().emit_err(errors::ElidedAnonymousLifetimeReportError {
@@ -1926,6 +1931,47 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
19261931
self.report_missing_lifetime_specifiers(vec![missing_lifetime], None);
19271932
}
19281933

1934+
fn point_at_impl_lifetimes(&mut self, err: &mut Diag<'_>, i: usize, lifetime: Span) {
1935+
let Some((rib, span)) = self.lifetime_ribs[..i]
1936+
.iter()
1937+
.rev()
1938+
.skip(1)
1939+
.filter_map(|rib| match rib.kind {
1940+
LifetimeRibKind::Generics { span, kind: LifetimeBinderKind::ImplBlock, .. } => {
1941+
Some((rib, span))
1942+
}
1943+
_ => None,
1944+
})
1945+
.next()
1946+
else {
1947+
return;
1948+
};
1949+
if !rib.bindings.is_empty() {
1950+
err.span_label(
1951+
span,
1952+
format!(
1953+
"there {} named lifetime{} specified on the impl block you could use",
1954+
if rib.bindings.len() == 1 { "is a" } else { "are" },
1955+
pluralize!(rib.bindings.len()),
1956+
),
1957+
);
1958+
if rib.bindings.len() == 1 {
1959+
err.span_suggestion_verbose(
1960+
lifetime.shrink_to_hi(),
1961+
"consider using the lifetime from the impl block",
1962+
format!("{} ", rib.bindings.keys().next().unwrap()),
1963+
Applicability::MaybeIncorrect,
1964+
);
1965+
}
1966+
} else {
1967+
err.span_label(
1968+
span,
1969+
"you could add a lifetime on the impl block, if the trait or the self type can \
1970+
have one",
1971+
);
1972+
}
1973+
}
1974+
19291975
#[instrument(level = "debug", skip(self))]
19301976
fn resolve_elided_lifetime(&mut self, anchor_id: NodeId, span: Span) {
19311977
let id = self.r.next_node_id();

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
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
46
LL | type Output = &i32;
57
| ^ this lifetime must come from the implemented type
68

tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ struct T;
33

44
impl<'a> IntoIterator for &S {
55
//~^ ERROR E0207
6+
//~| NOTE there is a named lifetime specified on the impl block you could use
67
//~| NOTE unconstrained lifetime parameter
78
type Item = &T;
89
//~^ ERROR in the trait associated type
10+
//~| HELP consider using the lifetime from the impl block
911
//~| NOTE this lifetime must come from the implemented type
1012
type IntoIter = std::collections::btree_map::Values<'a, i32, T>;
1113

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
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
2-
--> $DIR/missing-lifetime-in-assoc-type-1.rs:7:17
2+
--> $DIR/missing-lifetime-in-assoc-type-1.rs:8:17
33
|
4+
LL | impl<'a> IntoIterator for &S {
5+
| ---- there is a named lifetime specified on the impl block you could use
6+
...
47
LL | type Item = &T;
58
| ^ this lifetime must come from the implemented type
9+
|
10+
help: consider using the lifetime from the impl block
11+
|
12+
LL | type Item = &'a T;
13+
| ++
614

715
error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates
816
--> $DIR/missing-lifetime-in-assoc-type-1.rs:4:6

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
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
46
LL | type Item = &T;
57
| ^ this lifetime must come from the implemented type
68

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
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
46
LL | type Item = &T;
57
| ^ this lifetime must come from the implemented type
68

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ impl IntoIterator for &S {
55
type Item = &T;
66
//~^ ERROR in the trait associated type
77
type IntoIter<'a> = std::collections::btree_map::Values<'a, i32, T>;
8-
//~^ ERROR lifetime parameters or bounds on type `IntoIter` do not match the trait declaration
8+
//~^ ERROR lifetime parameters or bounds on associated type `IntoIter` do not match the trait declaration
99

1010
fn into_iter(self) -> Self::IntoIter {
1111
todo!()

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
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
46
LL | type Item = &T;
57
| ^ this lifetime must come from the implemented type
68

7-
error[E0195]: lifetime parameters or bounds on type `IntoIter` do not match the trait declaration
9+
error[E0195]: lifetime parameters or bounds on associated type `IntoIter` do not match the trait declaration
810
--> $DIR/missing-lifetime-in-assoc-type-4.rs:7:18
911
|
1012
LL | type IntoIter<'a> = std::collections::btree_map::Values<'a, i32, T>;
11-
| ^^^^ lifetimes do not match type in trait
13+
| ^^^^ lifetimes do not match associated type in trait
1214

1315
error: aborting due to 2 previous errors
1416

tests/ui/lifetimes/no_lending_iterators.stderr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ LL | impl Iterator for Data {
1313
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
1414
--> $DIR/no_lending_iterators.rs:18:17
1515
|
16+
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
1618
LL | type Item = &usize;
1719
| ^ this lifetime must come from the implemented type
1820

0 commit comments

Comments
 (0)