Skip to main content

rustc_ty_walk/
lib.rs

1//! This crate contains a helper for walking all types of a signature, while preserving spans as
2//! much as possible
3
4// tidy-alphabetical-start
5#![feature(associated_type_defaults)]
6// tidy-alphabetical-end
7
8use rustc_hir::def::DefKind;
9use rustc_hir::def_id::LocalDefId;
10use rustc_middle::ty::{self, TyCtxt, TypeVisitable, Unnormalized, VisitorResult, try_visit};
11use rustc_span::{Span, span_bug};
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{}
#[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("walk_types",
                                    "rustc_ty_walk", ::tracing::Level::TRACE,
                                    ::tracing_core::__macro_support::Option::Some("/rustc-dev/923c95cdf5ba65cea505aa2ea829f578e1506ed8/compiler/rustc_ty_walk/src/lib.rs"),
                                    ::tracing_core::__macro_support::Option::Some(19u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_ty_walk"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("item")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("item");
                                                        NAME.as_str()
                                                    }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&item)
                                                            as &dyn ::tracing::field::Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: V::Result = loop {};
            return __tracing_attr_fake_return;
        }
        {
            let kind = tcx.def_kind(item);
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("event /rustc-dev/923c95cdf5ba65cea505aa2ea829f578e1506ed8/compiler/rustc_ty_walk/src/lib.rs:26",
                                    "rustc_ty_walk", ::tracing::Level::TRACE,
                                    ::tracing_core::__macro_support::Option::Some("/rustc-dev/923c95cdf5ba65cea505aa2ea829f578e1506ed8/compiler/rustc_ty_walk/src/lib.rs"),
                                    ::tracing_core::__macro_support::Option::Some(26u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_ty_walk"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("kind")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("kind");
                                                        NAME.as_str()
                                                    }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::EVENT)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let enabled =
                    ::tracing::Level::TRACE <=
                                ::tracing::level_filters::STATIC_MAX_LEVEL &&
                            ::tracing::Level::TRACE <=
                                ::tracing::level_filters::LevelFilter::current() &&
                        {
                            let interest = __CALLSITE.interest();
                            !interest.is_never() &&
                                ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                                    interest)
                        };
                if enabled {
                    (|value_set: ::tracing::field::ValueSet|
                                {
                                    let meta = __CALLSITE.metadata();
                                    ::tracing::Event::dispatch(meta, &value_set);
                                    ;
                                })({
                            #[allow(unused_imports)]
                            use ::tracing::field::{debug, display, Value};
                            __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&kind)
                                                        as &dyn ::tracing::field::Value))])
                        });
                } else { ; }
            };
            let mut visit_alias =
                ||
                    {
                        if let Some(ty) = tcx.hir_node_by_def_id(item).ty() {
                            if ty.is_suggestable_infer_ty() {
                                return V::Result::output();
                            }
                            match ::rustc_ast_ir::visit::VisitorResult::branch(visitor.visit(ty.span,
                                        tcx.type_of(item).instantiate_identity().skip_norm_wip())) {
                                core::ops::ControlFlow::Continue(()) =>
                                    (),
                                    #[allow(unreachable_code)]
                                    core::ops::ControlFlow::Break(r) => {
                                    return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
                                }
                            };
                        }
                        for (clause, span) in
                            tcx.explicit_clauses_of(item).instantiate_identity(tcx) {
                            match ::rustc_ast_ir::visit::VisitorResult::branch(visitor.visit(span,
                                        clause.skip_norm_wip())) {
                                core::ops::ControlFlow::Continue(()) =>
                                    (),
                                    #[allow(unreachable_code)]
                                    core::ops::ControlFlow::Break(r) => {
                                    return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
                                }
                            };
                        }
                        V::Result::output()
                    };
            match kind {
                DefKind::AssocFn | DefKind::Fn => {
                    let hir_sig =
                        tcx.hir_node_by_def_id(item).fn_decl().unwrap();
                    if hir_sig.output.is_suggestable_infer_ty().is_some() {
                        return V::Result::output();
                    }
                    let ty_sig =
                        tcx.fn_sig(item).instantiate_identity().skip_norm_wip();
                    match ::rustc_ast_ir::visit::VisitorResult::branch(visitor.visit(hir_sig.output.span(),
                                ty_sig.output())) {
                        core::ops::ControlFlow::Continue(()) =>
                            (),
                            #[allow(unreachable_code)]
                            core::ops::ControlFlow::Break(r) => {
                            return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
                        }
                    };
                    for (hir, ty) in
                        hir_sig.inputs.iter().zip(ty_sig.inputs().iter()) {
                        match ::rustc_ast_ir::visit::VisitorResult::branch(visitor.visit(hir.span,
                                    ty.map_bound(|x| *x))) {
                            core::ops::ControlFlow::Continue(()) =>
                                (),
                                #[allow(unreachable_code)]
                                core::ops::ControlFlow::Break(r) => {
                                return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
                            }
                        };
                    }
                    for (clause, span) in
                        tcx.explicit_clauses_of(item).instantiate_identity(tcx) {
                        match ::rustc_ast_ir::visit::VisitorResult::branch(visitor.visit(span,
                                    clause.skip_norm_wip())) {
                            core::ops::ControlFlow::Continue(()) =>
                                (),
                                #[allow(unreachable_code)]
                                core::ops::ControlFlow::Break(r) => {
                                return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
                            }
                        };
                    }
                }
                DefKind::TyAlias { .. } | DefKind::AssocTy | DefKind::Static {
                    .. } | DefKind::Const | DefKind::AssocConst =>
                    return visit_alias(),
                DefKind::AnonConst if
                    tcx.anon_const_kind(item) !=
                        ty::AnonConstKind::NonTypeSystemInline => {
                    return visit_alias();
                }
                DefKind::AnonConst | DefKind::Closure |
                    DefKind::SyntheticCoroutineBody => {}
                DefKind::OpaqueTy => {
                    for (pred, span) in
                        tcx.explicit_item_bounds(item).iter_identity_copied().map(Unnormalized::skip_norm_wip)
                        {
                        match ::rustc_ast_ir::visit::VisitorResult::branch(visitor.visit(span,
                                    pred)) {
                            core::ops::ControlFlow::Continue(()) =>
                                (),
                                #[allow(unreachable_code)]
                                core::ops::ControlFlow::Break(r) => {
                                return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
                            }
                        };
                    }
                }
                DefKind::Struct | DefKind::Union | DefKind::Enum => {
                    let span = tcx.def_ident_span(item).unwrap();
                    let ty =
                        tcx.type_of(item).instantiate_identity().skip_norm_wip();
                    match ::rustc_ast_ir::visit::VisitorResult::branch(visitor.visit(span,
                                ty)) {
                        core::ops::ControlFlow::Continue(()) =>
                            (),
                            #[allow(unreachable_code)]
                            core::ops::ControlFlow::Break(r) => {
                            return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
                        }
                    };
                    let ty::Adt(def, args) =
                        ty.kind() else {
                            bug_impl(Some(span),
                                format_args!("invalid type for {1:?}: {0:#?}", ty.kind(),
                                    kind), Location::caller())
                        };
                    for field in def.all_fields() {
                        let span = tcx.def_ident_span(field.did).unwrap();
                        let ty = field.ty(tcx, args);
                        match ::rustc_ast_ir::visit::VisitorResult::branch(visitor.visit(span,
                                    ty)) {
                            core::ops::ControlFlow::Continue(()) =>
                                (),
                                #[allow(unreachable_code)]
                                core::ops::ControlFlow::Break(r) => {
                                return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
                            }
                        };
                    }
                    for (clause, span) in
                        tcx.explicit_clauses_of(item).instantiate_identity(tcx) {
                        match ::rustc_ast_ir::visit::VisitorResult::branch(visitor.visit(span,
                                    clause.skip_norm_wip())) {
                            core::ops::ControlFlow::Continue(()) =>
                                (),
                                #[allow(unreachable_code)]
                                core::ops::ControlFlow::Break(r) => {
                                return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
                            }
                        };
                    }
                }
                DefKind::Impl { of_trait } => {
                    if of_trait {
                        let span =
                            tcx.hir_node_by_def_id(item).expect_item().expect_impl().of_trait.unwrap().trait_ref.path.span;
                        let args =
                            &tcx.impl_trait_ref(item).instantiate_identity().skip_norm_wip().args[1..];
                        match ::rustc_ast_ir::visit::VisitorResult::branch(visitor.visit(span,
                                    args)) {
                            core::ops::ControlFlow::Continue(()) =>
                                (),
                                #[allow(unreachable_code)]
                                core::ops::ControlFlow::Break(r) => {
                                return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
                            }
                        };
                    }
                    let span =
                        match tcx.hir_node_by_def_id(item).ty() {
                            Some(ty) => ty.span,
                            _ => tcx.def_span(item),
                        };
                    match ::rustc_ast_ir::visit::VisitorResult::branch(visitor.visit(span,
                                tcx.type_of(item).instantiate_identity().skip_norm_wip())) {
                        core::ops::ControlFlow::Continue(()) =>
                            (),
                            #[allow(unreachable_code)]
                            core::ops::ControlFlow::Break(r) => {
                            return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
                        }
                    };
                    for (clause, span) in
                        tcx.explicit_clauses_of(item).instantiate_identity(tcx) {
                        match ::rustc_ast_ir::visit::VisitorResult::branch(visitor.visit(span,
                                    clause.skip_norm_wip())) {
                            core::ops::ControlFlow::Continue(()) =>
                                (),
                                #[allow(unreachable_code)]
                                core::ops::ControlFlow::Break(r) => {
                                return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
                            }
                        };
                    }
                }
                DefKind::TraitAlias | DefKind::Trait => {
                    for (clause, span) in
                        tcx.explicit_clauses_of(item).instantiate_identity(tcx) {
                        match ::rustc_ast_ir::visit::VisitorResult::branch(visitor.visit(span,
                                    clause.skip_norm_wip())) {
                            core::ops::ControlFlow::Continue(()) =>
                                (),
                                #[allow(unreachable_code)]
                                core::ops::ControlFlow::Break(r) => {
                                return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
                            }
                        };
                    }
                }
                DefKind::Variant | DefKind::TyParam | DefKind::ConstParam |
                    DefKind::Ctor(_, _) | DefKind::Field |
                    DefKind::LifetimeParam => {
                    bug_impl(Some(tcx.def_span(item)),
                        format_args!("{0:?} has not seen any uses of `walk_types` yet, ping oli-obk if you\'d like any help",
                            kind), Location::caller())
                }
                DefKind::ExternCrate | DefKind::ForeignMod |
                    DefKind::ForeignTy | DefKind::Macro(_) | DefKind::GlobalAsm
                    | DefKind::Mod | DefKind::Use |
                    DefKind::TestBinderConstraints => {}
            }
            V::Result::output()
        }
    }
}#[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    let mut visit_alias = || {
28        if let Some(ty) = tcx.hir_node_by_def_id(item).ty() {
29            // If the type of the item uses `_`, we're gonna error out anyway, but
30            // typeck (which type_of invokes below), will call back into opaque_types_defined_by
31            // causing a cycle. So we just bail out in this case.
32            if ty.is_suggestable_infer_ty() {
33                return V::Result::output();
34            }
35            // Associated types in traits don't necessarily have a type that we can visit
36            try_visit!(
37                visitor.visit(ty.span, tcx.type_of(item).instantiate_identity().skip_norm_wip())
38            );
39        }
40        for (clause, span) in tcx.explicit_clauses_of(item).instantiate_identity(tcx) {
41            try_visit!(visitor.visit(span, clause.skip_norm_wip()));
42        }
43        V::Result::output()
44    };
45    match kind {
46        // Walk over the signature of the function
47        DefKind::AssocFn | DefKind::Fn => {
48            let hir_sig = tcx.hir_node_by_def_id(item).fn_decl().unwrap();
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 hir_sig.output.is_suggestable_infer_ty().is_some() {
53                return V::Result::output();
54            }
55            let ty_sig = tcx.fn_sig(item).instantiate_identity().skip_norm_wip();
56            // Walk over the inputs and outputs manually in order to get good spans for them.
57            try_visit!(visitor.visit(hir_sig.output.span(), ty_sig.output()));
58            for (hir, ty) in hir_sig.inputs.iter().zip(ty_sig.inputs().iter()) {
59                try_visit!(visitor.visit(hir.span, ty.map_bound(|x| *x)));
60            }
61            for (clause, span) in tcx.explicit_clauses_of(item).instantiate_identity(tcx) {
62                try_visit!(visitor.visit(span, clause.skip_norm_wip()));
63            }
64        }
65        // Walk over the type behind the alias or the type of the item
66        DefKind::TyAlias { .. }
67        | DefKind::AssocTy
68        | DefKind::Static { .. }
69        | DefKind::Const
70        | DefKind::AssocConst => return visit_alias(),
71        DefKind::AnonConst
72            if tcx.anon_const_kind(item) != ty::AnonConstKind::NonTypeSystemInline =>
73        {
74            return visit_alias();
75        }
76        // These are not part of a public API, they can only appear as hidden types, and there
77        // the interesting parts are solely in the signature of the containing item's opaque type
78        // or dyn type.
79        DefKind::AnonConst | DefKind::Closure | DefKind::SyntheticCoroutineBody => {}
80        DefKind::OpaqueTy => {
81            for (pred, span) in tcx
82                .explicit_item_bounds(item)
83                .iter_identity_copied()
84                .map(Unnormalized::skip_norm_wip)
85            {
86                try_visit!(visitor.visit(span, pred));
87            }
88        }
89        // Look at field types
90        DefKind::Struct | DefKind::Union | DefKind::Enum => {
91            let span = tcx.def_ident_span(item).unwrap();
92            let ty = tcx.type_of(item).instantiate_identity().skip_norm_wip();
93            try_visit!(visitor.visit(span, ty));
94            let ty::Adt(def, args) = ty.kind() else {
95                span_bug!(span, "invalid type for {kind:?}: {:#?}", ty.kind())
96            };
97            for field in def.all_fields() {
98                let span = tcx.def_ident_span(field.did).unwrap();
99                let ty = field.ty(tcx, args);
100                try_visit!(visitor.visit(span, ty));
101            }
102            for (clause, span) in tcx.explicit_clauses_of(item).instantiate_identity(tcx) {
103                try_visit!(visitor.visit(span, clause.skip_norm_wip()));
104            }
105        }
106        DefKind::Impl { of_trait } => {
107            if of_trait {
108                let span = tcx
109                    .hir_node_by_def_id(item)
110                    .expect_item()
111                    .expect_impl()
112                    .of_trait
113                    .unwrap()
114                    .trait_ref
115                    .path
116                    .span;
117                let args =
118                    &tcx.impl_trait_ref(item).instantiate_identity().skip_norm_wip().args[1..];
119                try_visit!(visitor.visit(span, args));
120            }
121            let span = match tcx.hir_node_by_def_id(item).ty() {
122                Some(ty) => ty.span,
123                _ => tcx.def_span(item),
124            };
125            try_visit!(
126                visitor.visit(span, tcx.type_of(item).instantiate_identity().skip_norm_wip())
127            );
128            for (clause, span) in tcx.explicit_clauses_of(item).instantiate_identity(tcx) {
129                try_visit!(visitor.visit(span, clause.skip_norm_wip()));
130            }
131        }
132        DefKind::TraitAlias | DefKind::Trait => {
133            for (clause, span) in tcx.explicit_clauses_of(item).instantiate_identity(tcx) {
134                try_visit!(visitor.visit(span, clause.skip_norm_wip()));
135            }
136        }
137        DefKind::Variant
138        | DefKind::TyParam
139        | DefKind::ConstParam
140        | DefKind::Ctor(_, _)
141        | DefKind::Field
142        | DefKind::LifetimeParam => {
143            span_bug!(
144                tcx.def_span(item),
145                "{kind:?} has not seen any uses of `walk_types` yet, ping oli-obk if you'd like any help"
146            )
147        }
148        // These don't have any types, but are visited during privacy checking.
149        DefKind::ExternCrate
150        | DefKind::ForeignMod
151        | DefKind::ForeignTy
152        | DefKind::Macro(_)
153        | DefKind::GlobalAsm
154        | DefKind::Mod
155        | DefKind::Use
156        | DefKind::TestBinderConstraints => {}
157    }
158    V::Result::output()
159}