diff options
Diffstat (limited to 'compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs')
-rw-r--r-- | compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs | 400 |
1 files changed, 199 insertions, 201 deletions
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index 09f9aa3c8..49ad3ce50 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -1,20 +1,28 @@ //! Error Reporting for static impl Traits. +use crate::errors::{ + ButCallingIntroduces, ButNeedsToSatisfy, DynTraitConstraintSuggestion, MoreTargeted, + ReqIntroducedLocations, +}; use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::{ObligationCauseCode, UnifyReceiverContext}; use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; +use rustc_errors::{AddToDiagnostic, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_ty, Visitor}; -use rustc_hir::{self as hir, GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, TyKind}; +use rustc_hir::{ + self as hir, GenericBound, GenericParamKind, Item, ItemKind, Lifetime, LifetimeName, Node, + TyKind, +}; use rustc_middle::ty::{ self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, }; use rustc_span::symbol::Ident; use rustc_span::Span; +use rustc_span::def_id::LocalDefId; use std::ops::ControlFlow; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { @@ -49,46 +57,32 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } let param = self.find_param_with_region(*sup_r, *sub_r)?; - let lifetime = if sup_r.has_name() { - format!("lifetime `{}`", sup_r) - } else { - "an anonymous lifetime `'_`".to_string() + let simple_ident = param.param.pat.simple_ident(); + + let (has_impl_path, impl_path) = match ctxt.assoc_item.container { + AssocItemContainer::TraitContainer => { + let id = ctxt.assoc_item.container_id(tcx); + (true, tcx.def_path_str(id)) + } + AssocItemContainer::ImplContainer => (false, String::new()), }; - let mut err = struct_span_err!( - tcx.sess, - cause.span, - E0772, - "{} has {} but calling `{}` introduces an implicit `'static` lifetime \ - requirement", - param - .param - .pat - .simple_ident() - .map(|s| format!("`{}`", s)) - .unwrap_or_else(|| "`fn` parameter".to_string()), - lifetime, - ctxt.assoc_item.name, - ); - err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime)); - err.span_label( - cause.span, - &format!( - "...is used and required to live as long as `'static` here \ - because of an implicit lifetime bound on the {}", - match ctxt.assoc_item.container { - AssocItemContainer::TraitContainer => { - let id = ctxt.assoc_item.container_id(tcx); - format!("`impl` of `{}`", tcx.def_path_str(id)) - } - AssocItemContainer::ImplContainer => "inherent `impl`".to_string(), - }, - ), - ); + + let mut err = self.tcx().sess.create_err(ButCallingIntroduces { + param_ty_span: param.param_ty_span, + cause_span: cause.span, + has_param_name: simple_ident.is_some(), + param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(), + has_lifetime: sup_r.has_name(), + lifetime: sup_r.to_string(), + assoc_item: ctxt.assoc_item.name, + has_impl_path, + impl_path, + }); if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) { let reported = err.emit(); return Some(reported); } else { - err.cancel(); + err.cancel() } } return None; @@ -104,25 +98,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let sp = var_origin.span(); let return_sp = sub_origin.span(); let param = self.find_param_with_region(*sup_r, *sub_r)?; - let (lifetime_name, lifetime) = if sup_r.has_name() { - (sup_r.to_string(), format!("lifetime `{}`", sup_r)) - } else { - ("'_".to_owned(), "an anonymous lifetime `'_`".to_string()) - }; - let param_name = param - .param - .pat - .simple_ident() - .map(|s| format!("`{}`", s)) - .unwrap_or_else(|| "`fn` parameter".to_string()); - let mut err = struct_span_err!( - tcx.sess, - sp, - E0759, - "{} has {} but it needs to satisfy a `'static` lifetime requirement", - param_name, - lifetime, - ); + let lifetime_name = if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() }; let (mention_influencer, influencer_point) = if sup_origin.span().overlaps(param.param_ty_span) { @@ -141,7 +117,6 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } else { (!sup_origin.span().overlaps(return_sp), param.param_ty_span) }; - err.span_label(influencer_point, &format!("this data with {}...", lifetime)); debug!("try_report_static_impl_trait: param_info={:?}", param); @@ -155,31 +130,19 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { spans.dedup_by_key(|span| (span.lo(), span.hi())); // We try to make the output have fewer overlapping spans if possible. - let require_msg = if spans.is_empty() { - "...is used and required to live as long as `'static` here" - } else { - "...and is required to live as long as `'static` here" - }; let require_span = if sup_origin.span().overlaps(return_sp) { sup_origin.span() } else { return_sp }; - for span in &spans { - err.span_label(*span, "...is used here..."); - } - - if spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp) { - // If any of the "captured here" labels appears on the same line or after - // `require_span`, we put it on a note to ensure the text flows by appearing - // always at the end. - err.span_note(require_span, require_msg); + let spans_empty = spans.is_empty(); + let require_as_note = spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp); + let bound = if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin { + Some(*bound) } else { - // We don't need a note, it's already at the end, it can be shown as a `span_label`. - err.span_label(require_span, require_msg); - } + None + }; + + let mut subdiag = None; - if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin { - err.span_note(*bound, "`'static` lifetime requirement introduced by this bound"); - } if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = sub_origin { if let ObligationCauseCode::ReturnValue(hir_id) | ObligationCauseCode::BlockTailExpression(hir_id) = cause.code() @@ -187,33 +150,50 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let parent_id = tcx.hir().get_parent_item(*hir_id); if let Some(fn_decl) = tcx.hir().fn_decl_by_hir_id(parent_id.into()) { let mut span: MultiSpan = fn_decl.output.span().into(); + let mut spans = Vec::new(); let mut add_label = true; if let hir::FnRetTy::Return(ty) = fn_decl.output { let mut v = StaticLifetimeVisitor(vec![], tcx.hir()); v.visit_ty(ty); if !v.0.is_empty() { span = v.0.clone().into(); - for sp in v.0 { - span.push_span_label(sp, "`'static` requirement introduced here"); - } + spans = v.0; add_label = false; } } - if add_label { - span.push_span_label( - fn_decl.output.span(), - "requirement introduced by this return type", - ); - } - span.push_span_label(cause.span, "because of this returned expression"); - err.span_note( + let fn_decl_span = fn_decl.output.span(); + + subdiag = Some(ReqIntroducedLocations { span, - "`'static` lifetime requirement introduced by the return type", - ); + spans, + fn_decl_span, + cause_span: cause.span, + add_label, + }); } } } + let diag = ButNeedsToSatisfy { + sp, + influencer_point, + spans: spans.clone(), + // If any of the "captured here" labels appears on the same line or after + // `require_span`, we put it on a note to ensure the text flows by appearing + // always at the end. + require_span_as_note: require_as_note.then_some(require_span), + // We don't need a note, it's already at the end, it can be shown as a `span_label`. + require_span_as_label: (!require_as_note).then_some(require_span), + req_introduces_loc: subdiag, + + has_lifetime: sup_r.has_name(), + lifetime: sup_r.to_string(), + spans_empty, + bound, + }; + + let mut err = self.tcx().sess.create_err(diag); + let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id); let mut override_error_code = None; @@ -239,7 +219,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let mut v = TraitObjectVisitor(FxIndexSet::default()); v.visit_ty(param.param_ty); if let Some((ident, self_ty)) = - self.get_impl_ident_and_self_ty_from_trait(item_def_id, &v.0) + NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, item_def_id, &v.0) && self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0, ident, self_ty) { override_error_code = Some(ident.name); @@ -247,12 +227,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } if let (Some(ident), true) = (override_error_code, fn_returns.is_empty()) { // Provide a more targeted error code and description. - err.code(rustc_errors::error_code!(E0772)); - err.set_primary_message(&format!( - "{} has {} but calling `{}` introduces an implicit `'static` lifetime \ - requirement", - param_name, lifetime, ident, - )); + let retarget_subdiag = MoreTargeted { ident }; + retarget_subdiag.add_to_diagnostic(&mut err); } let arg = match param.param.pat.simple_ident() { @@ -268,6 +244,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { Some(arg), captures, Some((param.param_ty_span, param.param_ty.to_string())), + Some(anon_reg_sup.def_id), ); let reported = err.emit(); @@ -283,6 +260,7 @@ pub fn suggest_new_region_bound( arg: Option<String>, captures: String, param: Option<(Span, String)>, + scope_def_id: Option<LocalDefId>, ) { debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns); // FIXME: account for the need of parens in `&(dyn Trait + '_)` @@ -309,19 +287,12 @@ pub fn suggest_new_region_bound( let did = item_id.owner_id.to_def_id(); let ty = tcx.mk_opaque(did, ty::InternalSubsts::identity_for_item(tcx, did)); - if let Some(span) = opaque - .bounds - .iter() - .filter_map(|arg| match arg { - GenericBound::Outlives(Lifetime { - res: LifetimeName::Static, - ident, - .. - }) => Some(ident.span), - _ => None, - }) - .next() - { + if let Some(span) = opaque.bounds.iter().find_map(|arg| match arg { + GenericBound::Outlives(Lifetime { + res: LifetimeName::Static, ident, .. + }) => Some(ident.span), + _ => None, + }) { if let Some(explicit_static) = &explicit_static { err.span_suggestion_verbose( span, @@ -330,7 +301,7 @@ pub fn suggest_new_region_bound( Applicability::MaybeIncorrect, ); } - if let Some((param_span, param_ty)) = param.clone() { + if let Some((param_span, ref param_ty)) = param { err.span_suggestion_verbose( param_span, add_static_bound, @@ -338,27 +309,78 @@ pub fn suggest_new_region_bound( Applicability::MaybeIncorrect, ); } - } else if opaque - .bounds - .iter() - .filter_map(|arg| match arg { - GenericBound::Outlives(Lifetime { ident, .. }) - if ident.name.to_string() == lifetime_name => - { - Some(ident.span) - } - _ => None, - }) - .next() - .is_some() - { + } else if opaque.bounds.iter().any(|arg| match arg { + GenericBound::Outlives(Lifetime { ident, .. }) + if ident.name.to_string() == lifetime_name => + { + true + } + _ => false, + }) { } else { - err.span_suggestion_verbose( - fn_return.span.shrink_to_hi(), - &format!("{declare} `{ty}` {captures}, {explicit}",), - &plus_lt, - Applicability::MaybeIncorrect, - ); + // get a lifetime name of existing named lifetimes if any + let existing_lt_name = if let Some(id) = scope_def_id + && let Some(generics) = tcx.hir().get_generics(id) + && let named_lifetimes = generics + .params + .iter() + .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit })) + .map(|p| { if let hir::ParamName::Plain(name) = p.name {Some(name.to_string())} else {None}}) + .filter(|n| ! matches!(n, None)) + .collect::<Vec<_>>() + && named_lifetimes.len() > 0 { + named_lifetimes[0].clone() + } else { + None + }; + let name = if let Some(name) = &existing_lt_name { + format!("{}", name) + } else { + format!("'a") + }; + // if there are more than one elided lifetimes in inputs, the explicit `'_` lifetime cannot be used. + // introducing a new lifetime `'a` or making use of one from existing named lifetimes if any + if let Some(id) = scope_def_id + && let Some(generics) = tcx.hir().get_generics(id) + && let mut spans_suggs = generics + .params + .iter() + .filter(|p| p.is_elided_lifetime()) + .map(|p| + if p.span.hi() - p.span.lo() == rustc_span::BytePos(1) { // Ampersand (elided without '_) + (p.span.shrink_to_hi(),format!("{name} ")) + } else { // Underscore (elided with '_) + (p.span, format!("{name}")) + } + ) + .collect::<Vec<_>>() + && spans_suggs.len() > 1 + { + let use_lt = + if existing_lt_name == None { + spans_suggs.push((generics.span.shrink_to_hi(), format!("<{name}>"))); + format!("you can introduce a named lifetime parameter `{name}`") + } else { + // make use the existing named lifetime + format!("you can use the named lifetime parameter `{name}`") + }; + spans_suggs + .push((fn_return.span.shrink_to_hi(), format!(" + {name} "))); + err.multipart_suggestion_verbose( + &format!( + "{declare} `{ty}` {captures}, {use_lt}", + ), + spans_suggs, + Applicability::MaybeIncorrect, + ); + } else { + err.span_suggestion_verbose( + fn_return.span.shrink_to_hi(), + &format!("{declare} `{ty}` {captures}, {explicit}",), + &plus_lt, + Applicability::MaybeIncorrect, + ); + } } } TyKind::TraitObject(_, lt, _) => { @@ -403,66 +425,54 @@ pub fn suggest_new_region_bound( } impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { - fn get_impl_ident_and_self_ty_from_trait( - &self, + pub fn get_impl_ident_and_self_ty_from_trait( + tcx: TyCtxt<'tcx>, def_id: DefId, trait_objects: &FxIndexSet<DefId>, ) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> { - let tcx = self.tcx(); - match tcx.hir().get_if_local(def_id) { - Some(Node::ImplItem(impl_item)) => { - match tcx.hir().find_by_def_id(tcx.hir().get_parent_item(impl_item.hir_id()).def_id) + match tcx.hir().get_if_local(def_id)? { + Node::ImplItem(impl_item) => { + let impl_did = tcx.hir().get_parent_item(impl_item.hir_id()); + if let hir::OwnerNode::Item(Item { + kind: ItemKind::Impl(hir::Impl { self_ty, .. }), + .. + }) = tcx.hir().owner(impl_did) { - Some(Node::Item(Item { - kind: ItemKind::Impl(hir::Impl { self_ty, .. }), - .. - })) => Some((impl_item.ident, self_ty)), - _ => None, + Some((impl_item.ident, self_ty)) + } else { + None } } - Some(Node::TraitItem(trait_item)) => { - let trait_did = tcx.hir().get_parent_item(trait_item.hir_id()); - match tcx.hir().find_by_def_id(trait_did.def_id) { - Some(Node::Item(Item { kind: ItemKind::Trait(..), .. })) => { - // The method being called is defined in the `trait`, but the `'static` - // obligation comes from the `impl`. Find that `impl` so that we can point - // at it in the suggestion. - let trait_did = trait_did.to_def_id(); - match tcx - .hir() - .trait_impls(trait_did) - .iter() - .filter_map(|&impl_did| { - match tcx.hir().get_if_local(impl_did.to_def_id()) { - Some(Node::Item(Item { - kind: ItemKind::Impl(hir::Impl { self_ty, .. }), - .. - })) if trait_objects.iter().all(|did| { - // FIXME: we should check `self_ty` against the receiver - // type in the `UnifyReceiver` context, but for now, use - // this imperfect proxy. This will fail if there are - // multiple `impl`s for the same trait like - // `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`. - // In that case, only the first one will get suggestions. - let mut traits = vec![]; - let mut hir_v = HirTraitObjectVisitor(&mut traits, *did); - hir_v.visit_ty(self_ty); - !traits.is_empty() - }) => - { - Some(self_ty) - } - _ => None, - } - }) - .next() - { - Some(self_ty) => Some((trait_item.ident, self_ty)), - _ => None, - } + Node::TraitItem(trait_item) => { + let trait_id = tcx.hir().get_parent_item(trait_item.hir_id()); + debug_assert_eq!(tcx.def_kind(trait_id.def_id), hir::def::DefKind::Trait); + // The method being called is defined in the `trait`, but the `'static` + // obligation comes from the `impl`. Find that `impl` so that we can point + // at it in the suggestion. + let trait_did = trait_id.to_def_id(); + tcx.hir().trait_impls(trait_did).iter().find_map(|&impl_did| { + if let Node::Item(Item { + kind: ItemKind::Impl(hir::Impl { self_ty, .. }), + .. + }) = tcx.hir().find_by_def_id(impl_did)? + && trait_objects.iter().all(|did| { + // FIXME: we should check `self_ty` against the receiver + // type in the `UnifyReceiver` context, but for now, use + // this imperfect proxy. This will fail if there are + // multiple `impl`s for the same trait like + // `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`. + // In that case, only the first one will get suggestions. + let mut traits = vec![]; + let mut hir_v = HirTraitObjectVisitor(&mut traits, *did); + hir_v.visit_ty(self_ty); + !traits.is_empty() + }) + { + Some((trait_item.ident, *self_ty)) + } else { + None } - _ => None, - } + }) } _ => None, } @@ -493,7 +503,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // Get the `Ident` of the method being called and the corresponding `impl` (to point at // `Bar` in `impl Foo for dyn Bar {}` and the definition of the method being called). - let Some((ident, self_ty)) = self.get_impl_ident_and_self_ty_from_trait(instance.def_id(), &v.0) else { + let Some((ident, self_ty)) = NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, instance.def_id(), &v.0) else { return false; }; @@ -513,21 +523,9 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let mut traits = vec![]; let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did); hir_v.visit_ty(&self_ty); - for span in &traits { - let mut multi_span: MultiSpan = vec![*span].into(); - multi_span - .push_span_label(*span, "this has an implicit `'static` lifetime requirement"); - multi_span.push_span_label( - ident.span, - "calling this method introduces the `impl`'s 'static` requirement", - ); - err.span_note(multi_span, "the used `impl` has a `'static` requirement"); - err.span_suggestion_verbose( - span.shrink_to_hi(), - "consider relaxing the implicit `'static` requirement", - " + '_", - Applicability::MaybeIncorrect, - ); + for &span in &traits { + let subdiag = DynTraitConstraintSuggestion { span, ident }; + subdiag.add_to_diagnostic(err); suggested = true; } } @@ -545,7 +543,7 @@ impl<'tcx> TypeVisitor<'tcx> for TraitObjectVisitor { if let Some(def_id) = preds.principal_def_id() { self.0.insert(def_id); } - ControlFlow::CONTINUE + ControlFlow::Continue(()) } _ => t.super_visit_with(self), } |