rustc_ty_utils/
sig_types.rs

1//! This module contains helpers for walking all types of
2//! a signature, while preserving spans as much as possible
3
4use 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};
13
14pub trait SpannedTypeVisitor<'tcx> {
15    type Result: VisitorResult = ();
16    fn visit(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) -> Self::Result;
17}
18
19#[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 {
25    let kind = tcx.def_kind(item);
26    trace!(?kind);
27    match kind {
28        // Walk over the signature of the function
29        DefKind::AssocFn | DefKind::Fn => {
30            let 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.
34            if hir_sig.output.is_suggestable_infer_ty().is_some() {
35                return V::Result::output();
36            }
37            let ty_sig = tcx.fn_sig(item).instantiate_identity();
38            // Walk over the inputs and outputs manually in order to get good spans for them.
39            try_visit!(visitor.visit(hir_sig.output.span(), ty_sig.output()));
40            for (hir, ty) in hir_sig.inputs.iter().zip(ty_sig.inputs().iter()) {
41                try_visit!(visitor.visit(hir.span, ty.map_bound(|x| *x)));
42            }
43            for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) {
44                try_visit!(visitor.visit(span, pred));
45            }
46        }
47        // Walk over the type behind the alias
48        DefKind::TyAlias { .. } | DefKind::AssocTy |
49        // Walk over the type of the item
50        DefKind::Static { .. } | DefKind::Const | DefKind::AssocConst | DefKind::AnonConst => {
51            if 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.
55                if ty.is_suggestable_infer_ty() {
56                    return V::Result::output();
57                }
58                // Associated types in traits don't necessarily have a type that we can visit
59                try_visit!(visitor.visit(ty.span, tcx.type_of(item).instantiate_identity()));
60            }
61            for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) {
62                try_visit!(visitor.visit(span, pred));
63            }
64        }
65        DefKind::OpaqueTy => {
66            for (pred, span) in tcx.explicit_item_bounds(item).iter_identity_copied() {
67                try_visit!(visitor.visit(span, pred));
68            }
69        }
70        // Look at field types
71        DefKind::Struct | DefKind::Union | DefKind::Enum => {
72            let span = tcx.def_ident_span(item).unwrap();
73            let ty = tcx.type_of(item).instantiate_identity();
74            try_visit!(visitor.visit(span, ty));
75            let ty::Adt(def, args) = ty.kind() else {
76                span_bug!(span, "invalid type for {kind:?}: {:#?}", ty.kind())
77            };
78            for field in def.all_fields() {
79                let span = tcx.def_ident_span(field.did).unwrap();
80                let ty = field.ty(tcx, args);
81                try_visit!(visitor.visit(span, ty));
82            }
83            for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) {
84                try_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.
90        DefKind::InlineConst | DefKind::Closure | DefKind::SyntheticCoroutineBody => {}
91        DefKind::Impl { of_trait } => {
92            if of_trait {
93                let span = tcx.hir_node_by_def_id(item).expect_item().expect_impl().of_trait.unwrap().path.span;
94                let args = &tcx.impl_trait_ref(item).unwrap().instantiate_identity().args[1..];
95                try_visit!(visitor.visit(span, args));
96            }
97            let span = match tcx.hir_node_by_def_id(item).ty() {
98                Some(ty) => ty.span,
99                _ => tcx.def_span(item),
100            };
101            try_visit!(visitor.visit(span, tcx.type_of(item).instantiate_identity()));
102            for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) {
103                try_visit!(visitor.visit(span, pred));
104            }
105        }
106        DefKind::TraitAlias | DefKind::Trait => {
107            for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) {
108                try_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 => {
117            span_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}