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