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
130
131
132
//! 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;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::span_bug;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::Span;
use rustc_type_ir::visit::TypeVisitable;
use tracing::{instrument, trace};

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).iter_identity_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::SyntheticCoroutineBody => {}
        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()
}