| //! 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::{Applicability, Diag, ErrorGuaranteed, MultiSpan, Subdiagnostic}; |
| use rustc_hir::def_id::DefId; |
| use rustc_hir::intravisit::{walk_ty, Visitor}; |
| use rustc_hir::{ |
| self as hir, GenericBound, GenericParam, GenericParamKind, Item, ItemKind, Lifetime, |
| LifetimeName, LifetimeParamKind, MissingLifetimeKind, 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; |
| |
| impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { |
| /// Print the error message for lifetime errors when the return type is a static `impl Trait`, |
| /// `dyn Trait` or if a method call on a trait object introduces a static requirement. |
| pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorGuaranteed> { |
| debug!("try_report_static_impl_trait(error={:?})", self.error); |
| let tcx = self.tcx(); |
| let (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans) = match self.error.as_ref()? { |
| RegionResolutionError::SubSupConflict( |
| _, |
| var_origin, |
| sub_origin, |
| sub_r, |
| sup_origin, |
| sup_r, |
| spans, |
| ) if sub_r.is_static() => (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans), |
| RegionResolutionError::ConcreteFailure( |
| SubregionOrigin::Subtype(box TypeTrace { cause, .. }), |
| sub_r, |
| sup_r, |
| ) if sub_r.is_static() => { |
| // This is for an implicit `'static` requirement coming from `impl dyn Trait {}`. |
| if let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() { |
| // This may have a closure and it would cause ICE |
| // through `find_param_with_region` (#78262). |
| let anon_reg_sup = tcx.is_suitable_region(*sup_r)?; |
| let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id); |
| if fn_returns.is_empty() { |
| return None; |
| } |
| |
| let param = self.find_param_with_region(*sup_r, *sub_r)?; |
| 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 = self.tcx().dcx().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() |
| } |
| } |
| return None; |
| } |
| _ => return None, |
| }; |
| debug!( |
| "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})", |
| var_origin, sub_origin, sub_r, sup_origin, sup_r |
| ); |
| let anon_reg_sup = tcx.is_suitable_region(*sup_r)?; |
| debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup); |
| let sp = var_origin.span(); |
| let return_sp = sub_origin.span(); |
| let param = self.find_param_with_region(*sup_r, *sub_r)?; |
| let simple_ident = param.param.pat.simple_ident(); |
| 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) { |
| // Account for `async fn` like in `async-await/issues/issue-62097.rs`. |
| // The desugaring of `async fn`s causes `sup_origin` and `param` to point at the same |
| // place (but with different `ctxt`, hence `overlaps` instead of `==` above). |
| // |
| // This avoids the following: |
| // |
| // LL | pub async fn run_dummy_fn(&self) { |
| // | ^^^^^ |
| // | | |
| // | this data with an anonymous lifetime `'_`... |
| // | ...is captured here... |
| (false, sup_origin.span()) |
| } else { |
| (!sup_origin.span().overlaps(return_sp), param.param_ty_span) |
| }; |
| |
| debug!("try_report_static_impl_trait: param_info={:?}", param); |
| |
| let mut spans = spans.clone(); |
| |
| if mention_influencer { |
| spans.push(sup_origin.span()); |
| } |
| // We dedup the spans *ignoring* expansion context. |
| spans.sort(); |
| spans.dedup_by_key(|span| (span.lo(), span.hi())); |
| |
| // We try to make the output have fewer overlapping spans if possible. |
| let require_span = |
| if sup_origin.span().overlaps(return_sp) { sup_origin.span() } else { return_sp }; |
| |
| 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 { |
| None |
| }; |
| |
| let mut subdiag = None; |
| |
| if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = sub_origin { |
| if let ObligationCauseCode::ReturnValue(hir_id) |
| | ObligationCauseCode::BlockTailExpression(hir_id, ..) = cause.code() |
| { |
| 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(); |
| spans = v.0; |
| add_label = false; |
| } |
| } |
| let fn_decl_span = fn_decl.output.span(); |
| |
| subdiag = Some(ReqIntroducedLocations { |
| span, |
| 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: lifetime_name.clone(), |
| has_param_name: simple_ident.is_some(), |
| param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(), |
| spans_empty, |
| bound, |
| }; |
| |
| let mut err = self.tcx().dcx().create_err(diag); |
| |
| let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id); |
| |
| let mut override_error_code = None; |
| if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sup_origin |
| && let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() |
| // Handle case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a |
| // `'static` lifetime when called as a method on a binding: `bar.qux()`. |
| && self.find_impl_on_dyn_trait(&mut err, param.param_ty, ctxt) |
| { |
| override_error_code = Some(ctxt.assoc_item.name); |
| } |
| |
| if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sub_origin |
| && let code = match cause.code() { |
| ObligationCauseCode::MatchImpl(parent, ..) => parent.code(), |
| _ => cause.code(), |
| } |
| && let ( |
| &ObligationCauseCode::WhereClause(item_def_id) |
| | &ObligationCauseCode::WhereClauseInExpr(item_def_id, ..), |
| None, |
| ) = (code, override_error_code) |
| { |
| // Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static` |
| // lifetime as above, but called using a fully-qualified path to the method: |
| // `Foo::qux(bar)`. |
| let mut v = TraitObjectVisitor(FxIndexSet::default()); |
| v.visit_ty(param.param_ty); |
| if let Some((ident, self_ty)) = |
| 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); |
| } |
| } |
| if let (Some(ident), true) = (override_error_code, fn_returns.is_empty()) { |
| // Provide a more targeted error code and description. |
| let retarget_subdiag = MoreTargeted { ident }; |
| retarget_subdiag.add_to_diag(&mut err); |
| } |
| |
| let arg = match param.param.pat.simple_ident() { |
| Some(simple_ident) => format!("argument `{simple_ident}`"), |
| None => "the argument".to_string(), |
| }; |
| let captures = format!("captures data from {arg}"); |
| suggest_new_region_bound( |
| tcx, |
| &mut err, |
| fn_returns, |
| lifetime_name, |
| Some(arg), |
| captures, |
| Some((param.param_ty_span, param.param_ty.to_string())), |
| Some(anon_reg_sup.def_id), |
| ); |
| |
| let reported = err.emit(); |
| Some(reported) |
| } |
| } |
| |
| pub fn suggest_new_region_bound( |
| tcx: TyCtxt<'_>, |
| err: &mut Diag<'_>, |
| fn_returns: Vec<&rustc_hir::Ty<'_>>, |
| lifetime_name: String, |
| 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 + '_)` |
| let consider = "consider changing"; |
| let declare = "to declare that"; |
| let explicit = format!("you can add an explicit `{lifetime_name}` lifetime bound"); |
| let explicit_static = |
| arg.map(|arg| format!("explicit `'static` bound to the lifetime of {arg}")); |
| let add_static_bound = "alternatively, add an explicit `'static` bound to this reference"; |
| let plus_lt = format!(" + {lifetime_name}"); |
| for fn_return in fn_returns { |
| if fn_return.span.desugaring_kind().is_some() { |
| // Skip `async` desugaring `impl Future`. |
| continue; |
| } |
| match fn_return.kind { |
| // FIXME(precise_captures): Suggest adding to `use<...>` list instead. |
| TyKind::OpaqueDef(item_id, _, _) => { |
| let item = tcx.hir().item(item_id); |
| let ItemKind::OpaqueTy(opaque) = &item.kind else { |
| return; |
| }; |
| |
| // Get the identity type for this RPIT |
| let did = item_id.owner_id.to_def_id(); |
| let ty = Ty::new_opaque(tcx, did, ty::GenericArgs::identity_for_item(tcx, did)); |
| |
| 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, |
| format!("{consider} `{ty}`'s {explicit_static}"), |
| &lifetime_name, |
| Applicability::MaybeIncorrect, |
| ); |
| } |
| if let Some((param_span, ref param_ty)) = param { |
| err.span_suggestion_verbose( |
| param_span, |
| add_static_bound, |
| param_ty, |
| Applicability::MaybeIncorrect, |
| ); |
| } |
| } else if opaque.bounds.iter().any(|arg| { |
| matches!(arg, |
| GenericBound::Outlives(Lifetime { ident, .. }) |
| if ident.name.to_string() == lifetime_name ) |
| }) { |
| } else { |
| // 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 { name } else { "'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 = |
| make_elided_region_spans_suggs(name, generics.params.iter()) |
| && 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, _) => { |
| if let LifetimeName::ImplicitObjectLifetimeDefault = lt.res { |
| err.span_suggestion_verbose( |
| fn_return.span.shrink_to_hi(), |
| format!("{declare} the trait object {captures}, {explicit}",), |
| &plus_lt, |
| Applicability::MaybeIncorrect, |
| ); |
| } else if lt.ident.name.to_string() != lifetime_name { |
| // With this check we avoid suggesting redundant bounds. This |
| // would happen if there are nested impl/dyn traits and only |
| // one of them has the bound we'd suggest already there, like |
| // in `impl Foo<X = dyn Bar> + '_`. |
| if let Some(explicit_static) = &explicit_static { |
| err.span_suggestion_verbose( |
| lt.ident.span, |
| format!("{consider} the trait object's {explicit_static}"), |
| &lifetime_name, |
| Applicability::MaybeIncorrect, |
| ); |
| } |
| if let Some((param_span, param_ty)) = param.clone() { |
| err.span_suggestion_verbose( |
| param_span, |
| add_static_bound, |
| param_ty, |
| Applicability::MaybeIncorrect, |
| ); |
| } |
| } |
| } |
| _ => {} |
| } |
| } |
| } |
| |
| fn make_elided_region_spans_suggs<'a>( |
| name: &str, |
| generic_params: impl Iterator<Item = &'a GenericParam<'a>>, |
| ) -> Vec<(Span, String)> { |
| let mut spans_suggs = Vec::new(); |
| let mut bracket_span = None; |
| let mut consecutive_brackets = 0; |
| |
| let mut process_consecutive_brackets = |
| |span: Option<Span>, spans_suggs: &mut Vec<(Span, String)>| { |
| if span |
| .is_some_and(|span| bracket_span.map_or(true, |bracket_span| span == bracket_span)) |
| { |
| consecutive_brackets += 1; |
| } else if let Some(bracket_span) = bracket_span.take() { |
| let sugg = std::iter::once("<") |
| .chain(std::iter::repeat(name).take(consecutive_brackets).intersperse(", ")) |
| .chain([">"]) |
| .collect(); |
| spans_suggs.push((bracket_span.shrink_to_hi(), sugg)); |
| consecutive_brackets = 0; |
| } |
| bracket_span = span; |
| }; |
| |
| for p in generic_params { |
| if let GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided(kind) } = p.kind { |
| match kind { |
| MissingLifetimeKind::Underscore => { |
| process_consecutive_brackets(None, &mut spans_suggs); |
| spans_suggs.push((p.span, name.to_string())) |
| } |
| MissingLifetimeKind::Ampersand => { |
| process_consecutive_brackets(None, &mut spans_suggs); |
| spans_suggs.push((p.span.shrink_to_hi(), format!("{name} "))); |
| } |
| MissingLifetimeKind::Comma => { |
| process_consecutive_brackets(None, &mut spans_suggs); |
| spans_suggs.push((p.span.shrink_to_hi(), format!("{name}, "))); |
| } |
| MissingLifetimeKind::Brackets => { |
| process_consecutive_brackets(Some(p.span), &mut spans_suggs); |
| } |
| } |
| } |
| } |
| process_consecutive_brackets(None, &mut spans_suggs); |
| |
| spans_suggs |
| } |
| |
| impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { |
| 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>)> { |
| 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_node(impl_did) |
| { |
| Some((impl_item.ident, self_ty)) |
| } else { |
| 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_node_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, |
| } |
| } |
| |
| /// When we call a method coming from an `impl Foo for dyn Bar`, `dyn Bar` introduces a default |
| /// `'static` obligation. Suggest relaxing that implicit bound. |
| fn find_impl_on_dyn_trait( |
| &self, |
| err: &mut Diag<'_>, |
| ty: Ty<'_>, |
| ctxt: &UnifyReceiverContext<'tcx>, |
| ) -> bool { |
| let tcx = self.tcx(); |
| |
| // Find the method being called. |
| let Ok(Some(instance)) = ty::Instance::resolve( |
| tcx, |
| ctxt.param_env, |
| ctxt.assoc_item.def_id, |
| self.cx.resolve_vars_if_possible(ctxt.args), |
| ) else { |
| return false; |
| }; |
| |
| let mut v = TraitObjectVisitor(FxIndexSet::default()); |
| v.visit_ty(ty); |
| |
| // 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)) = |
| NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, instance.def_id(), &v.0) |
| else { |
| return false; |
| }; |
| |
| // Find the trait object types in the argument, so we point at *only* the trait object. |
| self.suggest_constrain_dyn_trait_in_impl(err, &v.0, ident, self_ty) |
| } |
| |
| fn suggest_constrain_dyn_trait_in_impl( |
| &self, |
| err: &mut Diag<'_>, |
| found_dids: &FxIndexSet<DefId>, |
| ident: Ident, |
| self_ty: &hir::Ty<'_>, |
| ) -> bool { |
| let mut suggested = false; |
| for found_did in found_dids { |
| let mut traits = vec![]; |
| let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did); |
| hir_v.visit_ty(self_ty); |
| for &span in &traits { |
| let subdiag = DynTraitConstraintSuggestion { span, ident }; |
| subdiag.add_to_diag(err); |
| suggested = true; |
| } |
| } |
| suggested |
| } |
| } |
| |
| /// Collect all the trait objects in a type that could have received an implicit `'static` lifetime. |
| pub struct TraitObjectVisitor(pub FxIndexSet<DefId>); |
| |
| impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for TraitObjectVisitor { |
| fn visit_ty(&mut self, t: Ty<'tcx>) { |
| match t.kind() { |
| ty::Dynamic(preds, re, _) if re.is_static() => { |
| if let Some(def_id) = preds.principal_def_id() { |
| self.0.insert(def_id); |
| } |
| } |
| _ => t.super_visit_with(self), |
| } |
| } |
| } |
| |
| /// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime. |
| pub struct HirTraitObjectVisitor<'a>(pub &'a mut Vec<Span>, pub DefId); |
| |
| impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> { |
| fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { |
| if let TyKind::TraitObject( |
| poly_trait_refs, |
| Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. }, |
| _, |
| ) = t.kind |
| { |
| for ptr in poly_trait_refs { |
| if Some(self.1) == ptr.trait_ref.trait_def_id() { |
| self.0.push(ptr.span); |
| } |
| } |
| } |
| walk_ty(self, t); |
| } |
| } |