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
```
This commit is contained in:
Esteban Küber 2025-01-16 21:02:23 +00:00
parent 2e01acc59e
commit 8ba2950fb6
9 changed files with 74 additions and 8 deletions

View file

@ -19,7 +19,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, DiagArgValue, ErrorGuaranteed, IntoDiagArg, StashKey, Suggestions,
Applicability, Diag, DiagArgValue, ErrorGuaranteed, IntoDiagArg, StashKey, Suggestions,
pluralize,
};
use rustc_hir::def::Namespace::{self, *};
use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS};
@ -1874,9 +1875,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
ty: ty.span,
});
} else {
self.r.dcx().emit_err(errors::AnonymousLifetimeNonGatReportError {
lifetime: lifetime.ident.span,
});
let mut err = self.r.dcx().create_err(
errors::AnonymousLifetimeNonGatReportError {
lifetime: lifetime.ident.span,
},
);
self.point_at_impl_lifetimes(&mut err, i, lifetime.ident.span);
err.emit();
}
} else {
self.r.dcx().emit_err(errors::ElidedAnonymousLifetimeReportError {
@ -1913,6 +1918,47 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
self.report_missing_lifetime_specifiers(vec![missing_lifetime], None);
}
fn point_at_impl_lifetimes(&mut self, err: &mut Diag<'_>, i: usize, lifetime: Span) {
let Some((rib, span)) = self.lifetime_ribs[..i]
.iter()
.rev()
.skip(1)
.filter_map(|rib| match rib.kind {
LifetimeRibKind::Generics { span, kind: LifetimeBinderKind::ImplBlock, .. } => {
Some((rib, span))
}
_ => None,
})
.next()
else {
return;
};
if !rib.bindings.is_empty() {
err.span_label(
span,
format!(
"there {} named lifetime{} specified on the impl block you could use",
if rib.bindings.len() == 1 { "is a" } else { "are" },
pluralize!(rib.bindings.len()),
),
);
if rib.bindings.len() == 1 {
err.span_suggestion_verbose(
lifetime.shrink_to_hi(),
"consider using the lifetime from the impl block",
format!("{} ", rib.bindings.keys().next().unwrap()),
Applicability::MaybeIncorrect,
);
}
} else {
err.span_label(
span,
"you could add a lifetime on the impl block, if the trait or the self type can \
have one",
);
}
}
#[instrument(level = "debug", skip(self))]
fn resolve_elided_lifetime(&mut self, anchor_id: NodeId, span: Span) {
let id = self.r.next_node_id();

View file

@ -1,6 +1,8 @@
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/assoc-type.rs:11:19
|
LL | impl MyTrait for &i32 {
| - you could add a lifetime on the impl block, if the trait or the self type can have one
LL | type Output = &i32;
| ^ this lifetime must come from the implemented type

View file

@ -3,9 +3,11 @@ struct T;
impl<'a> IntoIterator for &S {
//~^ ERROR E0207
//~| NOTE there is a named lifetime specified on the impl block you could use
//~| NOTE unconstrained lifetime parameter
type Item = &T;
//~^ ERROR in the trait associated type
//~| HELP consider using the lifetime from the impl block
//~| NOTE this lifetime must come from the implemented type
type IntoIter = std::collections::btree_map::Values<'a, i32, T>;

View file

@ -1,8 +1,16 @@
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:7:17
--> $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[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates
--> $DIR/missing-lifetime-in-assoc-type-1.rs:4:6

View file

@ -1,6 +1,8 @@
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

View file

@ -1,6 +1,8 @@
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-3.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

View file

@ -5,7 +5,7 @@ impl IntoIterator for &S {
type Item = &T;
//~^ ERROR in the trait associated type
type IntoIter<'a> = std::collections::btree_map::Values<'a, i32, T>;
//~^ ERROR lifetime parameters or bounds on type `IntoIter` do not match the trait declaration
//~^ ERROR lifetime parameters or bounds on associated type `IntoIter` do not match the trait declaration
fn into_iter(self) -> Self::IntoIter {
todo!()

View file

@ -1,14 +1,16 @@
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-4.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
error[E0195]: lifetime parameters or bounds on type `IntoIter` do not match the trait declaration
error[E0195]: lifetime parameters or bounds on associated type `IntoIter` do not match the trait declaration
--> $DIR/missing-lifetime-in-assoc-type-4.rs:7:18
|
LL | type IntoIter<'a> = std::collections::btree_map::Values<'a, i32, T>;
| ^^^^ lifetimes do not match type in trait
| ^^^^ lifetimes do not match associated type in trait
error: aborting due to 2 previous errors

View file

@ -13,6 +13,8 @@ LL | impl Iterator for Data {
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/no_lending_iterators.rs:18:17
|
LL | impl Bar for usize {
| - you could add a lifetime on the impl block, if the trait or the self type can have one
LL | type Item = &usize;
| ^ this lifetime must come from the implemented type