1 use rustc_data_structures
::sync
::Lock
;
3 use rustc_hir
::def_id
::LocalDefId
;
4 use rustc_hir
::intravisit
;
5 use rustc_hir
::{HirId, ItemLocalId}
;
6 use rustc_index
::bit_set
::GrowableBitSet
;
7 use rustc_middle
::hir
::nested_filter
;
8 use rustc_middle
::ty
::{DefIdTree, TyCtxt}
;
10 pub fn check_crate(tcx
: TyCtxt
<'_
>) {
11 tcx
.dep_graph
.assert_ignored();
13 if tcx
.sess
.opts
.unstable_opts
.hir_stats
{
14 crate::hir_stats
::print_hir_stats(tcx
);
17 #[cfg(debug_assertions)]
19 let errors
= Lock
::new(Vec
::new());
21 tcx
.hir().par_for_each_module(|module_id
| {
22 let mut v
= HirIdValidator
{
25 hir_ids_seen
: Default
::default(),
29 tcx
.hir().visit_item_likes_in_module(module_id
, &mut v
);
32 let errors
= errors
.into_inner();
34 if !errors
.is_empty() {
35 let message
= errors
.iter().fold(String
::new(), |s1
, s2
| s1
+ "\n" + s2
);
36 tcx
.sess
.delay_span_bug(rustc_span
::DUMMY_SP
, &message
);
41 struct HirIdValidator
<'a
, 'hir
> {
43 owner
: Option
<hir
::OwnerId
>,
44 hir_ids_seen
: GrowableBitSet
<ItemLocalId
>,
45 errors
: &'a Lock
<Vec
<String
>>,
48 impl<'a
, 'hir
> HirIdValidator
<'a
, 'hir
> {
49 fn new_visitor(&self, tcx
: TyCtxt
<'hir
>) -> HirIdValidator
<'a
, 'hir
> {
50 HirIdValidator { tcx, owner: None, hir_ids_seen: Default::default(), errors: self.errors }
55 fn error(&self, f
: impl FnOnce() -> String
) {
56 self.errors
.lock().push(f());
59 fn check
<F
: FnOnce(&mut HirIdValidator
<'a
, 'hir
>)>(&mut self, owner
: hir
::OwnerId
, walk
: F
) {
60 assert
!(self.owner
.is_none());
61 self.owner
= Some(owner
);
64 if owner
== hir
::CRATE_OWNER_ID
{
68 // There's always at least one entry for the owning item itself
72 .map(|local_id
| local_id
.as_usize())
74 .expect("owning item has no entry");
76 if max
!= self.hir_ids_seen
.len() - 1 {
77 // Collect the missing ItemLocalIds
78 let missing
: Vec
<_
> = (0..=max
as u32)
79 .filter(|&i
| !self.hir_ids_seen
.contains(ItemLocalId
::from_u32(i
)))
82 // Try to map those to something more useful
83 let mut missing_items
= Vec
::with_capacity(missing
.len());
85 for local_id
in missing
{
86 let hir_id
= HirId { owner, local_id: ItemLocalId::from_u32(local_id) }
;
88 trace
!("missing hir id {:#?}", hir_id
);
90 missing_items
.push(format
!(
91 "[local_id: {}, owner: {}]",
93 self.tcx
.hir().def_path(owner
.def_id
).to_string_no_crate_verbose()
98 "ItemLocalIds not assigned densely in {}. \
99 Max ItemLocalId = {}, missing IDs = {:#?}; seens IDs = {:#?}",
100 self.tcx
.hir().def_path(owner
.def_id
).to_string_no_crate_verbose(),
105 .map(|local_id
| HirId { owner, local_id }
)
106 .map(|h
| format
!("({:?} {})", h
, self.tcx
.hir().node_to_string(h
)))
113 fn check_nested_id(&mut self, id
: LocalDefId
) {
114 let Some(owner
) = self.owner
else { return }
;
115 let def_parent
= self.tcx
.local_parent(id
);
116 let def_parent_hir_id
= self.tcx
.local_def_id_to_hir_id(def_parent
);
117 if def_parent_hir_id
.owner
!= owner
{
120 "inconsistent Def parent at `{:?}` for `{:?}`:\nexpected={:?}\nfound={:?}",
121 self.tcx
.def_span(id
),
131 impl<'a
, 'hir
> intravisit
::Visitor
<'hir
> for HirIdValidator
<'a
, 'hir
> {
132 type NestedFilter
= nested_filter
::OnlyBodies
;
134 fn nested_visit_map(&mut self) -> Self::Map
{
138 fn visit_nested_item(&mut self, id
: hir
::ItemId
) {
139 self.check_nested_id(id
.owner_id
.def_id
);
142 fn visit_nested_trait_item(&mut self, id
: hir
::TraitItemId
) {
143 self.check_nested_id(id
.owner_id
.def_id
);
146 fn visit_nested_impl_item(&mut self, id
: hir
::ImplItemId
) {
147 self.check_nested_id(id
.owner_id
.def_id
);
150 fn visit_nested_foreign_item(&mut self, id
: hir
::ForeignItemId
) {
151 self.check_nested_id(id
.owner_id
.def_id
);
154 fn visit_item(&mut self, i
: &'hir hir
::Item
<'hir
>) {
155 let mut inner_visitor
= self.new_visitor(self.tcx
);
156 inner_visitor
.check(i
.owner_id
, |this
| intravisit
::walk_item(this
, i
));
159 fn visit_id(&mut self, hir_id
: HirId
) {
160 let owner
= self.owner
.expect("no owner");
162 if owner
!= hir_id
.owner
{
165 "HirIdValidator: The recorded owner of {} is {} instead of {}",
166 self.tcx
.hir().node_to_string(hir_id
),
167 self.tcx
.hir().def_path(hir_id
.owner
.def_id
).to_string_no_crate_verbose(),
168 self.tcx
.hir().def_path(owner
.def_id
).to_string_no_crate_verbose()
173 self.hir_ids_seen
.insert(hir_id
.local_id
);
176 fn visit_foreign_item(&mut self, i
: &'hir hir
::ForeignItem
<'hir
>) {
177 let mut inner_visitor
= self.new_visitor(self.tcx
);
178 inner_visitor
.check(i
.owner_id
, |this
| intravisit
::walk_foreign_item(this
, i
));
181 fn visit_trait_item(&mut self, i
: &'hir hir
::TraitItem
<'hir
>) {
182 let mut inner_visitor
= self.new_visitor(self.tcx
);
183 inner_visitor
.check(i
.owner_id
, |this
| intravisit
::walk_trait_item(this
, i
));
186 fn visit_impl_item(&mut self, i
: &'hir hir
::ImplItem
<'hir
>) {
187 let mut inner_visitor
= self.new_visitor(self.tcx
);
188 inner_visitor
.check(i
.owner_id
, |this
| intravisit
::walk_impl_item(this
, i
));