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::span_bug;
11use rustc_middle::ty::{self, TyCtxt, TypeVisitable, Unnormalized, VisitorResult, try_visit};
12use rustc_span::Span;
13use tracing::{instrument, trace};
14
15pub trait SpannedTypeVisitor<'tcx> {
16    type Result: VisitorResult = ();
17    fn visit(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) -> Self::Result;
18}
19
20#[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("compiler/rustc_ty_walk/src/lib.rs"),
                                    ::tracing_core::__macro_support::Option::Some(20u32),
                                    ::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 compiler/rustc_ty_walk/src/lib.rs:27",
                                    "rustc_ty_walk", ::tracing::Level::TRACE,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_ty_walk/src/lib.rs"),
                                    ::tracing_core::__macro_support::Option::Some(27u32),
                                    ::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 {
                            ::rustc_middle::util::bug::span_bug_fmt(span,
                                format_args!("invalid type for {1:?}: {0:#?}", ty.kind(),
                                    kind))
                        };
                    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 => {
                    ::rustc_middle::util::bug::span_bug_fmt(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))
                }
                DefKind::ExternCrate | DefKind::ForeignMod |
                    DefKind::ForeignTy | DefKind::Macro(_) | DefKind::GlobalAsm
                    | DefKind::Mod | DefKind::Use => {}
            }
            V::Result::output()
        }
    }
}#[instrument(level = "trace", skip(tcx, visitor))]
21pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>(
22    tcx: TyCtxt<'tcx>,
23    item: LocalDefId,
24    visitor: &mut V,
25) -> V::Result {
26    let kind = tcx.def_kind(item);
27    trace!(?kind);
28    let mut visit_alias = || {
29        if let Some(ty) = tcx.hir_node_by_def_id(item).ty() {
30            // If the type of the item uses `_`, we're gonna error out anyway, but
31            // typeck (which type_of invokes below), will call back into opaque_types_defined_by
32            // causing a cycle. So we just bail out in this case.
33            if ty.is_suggestable_infer_ty() {
34                return V::Result::output();
35            }
36            // Associated types in traits don't necessarily have a type that we can visit
37            try_visit!(
38                visitor.visit(ty.span, tcx.type_of(item).instantiate_identity().skip_norm_wip())
39            );
40        }
41        for (clause, span) in tcx.explicit_clauses_of(item).instantiate_identity(tcx) {
42            try_visit!(visitor.visit(span, clause.skip_norm_wip()));
43        }
44        V::Result::output()
45    };
46    match kind {
47        // Walk over the signature of the function
48        DefKind::AssocFn | DefKind::Fn => {
49            let hir_sig = tcx.hir_node_by_def_id(item).fn_decl().unwrap();
50            // If the type of the item uses `_`, we're gonna error out anyway, but
51            // typeck (which type_of invokes below), will call back into opaque_types_defined_by
52            // causing a cycle. So we just bail out in this case.
53            if hir_sig.output.is_suggestable_infer_ty().is_some() {
54                return V::Result::output();
55            }
56            let ty_sig = tcx.fn_sig(item).instantiate_identity().skip_norm_wip();
57            // Walk over the inputs and outputs manually in order to get good spans for them.
58            try_visit!(visitor.visit(hir_sig.output.span(), ty_sig.output()));
59            for (hir, ty) in hir_sig.inputs.iter().zip(ty_sig.inputs().iter()) {
60                try_visit!(visitor.visit(hir.span, ty.map_bound(|x| *x)));
61            }
62            for (clause, span) in tcx.explicit_clauses_of(item).instantiate_identity(tcx) {
63                try_visit!(visitor.visit(span, clause.skip_norm_wip()));
64            }
65        }
66        // Walk over the type behind the alias or the type of the item
67        DefKind::TyAlias { .. }
68        | DefKind::AssocTy
69        | DefKind::Static { .. }
70        | DefKind::Const { .. }
71        | DefKind::AssocConst { .. } => return visit_alias(),
72        DefKind::AnonConst
73            if tcx.anon_const_kind(item) != ty::AnonConstKind::NonTypeSystemInline =>
74        {
75            return visit_alias();
76        }
77        // These are not part of a public API, they can only appear as hidden types, and there
78        // the interesting parts are solely in the signature of the containing item's opaque type
79        // or dyn type.
80        DefKind::AnonConst | DefKind::Closure | DefKind::SyntheticCoroutineBody => {}
81        DefKind::OpaqueTy => {
82            for (pred, span) in tcx
83                .explicit_item_bounds(item)
84                .iter_identity_copied()
85                .map(Unnormalized::skip_norm_wip)
86            {
87                try_visit!(visitor.visit(span, pred));
88            }
89        }
90        // Look at field types
91        DefKind::Struct | DefKind::Union | DefKind::Enum => {
92            let span = tcx.def_ident_span(item).unwrap();
93            let ty = tcx.type_of(item).instantiate_identity().skip_norm_wip();
94            try_visit!(visitor.visit(span, ty));
95            let ty::Adt(def, args) = ty.kind() else {
96                span_bug!(span, "invalid type for {kind:?}: {:#?}", ty.kind())
97            };
98            for field in def.all_fields() {
99                let span = tcx.def_ident_span(field.did).unwrap();
100                let ty = field.ty(tcx, args);
101                try_visit!(visitor.visit(span, ty));
102            }
103            for (clause, span) in tcx.explicit_clauses_of(item).instantiate_identity(tcx) {
104                try_visit!(visitor.visit(span, clause.skip_norm_wip()));
105            }
106        }
107        DefKind::Impl { of_trait } => {
108            if of_trait {
109                let span = tcx
110                    .hir_node_by_def_id(item)
111                    .expect_item()
112                    .expect_impl()
113                    .of_trait
114                    .unwrap()
115                    .trait_ref
116                    .path
117                    .span;
118                let args =
119                    &tcx.impl_trait_ref(item).instantiate_identity().skip_norm_wip().args[1..];
120                try_visit!(visitor.visit(span, args));
121            }
122            let span = match tcx.hir_node_by_def_id(item).ty() {
123                Some(ty) => ty.span,
124                _ => tcx.def_span(item),
125            };
126            try_visit!(
127                visitor.visit(span, tcx.type_of(item).instantiate_identity().skip_norm_wip())
128            );
129            for (clause, span) in tcx.explicit_clauses_of(item).instantiate_identity(tcx) {
130                try_visit!(visitor.visit(span, clause.skip_norm_wip()));
131            }
132        }
133        DefKind::TraitAlias | DefKind::Trait => {
134            for (clause, span) in tcx.explicit_clauses_of(item).instantiate_identity(tcx) {
135                try_visit!(visitor.visit(span, clause.skip_norm_wip()));
136            }
137        }
138        DefKind::Variant
139        | DefKind::TyParam
140        | DefKind::ConstParam
141        | DefKind::Ctor(_, _)
142        | DefKind::Field
143        | DefKind::LifetimeParam => {
144            span_bug!(
145                tcx.def_span(item),
146                "{kind:?} has not seen any uses of `walk_types` yet, ping oli-obk if you'd like any help"
147            )
148        }
149        // These don't have any types, but are visited during privacy checking.
150        DefKind::ExternCrate
151        | DefKind::ForeignMod
152        | DefKind::ForeignTy
153        | DefKind::Macro(_)
154        | DefKind::GlobalAsm
155        | DefKind::Mod
156        | DefKind::Use => {}
157    }
158    V::Result::output()
159}