1//! This module contains helpers for walking all types of
2//! a signature, while preserving spans as much as possible
34use rustc_hir::def::DefKind;
5use rustc_hir::def_id::LocalDefId;
6use rustc_middle::span_bug;
7use rustc_middle::ty::{self, TyCtxt, TypeVisitable, VisitorResult, try_visit};
8use rustc_span::Span;
9use tracing::{instrument, trace};
1011pub trait SpannedTypeVisitor<'tcx> {
12type Result: VisitorResult = ();
13fn visit(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) -> Self::Result;
14}
1516#[instrument(level = "trace", skip(tcx, visitor))]
17pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>(
18 tcx: TyCtxt<'tcx>,
19 item: LocalDefId,
20 visitor: &mut V,
21) -> V::Result {
22let kind = tcx.def_kind(item);
23trace!(?kind);
24match kind {
25// Walk over the signature of the function
26DefKind::AssocFn | DefKind::Fn => {
27let hir_sig = tcx.hir_node_by_def_id(item).fn_decl().unwrap();
28// If the type of the item uses `_`, we're gonna error out anyway, but
29 // typeck (which type_of invokes below), will call back into opaque_types_defined_by
30 // causing a cycle. So we just bail out in this case.
31if hir_sig.output.is_suggestable_infer_ty().is_some() {
32return V::Result::output();
33 }
34let ty_sig = tcx.fn_sig(item).instantiate_identity();
35// Walk over the inputs and outputs manually in order to get good spans for them.
36try_visit!(visitor.visit(hir_sig.output.span(), ty_sig.output()));
37for (hir, ty) in hir_sig.inputs.iter().zip(ty_sig.inputs().iter()) {
38try_visit!(visitor.visit(hir.span, ty.map_bound(|x| *x)));
39 }
40for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) {
41try_visit!(visitor.visit(span, pred));
42 }
43 }
44// Walk over the type behind the alias
45DefKind::TyAlias { .. } | DefKind::AssocTy |
46// Walk over the type of the item
47DefKind::Static { .. } | DefKind::Const | DefKind::AssocConst | DefKind::AnonConst => {
48if let Some(ty) = tcx.hir_node_by_def_id(item).ty() {
49// If the type of the item uses `_`, we're gonna error out anyway, but
50 // typeck (which type_of invokes below), will call back into opaque_types_defined_by
51 // causing a cycle. So we just bail out in this case.
52if ty.is_suggestable_infer_ty() {
53return V::Result::output();
54 }
55// Associated types in traits don't necessarily have a type that we can visit
56try_visit!(visitor.visit(ty.span, tcx.type_of(item).instantiate_identity()));
57 }
58for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) {
59try_visit!(visitor.visit(span, pred));
60 }
61 }
62 DefKind::OpaqueTy => {
63for (pred, span) in tcx.explicit_item_bounds(item).iter_identity_copied() {
64try_visit!(visitor.visit(span, pred));
65 }
66 }
67// Look at field types
68DefKind::Struct | DefKind::Union | DefKind::Enum => {
69let span = tcx.def_ident_span(item).unwrap();
70let ty = tcx.type_of(item).instantiate_identity();
71try_visit!(visitor.visit(span, ty));
72let ty::Adt(def, args) = ty.kind() else {
73span_bug!(span, "invalid type for {kind:?}: {:#?}", ty.kind())
74 };
75for field in def.all_fields() {
76let span = tcx.def_ident_span(field.did).unwrap();
77let ty = field.ty(tcx, args);
78try_visit!(visitor.visit(span, ty));
79 }
80for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) {
81try_visit!(visitor.visit(span, pred));
82 }
83 }
84// These are not part of a public API, they can only appear as hidden types, and there
85 // the interesting parts are solely in the signature of the containing item's opaque type
86 // or dyn type.
87DefKind::InlineConst | DefKind::Closure | DefKind::SyntheticCoroutineBody => {}
88 DefKind::Impl { of_trait } => {
89if of_trait {
90let span = tcx.hir_node_by_def_id(item).expect_item().expect_impl().of_trait.unwrap().path.span;
91let args = &tcx.impl_trait_ref(item).unwrap().instantiate_identity().args[1..];
92try_visit!(visitor.visit(span, args));
93 }
94let span = match tcx.hir_node_by_def_id(item).ty() {
95Some(ty) => ty.span,
96_ => tcx.def_span(item),
97 };
98try_visit!(visitor.visit(span, tcx.type_of(item).instantiate_identity()));
99for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) {
100try_visit!(visitor.visit(span, pred));
101 }
102 }
103 DefKind::TraitAlias | DefKind::Trait => {
104for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) {
105try_visit!(visitor.visit(span, pred));
106 }
107 }
108 | DefKind::Variant
109 | DefKind::TyParam
110 | DefKind::ConstParam
111 | DefKind::Ctor(_, _)
112 | DefKind::Field
113 | DefKind::LifetimeParam => {
114span_bug!(
115 tcx.def_span(item),
116"{kind:?} has not seen any uses of `walk_types` yet, ping oli-obk if you'd like any help"
117)
118 }
119// These don't have any types.
120| DefKind::ExternCrate
121 | DefKind::ForeignMod
122 | DefKind::ForeignTy
123 | DefKind::Macro(_)
124 | DefKind::GlobalAsm
125 | DefKind::Mod
126 | DefKind::Use => {}
127 }
128 V::Result::output()
129}