Auto merge of #145314 - estebank:issue-135589-all, r=Nadrieril

Tweak output of missing lifetime on associated type

Follow up to https://github.com/rust-lang/rust/pull/135602.

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

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

Move the previous long message to a note and use a shorter primary message:

```
error: missing lifetime in associated 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
   |
note: in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
  --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: consider using the lifetime from the impl block
   |
LL |     type Item = &'a T;
   |                  ++
```

r? `@Nadrieril`
This commit is contained in:
bors
2025-11-04 21:37:44 +00:00
21 changed files with 257 additions and 44 deletions
@@ -1097,14 +1097,14 @@ fn check_region_bounds_on_impl_item<'tcx>(
.expect("expected impl item to have generics or else we can't compare them")
.span;
let mut generics_span = None;
let mut generics_span = tcx.def_span(trait_m.def_id);
let mut bounds_span = vec![];
let mut where_span = None;
if let Some(trait_node) = tcx.hir_get_if_local(trait_m.def_id)
&& let Some(trait_generics) = trait_node.generics()
{
generics_span = Some(trait_generics.span);
generics_span = trait_generics.span;
// FIXME: we could potentially look at the impl's bounds to not point at bounds that
// *are* present in the impl.
for p in trait_generics.predicates {
+1 -1
View File
@@ -191,7 +191,7 @@ pub(crate) struct LifetimesOrBoundsMismatchOnTrait {
#[label]
pub span: Span,
#[label(hir_analysis_generics_label)]
pub generics_span: Option<Span>,
pub generics_span: Span,
#[label(hir_analysis_where_label)]
pub where_span: Option<Span>,
#[label(hir_analysis_bounds_label)]
+2 -2
View File
@@ -11,9 +11,9 @@ resolve_added_macro_use =
resolve_ancestor_only =
visibilities can only be restricted to ancestor modules
resolve_anonymous_lifetime_non_gat_report_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
resolve_anonymous_lifetime_non_gat_report_error = missing lifetime in associated type
.label = this lifetime must come from the implemented type
.note = in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
resolve_arguments_macro_use_not_allowed = arguments to `macro_use` are not allowed here
+2
View File
@@ -958,6 +958,8 @@ pub(crate) struct AnonymousLifetimeNonGatReportError {
#[primary_span]
#[label]
pub(crate) lifetime: Span,
#[note]
pub(crate) decl: MultiSpan,
}
#[derive(Subdiagnostic)]
+95 -15
View File
@@ -10,6 +10,7 @@
use std::borrow::Cow;
use std::collections::hash_map::Entry;
use std::mem::{replace, swap, take};
use std::ops::ControlFlow;
use rustc_ast::visit::{
AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, try_visit, visit_opt, walk_list,
@@ -19,21 +20,21 @@
use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagArgValue, ErrorGuaranteed, IntoDiagArg, StashKey, Suggestions,
pluralize,
Applicability, Diag, DiagArgValue, ErrorGuaranteed, IntoDiagArg, MultiSpan, StashKey,
Suggestions, pluralize,
};
use rustc_hir::def::Namespace::{self, *};
use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS};
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE, LocalDefId};
use rustc_hir::{MissingLifetimeKind, PrimTy, TraitCandidate};
use rustc_middle::middle::resolve_bound_vars::Set1;
use rustc_middle::ty::{DelegationFnSig, Visibility};
use rustc_middle::ty::{AssocTag, DelegationFnSig, Visibility};
use rustc_middle::{bug, span_bug};
use rustc_session::config::{CrateType, ResolveDocLinks};
use rustc_session::lint;
use rustc_session::parse::feature_err;
use rustc_span::source_map::{Spanned, respan};
use rustc_span::{BytePos, Ident, Span, Symbol, SyntaxContext, kw, sym};
use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, SyntaxContext, kw, sym};
use smallvec::{SmallVec, smallvec};
use thin_vec::ThinVec;
use tracing::{debug, instrument, trace};
@@ -373,11 +374,14 @@ enum LifetimeBinderKind {
FnPtrType,
PolyTrait,
WhereBound,
// Item covers foreign items, ADTs, type aliases, trait associated items and
// trait alias associated items.
Item,
ConstItem,
Function,
Closure,
ImplBlock,
// Covers only `impl` associated types.
ImplAssocType,
}
@@ -724,6 +728,9 @@ pub(crate) struct DiagMetadata<'ast> {
/// The current impl items (used to suggest).
current_impl_items: Option<&'ast [Box<AssocItem>]>,
/// The current impl items (used to suggest).
current_impl_item: Option<&'ast AssocItem>,
/// When processing impl trait
currently_processing_impl_trait: Option<(TraitRef, Ty)>,
@@ -1880,9 +1887,31 @@ fn resolve_anonymous_lifetime(
ty: ty.span,
});
} else {
let decl = if !trait_id.is_local()
&& let Some(assoc) = self.diag_metadata.current_impl_item
&& let AssocItemKind::Type(_) = assoc.kind
&& let assocs = self.r.tcx.associated_items(trait_id)
&& let Some(ident) = assoc.kind.ident()
&& let Some(assoc) = assocs.find_by_ident_and_kind(
self.r.tcx,
ident,
AssocTag::Type,
trait_id,
) {
let mut decl: MultiSpan =
self.r.tcx.def_span(assoc.def_id).into();
decl.push_span_label(
self.r.tcx.def_span(trait_id),
String::new(),
);
decl
} else {
DUMMY_SP.into()
};
let mut err = self.r.dcx().create_err(
errors::AnonymousLifetimeNonGatReportError {
lifetime: lifetime.ident.span,
decl,
},
);
self.point_at_impl_lifetimes(&mut err, i, lifetime.ident.span);
@@ -1924,17 +1953,13 @@ fn resolve_anonymous_lifetime(
}
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 {
let Some((rib, span)) =
self.lifetime_ribs[..i].iter().rev().find_map(|rib| match rib.kind {
LifetimeRibKind::Generics { span, kind: LifetimeBinderKind::ImplBlock, .. } => {
Some((rib, span))
}
_ => None,
})
.next()
else {
return;
};
@@ -1956,11 +1981,63 @@ fn point_at_impl_lifetimes(&mut self, err: &mut Diag<'_>, i: usize, lifetime: Sp
);
}
} else {
err.span_label(
span,
"you could add a lifetime on the impl block, if the trait or the self type can \
have one",
);
struct AnonRefFinder;
impl<'ast> Visitor<'ast> for AnonRefFinder {
type Result = ControlFlow<Span>;
fn visit_ty(&mut self, ty: &'ast ast::Ty) -> Self::Result {
if let ast::TyKind::Ref(None, mut_ty) = &ty.kind {
return ControlFlow::Break(mut_ty.ty.span.shrink_to_lo());
}
visit::walk_ty(self, ty)
}
fn visit_lifetime(
&mut self,
lt: &'ast ast::Lifetime,
_cx: visit::LifetimeCtxt,
) -> Self::Result {
if lt.ident.name == kw::UnderscoreLifetime {
return ControlFlow::Break(lt.ident.span);
}
visit::walk_lifetime(self, lt)
}
}
if let Some(ty) = &self.diag_metadata.current_self_type
&& let ControlFlow::Break(sp) = AnonRefFinder.visit_ty(ty)
{
err.multipart_suggestion_verbose(
"add a lifetime to the impl block and use it in the self type and associated \
type",
vec![
(span, "<'a>".to_string()),
(sp, "'a ".to_string()),
(lifetime.shrink_to_hi(), "'a ".to_string()),
],
Applicability::MaybeIncorrect,
);
} else if let Some(item) = &self.diag_metadata.current_item
&& let ItemKind::Impl(impl_) = &item.kind
&& let Some(of_trait) = &impl_.of_trait
&& let ControlFlow::Break(sp) = AnonRefFinder.visit_trait_ref(&of_trait.trait_ref)
{
err.multipart_suggestion_verbose(
"add a lifetime to the impl block and use it in the trait and associated type",
vec![
(span, "<'a>".to_string()),
(sp, "'a".to_string()),
(lifetime.shrink_to_hi(), "'a ".to_string()),
],
Applicability::MaybeIncorrect,
);
} else {
err.span_label(
span,
"you could add a lifetime on the impl block, if the trait or the self type \
could have one",
);
}
}
}
@@ -3304,6 +3381,8 @@ fn resolve_impl_item(
) {
use crate::ResolutionError::*;
self.resolve_doc_links(&item.attrs, MaybeExported::ImplItem(trait_id.ok_or(&item.vis)));
let prev = self.diag_metadata.current_impl_item.take();
self.diag_metadata.current_impl_item = Some(&item);
match &item.kind {
AssocItemKind::Const(box ast::ConstItem {
ident,
@@ -3452,6 +3531,7 @@ fn resolve_impl_item(
panic!("unexpanded macro in resolve!")
}
}
self.diag_metadata.current_impl_item = prev;
}
fn check_trait_item<F>(
@@ -9,7 +9,7 @@ trait MyTrait {
impl MyTrait for &i32 {
type Output = &i32;
//~^ 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
//~^ ERROR missing lifetime in associated type
}
impl MyTrait for &u32 {
@@ -17,6 +17,19 @@ impl MyTrait for &u32 {
//~^ ERROR `'_` cannot be used here
}
impl<'a> MyTrait for &f64 {
type Output = &f64;
//~^ ERROR missing lifetime in associated type
}
trait OtherTrait<'a> {
type Output;
}
impl OtherTrait<'_> for f64 {
type Output = &f64;
//~^ ERROR missing lifetime in associated type
}
// This is what you have to do:
impl<'a> MyTrait for &'a f32 {
type Output = &'a f32;
@@ -1,10 +1,15 @@
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
error: missing lifetime in associated 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
|
= note: in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to 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> MyTrait for &'a i32 {
LL ~ type Output = &'a i32;
|
error[E0637]: `'_` cannot be used here
--> $DIR/assoc-type.rs:16:20
@@ -12,6 +17,33 @@ error[E0637]: `'_` cannot be used here
LL | type Output = &'_ i32;
| ^^ `'_` is a reserved lifetime name
error: aborting due to 2 previous errors
error: missing lifetime in associated type
--> $DIR/assoc-type.rs:21:19
|
LL | impl<'a> MyTrait for &f64 {
| ---- there is a named lifetime specified on the impl block you could use
LL | type Output = &f64;
| ^ this lifetime must come from the implemented type
|
= note: in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
help: consider using the lifetime from the impl block
|
LL | type Output = &'a f64;
| ++
error: missing lifetime in associated type
--> $DIR/assoc-type.rs:29:19
|
LL | type Output = &f64;
| ^ this lifetime must come from the implemented type
|
= note: in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
help: add a lifetime to the impl block and use it in the trait and associated type
|
LL ~ impl<'a> OtherTrait<'a> for f64 {
LL ~ type Output = &'a f64;
|
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0637`.
@@ -7,9 +7,10 @@ impl<'a> IntoIterator for &S {
//~| 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
//~^ ERROR missing lifetime in associated type
//~| HELP consider using the lifetime from the impl block
//~| NOTE this lifetime must come from the implemented type
//~| NOTE in the trait the associated type is declared without lifetime parameters
type IntoIter = std::collections::btree_map::Values<'a, i32, T>;
fn into_iter(self) -> Self::IntoIter {
@@ -1,4 +1,4 @@
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
error: missing lifetime in associated type
--> $DIR/missing-lifetime-in-assoc-type-1.rs:9:17
|
LL | impl<'a> IntoIterator for &S {
@@ -7,6 +7,8 @@ LL | impl<'a> IntoIterator for &S {
LL | type Item = &T;
| ^ this lifetime must come from the implemented type
|
note: in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: consider using the lifetime from the impl block
|
LL | type Item = &'a T;
@@ -3,7 +3,7 @@
impl IntoIterator for &S {
type Item = &T;
//~^ ERROR in the trait associated type
//~^ ERROR missing lifetime in associated type
type IntoIter = std::collections::btree_map::Values<'a, i32, T>;
//~^ ERROR use of undeclared lifetime name `'a`
@@ -1,10 +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
error: missing lifetime in associated 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
|
note: in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
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;
|
error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/missing-lifetime-in-assoc-type-2.rs:7:57
@@ -3,7 +3,7 @@
impl IntoIterator for &S {
type Item = &T;
//~^ ERROR in the trait associated type
//~^ ERROR missing lifetime in associated type
type IntoIter = std::collections::btree_map::Values<i32, T>;
//~^ ERROR missing lifetime specifier
@@ -1,10 +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
error: missing lifetime in associated 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
|
note: in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
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;
|
error[E0106]: missing lifetime specifier
--> $DIR/missing-lifetime-in-assoc-type-3.rs:7:56
@@ -3,7 +3,7 @@
impl IntoIterator for &S {
type Item = &T;
//~^ ERROR in the trait associated type
//~^ ERROR missing lifetime in 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
@@ -1,16 +1,26 @@
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
error: missing lifetime in associated 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
|
note: in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
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;
|
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
|
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
|
= note: lifetimes in impl do not match this associated type in trait
error: aborting due to 2 previous errors
@@ -7,9 +7,10 @@ impl<'a> IntoIterator for &'_ S {
//~| 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
//~^ ERROR missing lifetime in associated type
//~| HELP consider using the lifetime from the impl block
//~| NOTE this lifetime must come from the implemented type
//~| NOTE in the trait the associated type is declared without lifetime parameters
type IntoIter = std::collections::btree_map::Values<'a, i32, T>;
fn into_iter(self) -> Self::IntoIter {
@@ -1,4 +1,4 @@
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
error: missing lifetime in associated type
--> $DIR/missing-lifetime-in-assoc-type-5.rs:9:17
|
LL | impl<'a> IntoIterator for &'_ S {
@@ -7,6 +7,8 @@ LL | impl<'a> IntoIterator for &'_ S {
LL | type Item = &T;
| ^ this lifetime must come from the implemented type
|
note: in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: consider using the lifetime from the impl block
|
LL | type Item = &'a T;
@@ -0,0 +1,26 @@
//~ NOTE in the trait the associated type is declared without lifetime parameters
struct S;
struct T;
trait Trait {
type Item;
type IntoIter;
fn into_iter(self) -> Self::IntoIter;
}
impl<'a> Trait 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 missing lifetime in 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,30 @@
error: missing lifetime in associated type
--> $DIR/missing-lifetime-in-assoc-type-6.rs:16:17
|
LL | impl<'a> Trait 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
|
= note: in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to 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-6.rs:11:6
|
LL | impl<'a> Trait for &'_ S {
| ^^ unconstrained lifetime parameter
|
help: consider using the named lifetime here instead of an implicit lifetime
|
LL - impl<'a> Trait for &'_ S {
LL + impl<'a> Trait for &'a S {
|
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0207`.
+1 -1
View File
@@ -16,7 +16,7 @@ trait Bar {
impl Bar for usize {
type Item = &usize;
//~^ 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
//~^ ERROR missing lifetime in associated type
fn poke(&mut self, item: Self::Item) {
self += *item;
@@ -10,13 +10,15 @@ note: you can't create an `Iterator` that borrows each `Item` from itself, but y
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
error: missing lifetime in associated 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
| - you could add a lifetime on the impl block, if the trait or the self type could have one
LL | type Item = &usize;
| ^ this lifetime must come from the implemented type
|
= note: in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
error[E0195]: lifetime parameters or bounds on associated type `Item` do not match the trait declaration
--> $DIR/no_lending_iterators.rs:27:14