diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 5ae2aaadffae7..bf53b891a08f1 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -768,6 +768,9 @@ pub(crate) struct DiagMetadata<'ast> { /// Given `where ::Baz: String`, suggest `where T: Bar`. current_where_predicate: Option<&'ast WherePredicate>, + /// Whether we are visiting an associated type equality binding like `Trait`. + in_assoc_ty_binding: bool, + current_type_path: Option<&'ast Ty>, /// The current impl items (used to suggest). @@ -1327,7 +1330,11 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc } match constraint.kind { AssocItemConstraintKind::Equality { ref term } => match term { - Term::Ty(ty) => self.visit_ty(ty), + Term::Ty(ty) => { + let prev = replace(&mut self.diag_metadata.in_assoc_ty_binding, true); + self.visit_ty(ty); + self.diag_metadata.in_assoc_ty_binding = prev; + } Term::Const(c) => { self.resolve_anon_const(c, AnonConstKind::ConstArg(IsRepeatExpr::No)) } @@ -1927,21 +1934,35 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } LifetimeRibKind::AnonymousReportError => { let guar = if elided { - let suggestion = self.lifetime_ribs[i..].iter().rev().find_map(|rib| { - if let LifetimeRibKind::Generics { - span, - kind: LifetimeBinderKind::PolyTrait | LifetimeBinderKind::WhereBound, - .. - } = rib.kind - { - Some(crate::diagnostics::ElidedAnonymousLifetimeReportErrorSuggestion { - lo: span.shrink_to_lo(), - hi: lifetime.ident.span.shrink_to_hi(), - }) - } else { - None - } - }); + let suggestion = if self.diag_metadata.in_assoc_ty_binding { + // In an associated type binding like `I: IntoIterator`, + // introducing the lifetime on the trait ref would produce + // `I: for<'a> IntoIterator`. Prefer a named lifetime + // from an enclosing item instead, so the assoc-ty-binding-specific path + // below builds that suggestion. + None + } else { + self.lifetime_ribs[i..].iter().rev().find_map(|rib| { + // Look for a `Generics` rib that represents a trait or where-bound + // binder (`T: Trait<&U>` or `where T: Trait<&U>`), since that is + // where the generic E0637 diagnostic can insert `for<'a>`. + if let LifetimeRibKind::Generics { + span, + kind: + LifetimeBinderKind::PolyTrait + | LifetimeBinderKind::WhereBound, + .. + } = rib.kind + { + Some(crate::diagnostics::ElidedAnonymousLifetimeReportErrorSuggestion { + lo: span.shrink_to_lo(), + hi: lifetime.ident.span.shrink_to_hi(), + }) + } else { + None + } + }) + }; // are we trying to use an anonymous lifetime // on a non GAT associated trait type? if !self.in_func_body @@ -1993,6 +2014,22 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.point_at_impl_lifetimes(&mut err, i, lifetime.ident.span); err.emit() } + } else if self.diag_metadata.in_assoc_ty_binding { + // For associated type bindings, e.g. + // `fn f>()`, introduce a named lifetime + // on an enclosing generics binder instead: + // `fn f<'a, I: IntoIterator>()`. + let mut err = self.r.dcx().create_err( + crate::diagnostics::ElidedAnonymousLifetimeReportError { + span: lifetime.ident.span, + suggestion, + }, + ); + self.suggest_introducing_lifetime_for_assoc_ty_binding( + &mut err, + lifetime.ident.span, + ); + err.emit() } else { self.r.dcx().emit_err( crate::diagnostics::ElidedAnonymousLifetimeReportError { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 8994a7713c858..95fdf8124a225 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3762,6 +3762,50 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { String, Vec<(Span, String)>, ) -> bool, + ) { + self.suggest_introducing_lifetime_filtered(err, name, |_| true, suggest); + } + + pub(crate) fn suggest_introducing_lifetime_for_assoc_ty_binding( + &self, + err: &mut Diag<'_>, + lifetime: Span, + ) { + self.suggest_introducing_lifetime_filtered( + err, + None, + |kind| { + !matches!( + kind, + LifetimeBinderKind::FnPtrType + | LifetimeBinderKind::PolyTrait + | LifetimeBinderKind::WhereBound + ) + }, + |err, _higher_ranked, span, message, intro_sugg, _| { + err.multipart_suggestion( + message, + vec![(span, intro_sugg), (lifetime.shrink_to_hi(), "'a ".to_string())], + Applicability::MaybeIncorrect, + ); + false + }, + ); + } + + fn suggest_introducing_lifetime_filtered( + &self, + err: &mut Diag<'_>, + name: Option, + mut consider: impl FnMut(LifetimeBinderKind) -> bool, + suggest: impl Fn( + &mut Diag<'_>, + bool, + Span, + Cow<'static, str>, + String, + Vec<(Span, String)>, + ) -> bool, ) { let mut suggest_note = true; for rib in self.lifetime_ribs.iter().rev() { @@ -3775,7 +3819,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { { continue; } - if let LifetimeBinderKind::ImplAssocType = kind { + if matches!(kind, LifetimeBinderKind::ImplAssocType) || !consider(kind) { continue; } diff --git a/tests/ui/error-codes/E0637.rs b/tests/ui/error-codes/E0637.rs index 382ce3ed01f34..17275b145b21b 100644 --- a/tests/ui/error-codes/E0637.rs +++ b/tests/ui/error-codes/E0637.rs @@ -14,4 +14,16 @@ where { } +// Regression test for https://github.com/rust-lang/rust/issues/156456. +fn associated_type_binding>() {} +//~^ ERROR: `&` without an explicit lifetime name cannot be used here [E0637] + +trait T { + type Assoc; +} + +// Regression test for https://github.com/rust-lang/rust/issues/122025. +fn foo(t: F) where F: T {} +//~^ ERROR: `&` without an explicit lifetime name cannot be used here [E0637] + fn main() {} diff --git a/tests/ui/error-codes/E0637.stderr b/tests/ui/error-codes/E0637.stderr index 88c08cb94ba8d..d7e718a25e5c8 100644 --- a/tests/ui/error-codes/E0637.stderr +++ b/tests/ui/error-codes/E0637.stderr @@ -30,7 +30,29 @@ help: consider introducing a higher-ranked lifetime here LL | T: for<'a> Into<&'a u32>, | +++++++ ++ -error: aborting due to 3 previous errors +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/E0637.rs:18:51 + | +LL | fn associated_type_binding>() {} + | ^ explicit lifetime name needed here + | +help: consider introducing a named lifetime parameter + | +LL | fn associated_type_binding<'a, I: IntoIterator>() {} + | +++ ++ + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/E0637.rs:26:34 + | +LL | fn foo(t: F) where F: T {} + | ^ explicit lifetime name needed here + | +help: consider introducing a named lifetime parameter + | +LL | fn foo<'a, F>(t: F) where F: T {} + | +++ ++ + +error: aborting due to 5 previous errors Some errors have detailed explanations: E0106, E0637. For more information about an error, try `rustc --explain E0106`.