rustc_hir_analysis/collect/
dump.rs

1use rustc_hir as hir;
2use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
3use rustc_hir::intravisit;
4use rustc_middle::hir::nested_filter;
5use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
6use rustc_span::sym;
7
8pub(crate) fn opaque_hidden_types(tcx: TyCtxt<'_>) {
9    if !tcx.has_attr(CRATE_DEF_ID, sym::rustc_hidden_type_of_opaques) {
10        return;
11    }
12
13    for id in tcx.hir_crate_items(()).opaques() {
14        if let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. }
15        | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. } =
16            tcx.hir().expect_opaque_ty(id).origin
17            && let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(fn_def_id)
18            && let (_, hir::TraitFn::Required(..)) = trait_item.expect_fn()
19        {
20            continue;
21        }
22
23        let ty = tcx.type_of(id).instantiate_identity();
24        let span = tcx.def_span(id);
25        tcx.dcx().emit_err(crate::errors::TypeOf { span, ty });
26    }
27}
28
29pub(crate) fn predicates_and_item_bounds(tcx: TyCtxt<'_>) {
30    for id in tcx.hir_crate_items(()).owners() {
31        if tcx.has_attr(id, sym::rustc_dump_predicates) {
32            let preds = tcx.predicates_of(id).instantiate_identity(tcx).predicates;
33            let span = tcx.def_span(id);
34
35            let mut diag = tcx.dcx().struct_span_err(span, sym::rustc_dump_predicates.as_str());
36            for pred in preds {
37                diag.note(format!("{pred:?}"));
38            }
39            diag.emit();
40        }
41        if tcx.has_attr(id, sym::rustc_dump_item_bounds) {
42            let bounds = tcx.item_bounds(id).instantiate_identity();
43            let span = tcx.def_span(id);
44
45            let mut diag = tcx.dcx().struct_span_err(span, sym::rustc_dump_item_bounds.as_str());
46            for bound in bounds {
47                diag.note(format!("{bound:?}"));
48            }
49            diag.emit();
50        }
51    }
52}
53
54pub(crate) fn def_parents(tcx: TyCtxt<'_>) {
55    for iid in tcx.hir().items() {
56        let did = iid.owner_id.def_id;
57        if tcx.has_attr(did, sym::rustc_dump_def_parents) {
58            struct AnonConstFinder<'tcx> {
59                tcx: TyCtxt<'tcx>,
60                anon_consts: Vec<LocalDefId>,
61            }
62
63            impl<'tcx> intravisit::Visitor<'tcx> for AnonConstFinder<'tcx> {
64                type NestedFilter = nested_filter::All;
65
66                fn nested_visit_map(&mut self) -> Self::Map {
67                    self.tcx.hir()
68                }
69
70                fn visit_anon_const(&mut self, c: &'tcx rustc_hir::AnonConst) {
71                    self.anon_consts.push(c.def_id);
72                    intravisit::walk_anon_const(self, c)
73                }
74            }
75
76            // Look for any anon consts inside of this item as there is no way to apply
77            // the `rustc_dump_def_parents` attribute to the anon const so it would not be possible
78            // to see what its def parent is.
79            let mut anon_ct_finder = AnonConstFinder { tcx, anon_consts: vec![] };
80            intravisit::walk_item(&mut anon_ct_finder, tcx.hir().item(iid));
81
82            for did in [did].into_iter().chain(anon_ct_finder.anon_consts) {
83                let span = tcx.def_span(did);
84
85                let mut diag = tcx.dcx().struct_span_err(
86                    span,
87                    format!("{}: {did:?}", sym::rustc_dump_def_parents.as_str()),
88                );
89
90                let mut current_did = did.to_def_id();
91                while let Some(parent_did) = tcx.opt_parent(current_did) {
92                    current_did = parent_did;
93                    diag.span_note(tcx.def_span(parent_did), format!("{parent_did:?}"));
94                }
95                diag.emit();
96            }
97        }
98    }
99}
100
101pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
102    for id in tcx.hir().items() {
103        let def_id = id.owner_id.def_id;
104
105        let Some(attr) = tcx.get_attr(def_id, sym::rustc_dump_vtable) else {
106            continue;
107        };
108
109        let vtable_entries = match tcx.hir().item(id).kind {
110            hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => {
111                let trait_ref = tcx.impl_trait_ref(def_id).unwrap().instantiate_identity();
112                if trait_ref.has_non_region_param() {
113                    tcx.dcx().span_err(
114                        attr.span,
115                        "`rustc_dump_vtable` must be applied to non-generic impl",
116                    );
117                    continue;
118                }
119                if !tcx.is_dyn_compatible(trait_ref.def_id) {
120                    tcx.dcx().span_err(
121                        attr.span,
122                        "`rustc_dump_vtable` must be applied to dyn-compatible trait",
123                    );
124                    continue;
125                }
126                let Ok(trait_ref) = tcx
127                    .try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref)
128                else {
129                    tcx.dcx().span_err(
130                        attr.span,
131                        "`rustc_dump_vtable` applied to impl header that cannot be normalized",
132                    );
133                    continue;
134                };
135                tcx.vtable_entries(trait_ref)
136            }
137            hir::ItemKind::TyAlias(_, _) => {
138                let ty = tcx.type_of(def_id).instantiate_identity();
139                if ty.has_non_region_param() {
140                    tcx.dcx().span_err(
141                        attr.span,
142                        "`rustc_dump_vtable` must be applied to non-generic type",
143                    );
144                    continue;
145                }
146                let Ok(ty) =
147                    tcx.try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), ty)
148                else {
149                    tcx.dcx().span_err(
150                        attr.span,
151                        "`rustc_dump_vtable` applied to type alias that cannot be normalized",
152                    );
153                    continue;
154                };
155                let ty::Dynamic(data, _, _) = *ty.kind() else {
156                    tcx.dcx().span_err(attr.span, "`rustc_dump_vtable` to type alias of dyn type");
157                    continue;
158                };
159                if let Some(principal) = data.principal() {
160                    tcx.vtable_entries(
161                        tcx.instantiate_bound_regions_with_erased(principal).with_self_ty(tcx, ty),
162                    )
163                } else {
164                    TyCtxt::COMMON_VTABLE_ENTRIES
165                }
166            }
167            _ => {
168                tcx.dcx().span_err(
169                    attr.span,
170                    "`rustc_dump_vtable` only applies to impl, or type alias of dyn type",
171                );
172                continue;
173            }
174        };
175
176        tcx.dcx().span_err(tcx.def_span(def_id), format!("vtable entries: {vtable_entries:#?}"));
177    }
178}