blob: 2d8c78267d9fc491cc7f19c7495f1bf625b81140 [file] [log] [blame]
//! This module contains helpers for walking all types of
//! a signature, while preserving spans as much as possible
use rustc_ast_ir::try_visit;
use rustc_ast_ir::visit::VisitorResult;
use rustc_hir::{def::DefKind, def_id::LocalDefId};
use rustc_middle::span_bug;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::Span;
use rustc_type_ir::visit::TypeVisitable;
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);
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.get_infer_ret_ty().is_some() {
return V::Result::output();
}
let ty_sig = tcx.fn_sig(item).instantiate_identity();
// 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 (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) {
try_visit!(visitor.visit(span, pred));
}
}
// Walk over the type behind the alias
DefKind::TyAlias { .. } | DefKind::AssocTy |
// Walk over the type of the item
DefKind::Static { .. } | DefKind::Const | DefKind::AssocConst | DefKind::AnonConst => {
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()));
}
for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) {
try_visit!(visitor.visit(span, pred));
}
}
DefKind::OpaqueTy => {
for (pred, span) in tcx.explicit_item_bounds(item).instantiate_identity_iter_copied() {
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();
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 (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) {
try_visit!(visitor.visit(span, pred));
}
}
// 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::InlineConst | DefKind::Closure => {}
DefKind::Impl { of_trait } => {
if of_trait {
let span = tcx.hir_node_by_def_id(item).expect_item().expect_impl().of_trait.unwrap().path.span;
let args = &tcx.impl_trait_ref(item).unwrap().instantiate_identity().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()));
for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) {
try_visit!(visitor.visit(span, pred));
}
}
DefKind::TraitAlias | DefKind::Trait => {
for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) {
try_visit!(visitor.visit(span, pred));
}
}
| 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.
| DefKind::ExternCrate
| DefKind::ForeignMod
| DefKind::ForeignTy
| DefKind::Macro(_)
| DefKind::GlobalAsm
| DefKind::Mod
| DefKind::Use => {}
}
V::Result::output()
}