Skip to content

Tweak output of missing lifetime on associated type #135602

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
24 changes: 23 additions & 1 deletion compiler/rustc_hir_analysis/src/impl_wf_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ use std::assert_matches::debug_assert_matches;

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;
Expand Down Expand Up @@ -158,6 +159,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 implict \
lifetime",
sugg,
Applicability::MaybeIncorrect,
);
}
}
res = Err(diag.emit());
}
}
Expand Down
58 changes: 53 additions & 5 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ use rustc_ast::*;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
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};
Expand Down Expand Up @@ -359,6 +360,7 @@ enum LifetimeBinderKind {
Function,
Closure,
ImplBlock,
ImplAssocType,
Copy link
Member

@Nadrieril Nadrieril Mar 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a comment to the Item variant explaining which items it covers? Is it only trait assoc types now? If so better rename it.

}

impl LifetimeBinderKind {
Expand All @@ -369,6 +371,7 @@ impl LifetimeBinderKind {
PolyTrait => "bound",
WhereBound => "bound",
Item | ConstItem => "item",
ImplAssocType => "associated type",
ImplBlock => "impl block",
Function => "function",
Closure => "closure",
Expand Down Expand Up @@ -1870,9 +1873,13 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
ty: ty.span,
});
} else {
self.r.dcx().emit_err(errors::AnonymousLivetimeNonGatReportError {
lifetime: lifetime.ident.span,
});
let mut err = self.r.dcx().create_err(
errors::AnonymousLivetimeNonGatReportError {
lifetime: lifetime.ident.span,
},
);
self.point_at_impl_lifetimes(&mut err, i, lifetime.ident.span);
err.emit();
}
} else {
self.r.dcx().emit_err(errors::ElidedAnonymousLivetimeReportError {
Expand Down Expand Up @@ -1909,6 +1916,47 @@ impl<'a, 'ast, 'ra: 'ast, '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)
Copy link
Member

@Nadrieril Nadrieril Mar 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the skip? Could you add a comment explaining it?

.filter_map(|rib| match rib.kind {
LifetimeRibKind::Generics { span, kind: LifetimeBinderKind::ImplBlock, .. } => {
Some((rib, span))
}
_ => None,
})
.next()
Comment on lines +1949 to +1955
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
.filter_map(|rib| match rib.kind {
LifetimeRibKind::Generics { span, kind: LifetimeBinderKind::ImplBlock, .. } => {
Some((rib, span))
}
_ => None,
})
.next()
.find_map(|rib| match rib.kind {
LifetimeRibKind::Generics { span, kind: LifetimeBinderKind::ImplBlock, .. } => {
Some((rib, span))
}
_ => None,
})

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();
Expand Down Expand Up @@ -3349,7 +3397,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
LifetimeRibKind::Generics {
binder: item.id,
span: generics.span,
kind: LifetimeBinderKind::Item,
kind: LifetimeBinderKind::ImplAssocType,
},
|this| {
this.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| {
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2976,6 +2976,9 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
{
continue;
}
if let LifetimeBinderKind::ImplAssocType = kind {
continue;
}

if !span.can_be_used_for_suggestions()
&& suggest_note
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/impl-header-lifetime-elision/assoc-type.stderr
Original file line number Diff line number Diff line change
@@ -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

Expand Down
19 changes: 19 additions & 0 deletions tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs
Original file line number Diff line number Diff line change
@@ -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 implict 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() {}
28 changes: 28 additions & 0 deletions tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr
Original file line number Diff line number Diff line change
@@ -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 implict lifetime
|
LL | impl<'a> IntoIterator for &'a S {
| ++
Comment on lines +17 to +26
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0207`.
14 changes: 14 additions & 0 deletions tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.rs
Original file line number Diff line number Diff line change
@@ -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() {}
20 changes: 20 additions & 0 deletions tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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 | impl IntoIterator for &S {
| - help: consider introducing lifetime `'a` here: `<'a>`
...
LL | type IntoIter = std::collections::btree_map::Values<'a, i32, T>;
| ^^ undeclared lifetime
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0261`.
14 changes: 14 additions & 0 deletions tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.rs
Original file line number Diff line number Diff line change
@@ -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() {}
25 changes: 25 additions & 0 deletions tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr
Original file line number Diff line number Diff line change
@@ -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`.
14 changes: 14 additions & 0 deletions tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.rs
Original file line number Diff line number Diff line change
@@ -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 type `IntoIter` do not match the trait declaration

fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
fn main() {}
17 changes: 17 additions & 0 deletions tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.stderr
Original file line number Diff line number Diff line change
@@ -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 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

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0195`.
19 changes: 19 additions & 0 deletions tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.rs
Original file line number Diff line number Diff line change
@@ -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 implict 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() {}
28 changes: 28 additions & 0 deletions tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr
Original file line number Diff line number Diff line change
@@ -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-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 implict 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`.
2 changes: 2 additions & 0 deletions tests/ui/lifetimes/no_lending_iterators.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
5 changes: 3 additions & 2 deletions tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading