Rollup merge of #135602 - estebank:issue-135589, r=Nadrieril

Tweak output of missing lifetime on associated type

Each commit can be reviewed independently. Address parts of #135589.

---

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
```

---

On unconstrained lifetime on impl block, suggest using it if there's an implicit borrow in the self type
```
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
   |
LL | impl<'a> IntoIterator for &S {
   |      ^^ unconstrained lifetime parameter
   |
help: consider using the named lifetime here instead of an implict lifetime
   |
LL | impl<'a> IntoIterator for &'a S {
   |                            ++
```

---

Do not suggest introducing lifetime in impl assoc type

---

Previously we only showed the trait's assoc item if the trait was local, because we were looking for a small span only for the generics, which we don't have for foreign traits. We now use `def_span` for the item, so we at least provide some context, even if its span is too wide.

```
error[E0195]: lifetime parameters or bounds on type `IntoIter` do not match the trait declaration
   --> tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.rs:7:18
    |
7   |     type IntoIter<'a> = std::collections::btree_map::Values<'a, i32, T>;
    |                  ^^^^ lifetimes do not match type in trait
    |
   ::: /home/gh-estebank/rust/library/core/src/iter/traits/collect.rs:292:5
    |
292 |     type IntoIter: Iterator<Item = Self::Item>;
    |     ------------------------------------------ lifetimes in impl do not match this type in trait
```
This commit is contained in:
Matthias Krüger
2025-11-01 08:25:43 +01:00
committed by GitHub
17 changed files with 287 additions and 12 deletions
@@ -12,11 +12,12 @@
use min_specialization::check_min_specialization;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_errors::codes::*;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
use rustc_span::ErrorGuaranteed;
use rustc_span::{ErrorGuaranteed, kw};
use crate::constrained_generic_params as cgp;
use crate::errors::UnconstrainedGenericParameter;
@@ -150,6 +151,27 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained(
const_param_note2: false,
});
diag.code(E0207);
for p in &impl_generics.own_params {
if p.name == kw::UnderscoreLifetime {
let span = tcx.def_span(p.def_id);
let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) else {
continue;
};
let (span, sugg) = if &snippet == "'_" {
(span, param.name.to_string())
} else {
(span.shrink_to_hi(), format!("{} ", param.name))
};
diag.span_suggestion_verbose(
span,
"consider using the named lifetime here instead of an implicit \
lifetime",
sugg,
Applicability::MaybeIncorrect,
);
}
}
res = Err(diag.emit());
}
}
+53 -5
View File
@@ -19,7 +19,8 @@
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};
@@ -377,6 +378,7 @@ enum LifetimeBinderKind {
Function,
Closure,
ImplBlock,
ImplAssocType,
}
impl LifetimeBinderKind {
@@ -387,6 +389,7 @@ fn descr(self) -> &'static str {
PolyTrait => "bound",
WhereBound => "bound",
Item | ConstItem => "item",
ImplAssocType => "associated type",
ImplBlock => "impl block",
Function => "function",
Closure => "closure",
@@ -1874,9 +1877,13 @@ fn resolve_anonymous_lifetime(
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 +1920,47 @@ fn resolve_anonymous_lifetime(
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();
@@ -3352,7 +3400,7 @@ fn resolve_impl_item(
&generics.params,
RibKind::AssocItem,
item.id,
LifetimeBinderKind::Item,
LifetimeBinderKind::ImplAssocType,
generics.span,
|this| {
this.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| {
@@ -3178,6 +3178,9 @@ fn suggest_introducing_lifetime(
{
continue;
}
if let LifetimeBinderKind::ImplAssocType = kind {
continue;
}
if !span.can_be_used_for_suggestions()
&& suggest_note
@@ -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
@@ -0,0 +1,19 @@
struct S;
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
//~| HELP consider using the named lifetime here instead of an implicit lifetime
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>;
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
fn main() {}
@@ -0,0 +1,28 @@
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:9: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
|
LL | impl<'a> IntoIterator for &S {
| ^^ unconstrained lifetime parameter
|
help: consider using the named lifetime here instead of an implicit lifetime
|
LL | impl<'a> IntoIterator for &'a S {
| ++
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0207`.
@@ -0,0 +1,14 @@
struct S;
struct T;
impl IntoIterator for &S {
type Item = &T;
//~^ ERROR in the trait associated type
type IntoIter = std::collections::btree_map::Values<'a, i32, T>;
//~^ ERROR use of undeclared lifetime name `'a`
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
fn main() {}
@@ -0,0 +1,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
--> $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
error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/missing-lifetime-in-assoc-type-2.rs:7:57
|
LL | type IntoIter = std::collections::btree_map::Values<'a, i32, T>;
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'a` here
|
LL | impl<'a> IntoIterator for &S {
| ++++
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0261`.
@@ -0,0 +1,14 @@
struct S;
struct T;
impl IntoIterator for &S {
type Item = &T;
//~^ ERROR in the trait associated type
type IntoIter = std::collections::btree_map::Values<i32, T>;
//~^ ERROR missing lifetime specifier
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
fn main() {}
@@ -0,0 +1,25 @@
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
error[E0106]: missing lifetime specifier
--> $DIR/missing-lifetime-in-assoc-type-3.rs:7:56
|
LL | type IntoIter = std::collections::btree_map::Values<i32, T>;
| ^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
LL ~ impl<'a> IntoIterator for &S {
LL | type Item = &T;
LL |
LL ~ type IntoIter = std::collections::btree_map::Values<'a, i32, T>;
|
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0106`.
@@ -0,0 +1,14 @@
struct S;
struct T;
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 associated type `IntoIter` do not match the trait declaration
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
fn main() {}
@@ -0,0 +1,17 @@
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 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 associated type in trait
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0195`.
@@ -0,0 +1,19 @@
struct S;
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
//~| HELP consider using the named lifetime here instead of an implicit lifetime
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>;
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
fn main() {}
@@ -0,0 +1,29 @@
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-5.rs:9: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-5.rs:4:6
|
LL | impl<'a> IntoIterator for &'_ S {
| ^^ unconstrained lifetime parameter
|
help: consider using the named lifetime here instead of an implicit lifetime
|
LL - impl<'a> IntoIterator for &'_ S {
LL + impl<'a> IntoIterator for &'a S {
|
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0207`.
@@ -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
@@ -6,8 +6,9 @@ LL | type Item = IteratorChunk<T, S>;
|
help: consider introducing a named lifetime parameter
|
LL | type Item<'a> = IteratorChunk<'a, T, S>;
| ++++ +++
LL ~ impl<'a, T, S: Iterator<Item = T>> Iterator for ChunkingIterator<T, S> {
LL ~ type Item = IteratorChunk<'a, T, S>;
|
error: aborting due to 1 previous error
@@ -17,10 +17,6 @@ LL | type T = &'missing ();
|
help: consider introducing lifetime `'missing` here
|
LL | type T<'missing> = &'missing ();
| ++++++++++
help: consider introducing lifetime `'missing` here
|
LL | impl<'missing> Lt<'missing> for () {
| ++++++++++