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