| //! This crate contains a helper for walking all types of a signature, while preserving spans as | |
| //! much as possible | |
| // tidy-alphabetical-start | |
| #![feature(associated_type_defaults)] | |
| // tidy-alphabetical-end | |
| use rustc_hir::def::DefKind; | |
| use rustc_hir::def_id::LocalDefId; | |
| use rustc_middle::ty::{self, TyCtxt, TypeVisitable, Unnormalized, VisitorResult, try_visit}; | |
| use rustc_span::{Span, span_bug}; | |
| use tracing::{instrument, trace}; | |
| pub trait SpannedTypeVisitor<'tcx> { | |
| type Result: VisitorResult = (); | |
| fn visit(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) -> Self::Result; | |
| } | |
| #[instrument(level = "trace", skip(tcx, visitor))] | |
| pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( | |
| tcx: TyCtxt<'tcx>, | |
| item: LocalDefId, | |
| visitor: &mut V, | |
| ) -> V::Result { | |
| let kind = tcx.def_kind(item); | |
| trace!(?kind); | |
| let mut visit_alias = || { | |
| if let Some(ty) = tcx.hir_node_by_def_id(item).ty() { | |
| // If the type of the item uses `_`, we're gonna error out anyway, but | |
| // typeck (which type_of invokes below), will call back into opaque_types_defined_by | |
| // causing a cycle. So we just bail out in this case. | |
| if ty.is_suggestable_infer_ty() { | |
| return V::Result::output(); | |
| } | |
| // Associated types in traits don't necessarily have a type that we can visit | |
| try_visit!( | |
| visitor.visit(ty.span, tcx.type_of(item).instantiate_identity().skip_norm_wip()) | |
| ); | |
| } | |
| for (clause, span) in tcx.explicit_clauses_of(item).instantiate_identity(tcx) { | |
| try_visit!(visitor.visit(span, clause.skip_norm_wip())); | |
| } | |
| V::Result::output() | |
| }; | |
| match kind { | |
| // Walk over the signature of the function | |
| DefKind::AssocFn | DefKind::Fn => { | |
| let hir_sig = tcx.hir_node_by_def_id(item).fn_decl().unwrap(); | |
| // If the type of the item uses `_`, we're gonna error out anyway, but | |
| // typeck (which type_of invokes below), will call back into opaque_types_defined_by | |
| // causing a cycle. So we just bail out in this case. | |
| if hir_sig.output.is_suggestable_infer_ty().is_some() { | |
| return V::Result::output(); | |
| } | |
| let ty_sig = tcx.fn_sig(item).instantiate_identity().skip_norm_wip(); | |
| // Walk over the inputs and outputs manually in order to get good spans for them. | |
| try_visit!(visitor.visit(hir_sig.output.span(), ty_sig.output())); | |
| for (hir, ty) in hir_sig.inputs.iter().zip(ty_sig.inputs().iter()) { | |
| try_visit!(visitor.visit(hir.span, ty.map_bound(|x| *x))); | |
| } | |
| for (clause, span) in tcx.explicit_clauses_of(item).instantiate_identity(tcx) { | |
| try_visit!(visitor.visit(span, clause.skip_norm_wip())); | |
| } | |
| } | |
| // Walk over the type behind the alias or the type of the item | |
| DefKind::TyAlias { .. } | |
| | DefKind::AssocTy | |
| | DefKind::Static { .. } | |
| | DefKind::Const | |
| | DefKind::AssocConst => return visit_alias(), | |
| DefKind::AnonConst | |
| if tcx.anon_const_kind(item) != ty::AnonConstKind::NonTypeSystemInline => | |
| { | |
| return visit_alias(); | |
| } | |
| // These are not part of a public API, they can only appear as hidden types, and there | |
| // the interesting parts are solely in the signature of the containing item's opaque type | |
| // or dyn type. | |
| DefKind::AnonConst | DefKind::Closure | DefKind::SyntheticCoroutineBody => {} | |
| DefKind::OpaqueTy => { | |
| for (pred, span) in tcx | |
| .explicit_item_bounds(item) | |
| .iter_identity_copied() | |
| .map(Unnormalized::skip_norm_wip) | |
| { | |
| try_visit!(visitor.visit(span, pred)); | |
| } | |
| } | |
| // Look at field types | |
| DefKind::Struct | DefKind::Union | DefKind::Enum => { | |
| let span = tcx.def_ident_span(item).unwrap(); | |
| let ty = tcx.type_of(item).instantiate_identity().skip_norm_wip(); | |
| try_visit!(visitor.visit(span, ty)); | |
| let ty::Adt(def, args) = ty.kind() else { | |
| span_bug!(span, "invalid type for {kind:?}: {:#?}", ty.kind()) | |
| }; | |
| for field in def.all_fields() { | |
| let span = tcx.def_ident_span(field.did).unwrap(); | |
| let ty = field.ty(tcx, args); | |
| try_visit!(visitor.visit(span, ty)); | |
| } | |
| for (clause, span) in tcx.explicit_clauses_of(item).instantiate_identity(tcx) { | |
| try_visit!(visitor.visit(span, clause.skip_norm_wip())); | |
| } | |
| } | |
| DefKind::Impl { of_trait } => { | |
| if of_trait { | |
| let span = tcx | |
| .hir_node_by_def_id(item) | |
| .expect_item() | |
| .expect_impl() | |
| .of_trait | |
| .unwrap() | |
| .trait_ref | |
| .path | |
| .span; | |
| let args = | |
| &tcx.impl_trait_ref(item).instantiate_identity().skip_norm_wip().args[1..]; | |
| try_visit!(visitor.visit(span, args)); | |
| } | |
| let span = match tcx.hir_node_by_def_id(item).ty() { | |
| Some(ty) => ty.span, | |
| _ => tcx.def_span(item), | |
| }; | |
| try_visit!( | |
| visitor.visit(span, tcx.type_of(item).instantiate_identity().skip_norm_wip()) | |
| ); | |
| for (clause, span) in tcx.explicit_clauses_of(item).instantiate_identity(tcx) { | |
| try_visit!(visitor.visit(span, clause.skip_norm_wip())); | |
| } | |
| } | |
| DefKind::TraitAlias | DefKind::Trait => { | |
| for (clause, span) in tcx.explicit_clauses_of(item).instantiate_identity(tcx) { | |
| try_visit!(visitor.visit(span, clause.skip_norm_wip())); | |
| } | |
| } | |
| DefKind::Variant | |
| | DefKind::TyParam | |
| | DefKind::ConstParam | |
| | DefKind::Ctor(_, _) | |
| | DefKind::Field | |
| | DefKind::LifetimeParam => { | |
| span_bug!( | |
| tcx.def_span(item), | |
| "{kind:?} has not seen any uses of `walk_types` yet, ping oli-obk if you'd like any help" | |
| ) | |
| } | |
| // These don't have any types, but are visited during privacy checking. | |
| DefKind::ExternCrate | |
| | DefKind::ForeignMod | |
| | DefKind::ForeignTy | |
| | DefKind::Macro(_) | |
| | DefKind::GlobalAsm | |
| | DefKind::Mod | |
| | DefKind::Use | |
| | DefKind::TestBinderConstraints => {} | |
| } | |
| V::Result::output() | |
| } |