diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index cbdc501291bc8..5b8eef0965759 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -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; @@ -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()); } } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index e52cbeb733ac0..12c5f7fbc1113 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -20,7 +20,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}; @@ -377,6 +378,7 @@ enum LifetimeBinderKind { Function, Closure, ImplBlock, + ImplAssocType, } impl LifetimeBinderKind { @@ -387,6 +389,7 @@ impl LifetimeBinderKind { PolyTrait => "bound", WhereBound => "bound", Item | ConstItem => "item", + ImplAssocType => "associated type", ImplBlock => "impl block", Function => "function", Closure => "closure", @@ -1887,9 +1890,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 { @@ -1926,6 +1933,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(); @@ -3360,7 +3408,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { &generics.params, RibKind::AssocItem, item.id, - LifetimeBinderKind::Item, + LifetimeBinderKind::ImplAssocType, generics.span, |this| { this.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index aca251da71d39..f1fde99f1fc12 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3154,6 +3154,9 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { { continue; } + if let LifetimeBinderKind::ImplAssocType = kind { + continue; + } if !span.can_be_used_for_suggestions() && suggest_note diff --git a/tests/ui/impl-header-lifetime-elision/assoc-type.stderr b/tests/ui/impl-header-lifetime-elision/assoc-type.stderr index e650eeca48ad9..72c066426bd99 100644 --- a/tests/ui/impl-header-lifetime-elision/assoc-type.stderr +++ b/tests/ui/impl-header-lifetime-elision/assoc-type.stderr @@ -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 diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs new file mode 100644 index 0000000000000..1ae381ef7bc02 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs @@ -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() {} diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr new file mode 100644 index 0000000000000..bf26ce4a63027 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr @@ -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 { + | ++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0207`. diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.rs new file mode 100644 index 0000000000000..dd720f075ac46 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.rs @@ -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() {} diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr new file mode 100644 index 0000000000000..7a0246eaac8fd --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr @@ -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`. diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.rs new file mode 100644 index 0000000000000..60d1f0f8fe571 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.rs @@ -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; + //~^ ERROR missing lifetime specifier + + fn into_iter(self) -> Self::IntoIter { + todo!() + } +} +fn main() {} diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr new file mode 100644 index 0000000000000..408d5bb40664d --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr @@ -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; + | ^ 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`. diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.rs new file mode 100644 index 0000000000000..0c99e8874c354 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.rs @@ -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() {} diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.stderr new file mode 100644 index 0000000000000..ebe051509aad2 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.stderr @@ -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`. diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.rs new file mode 100644 index 0000000000000..ff64d6b900949 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.rs @@ -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() {} diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr new file mode 100644 index 0000000000000..9c960c67e2502 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr @@ -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 implict 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`. diff --git a/tests/ui/lifetimes/no_lending_iterators.stderr b/tests/ui/lifetimes/no_lending_iterators.stderr index 340ef93588515..cadba149c234d 100644 --- a/tests/ui/lifetimes/no_lending_iterators.stderr +++ b/tests/ui/lifetimes/no_lending_iterators.stderr @@ -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 diff --git a/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr b/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr index 5020395eb6aea..dc21c2e3cf9bf 100644 --- a/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr +++ b/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr @@ -6,8 +6,9 @@ LL | type Item = IteratorChunk; | help: consider introducing a named lifetime parameter | -LL | type Item<'a> = IteratorChunk<'a, T, S>; - | ++++ +++ +LL ~ impl<'a, T, S: Iterator> Iterator for ChunkingIterator { +LL ~ type Item = IteratorChunk<'a, T, S>; + | error: aborting due to 1 previous error diff --git a/tests/ui/nll/user-annotations/region-error-ice-109072.stderr b/tests/ui/nll/user-annotations/region-error-ice-109072.stderr index 42551b87f6234..026f5b5f80a9b 100644 --- a/tests/ui/nll/user-annotations/region-error-ice-109072.stderr +++ b/tests/ui/nll/user-annotations/region-error-ice-109072.stderr @@ -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 () { | ++++++++++