1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! 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::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()
}