Skip to content

Tweak output of missing lifetime on associated type #145314

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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1103,14 +1103,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 {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,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)]
Expand Down
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
4 changes: 2 additions & 2 deletions compiler/rustc_resolve/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_resolve/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,8 @@ pub(crate) struct AnonymousLifetimeNonGatReportError {
#[primary_span]
#[label]
pub(crate) lifetime: Span,
#[note]
pub(crate) decl: MultiSpan,
}

#[derive(Subdiagnostic)]
Expand Down
144 changes: 137 additions & 7 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use std::borrow::Cow;
use std::collections::BTreeSet;
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,
Expand All @@ -20,20 +21,21 @@ 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, 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::{self, BuiltinLintDiag};
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};
Expand Down Expand Up @@ -372,11 +374,15 @@ 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,
}

impl LifetimeBinderKind {
Expand All @@ -387,6 +393,7 @@ impl LifetimeBinderKind {
PolyTrait => "bound",
WhereBound => "bound",
Item | ConstItem => "item",
ImplAssocType => "associated type",
ImplBlock => "impl block",
Function => "function",
Closure => "closure",
Expand Down Expand Up @@ -721,6 +728,9 @@ 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)>,

Expand Down Expand Up @@ -1887,9 +1897,35 @@ 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 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);
err.emit();
}
} else {
self.r.dcx().emit_err(errors::ElidedAnonymousLifetimeReportError {
Expand Down Expand Up @@ -1926,6 +1962,97 @@ 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()
.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 {
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",
);
}
}
}

#[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 @@ -3261,6 +3388,8 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
) {
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,
Expand Down Expand Up @@ -3360,7 +3489,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| {
Expand Down Expand Up @@ -3409,6 +3538,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
panic!("unexpanded macro in resolve!")
}
}
self.diag_metadata.current_impl_item = prev;
}

fn check_trait_item<F>(
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 @@ -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
Expand Down
15 changes: 14 additions & 1 deletion tests/ui/impl-header-lifetime-elision/assoc-type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,27 @@ 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 {
type Output = &'_ i32;
//~^ 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;
Expand Down
Loading
Loading