rapx/analysis/unsafety_isolation/
hir_visitor.rs1use rustc_data_structures::fx::FxHashMap;
2use rustc_hir::{
3 def_id::DefId, intravisit, intravisit::Visitor, Block, Body, BodyId, ExprKind, HirId, Impl,
4 ImplItemKind, ItemKind, QPath, TraitItemKind,
5};
6use rustc_middle::ty::{self, Ty, TyCtxt};
7use rustc_span::Span;
8use std::collections::HashSet;
9
10pub type RelatedItemMap = FxHashMap<Option<HirId>, Vec<(BodyId, Span)>>;
12
13pub struct RelatedFnCollector<'tcx> {
14 tcx: TyCtxt<'tcx>,
15 hash_map: RelatedItemMap,
16}
17
18impl<'tcx> RelatedFnCollector<'tcx> {
19 pub fn collect(tcx: TyCtxt<'tcx>) -> RelatedItemMap {
20 let mut collector = RelatedFnCollector {
21 tcx,
22 hash_map: RelatedItemMap::default(),
23 };
24
25 tcx.hir_visit_all_item_likes_in_crate(&mut collector);
26
27 collector.hash_map
28 }
29}
30
31impl<'tcx> Visitor<'tcx> for RelatedFnCollector<'tcx> {
32 fn visit_item(&mut self, item: &'tcx rustc_hir::Item<'tcx>) {
33 match &item.kind {
34 ItemKind::Impl(Impl {
35 generics: _generics,
36 self_ty,
37 items: impl_items,
38 ..
39 }) => {
40 let key = Some(self_ty.hir_id);
41 let entry = self.hash_map.entry(key).or_default();
42 entry.extend(impl_items.iter().filter_map(|impl_item_id| {
43 self.tcx
45 .hir_maybe_body_owned_by(impl_item_id.owner_id.def_id)
46 .map(|body| (body.id(), self.tcx.hir_impl_item(*impl_item_id).span))
47 }));
51 }
52 ItemKind::Trait(
53 _,
54 _is_auto,
55 _safety,
56 _ident,
57 _generics,
58 _generic_bounds,
59 trait_item_ids,
60 ) => {
61 let key = None;
62 let entry = self.hash_map.entry(key).or_default();
63 entry.extend(trait_item_ids.iter().filter_map(|trait_item_id| {
64 let trait_item = self.tcx.hir_trait_item(*trait_item_id);
65 if let TraitItemKind::Fn(_, _) = trait_item.kind {
66 self.tcx
67 .hir_maybe_body_owned_by(trait_item.owner_id.def_id)
68 .map(|body| (body.id(), trait_item.span))
69 } else {
70 None
71 }
72 }));
73 }
74 ItemKind::Fn {
75 sig: _,
76 generics: _,
77 body: body_id,
78 has_body: _,
79 ident: _,
80 } => {
81 let key = Some(body_id.hir_id);
82 let entry = self.hash_map.entry(key).or_default();
83 entry.push((*body_id, item.span));
84 }
85 _ => (),
86 }
87 }
88
89 fn visit_trait_item(&mut self, _trait_item: &'tcx rustc_hir::TraitItem<'tcx>) {
90 }
92
93 fn visit_impl_item(&mut self, _impl_item: &'tcx rustc_hir::ImplItem<'tcx>) {
94 }
96
97 fn visit_foreign_item(&mut self, _foreign_item: &'tcx rustc_hir::ForeignItem<'tcx>) {
98 }
100}
101
102pub struct ContainsUnsafe<'tcx> {
103 tcx: TyCtxt<'tcx>,
104 function_unsafe: bool,
105 block_unsafe: bool,
106}
107
108impl<'tcx> ContainsUnsafe<'tcx> {
109 pub fn contains_unsafe(tcx: TyCtxt<'tcx>, body_id: BodyId) -> (bool, bool) {
110 let mut visitor = ContainsUnsafe {
111 tcx,
112 function_unsafe: false,
113 block_unsafe: false,
114 };
115
116 let body = visitor.tcx.hir_body(body_id);
117 visitor.function_unsafe = visitor.body_unsafety(body);
118 visitor.visit_body(body);
119
120 (visitor.function_unsafe, visitor.block_unsafe)
121 }
122
123 fn body_unsafety(&self, body: &'tcx Body<'tcx>) -> bool {
124 let did = body.value.hir_id.owner.to_def_id();
125 let sig = self.tcx.fn_sig(did);
126 if let rustc_hir::Safety::Unsafe = sig.skip_binder().safety() {
127 return true;
128 }
129 false
130 }
131}
132
133impl<'tcx> Visitor<'tcx> for ContainsUnsafe<'tcx> {
134 fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
141 use rustc_hir::BlockCheckMode;
142 if let BlockCheckMode::UnsafeBlock(_unsafe_source) = block.rules {
143 self.block_unsafe = true;
145 }
146 intravisit::walk_block(self, block);
147 }
148}
149
150pub struct ContainsLit {
151 pub structs_used: HashSet<String>,
152}
153
154impl<'tcx> Visitor<'tcx> for ContainsLit {
155 fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) {
156 if let ExprKind::Struct(ref qpath, _, _) = expr.kind {
157 if let QPath::Resolved(_, path) = qpath {
158 if let Some(ident) = path.segments.last().map(|segment| segment.ident) {
159 self.structs_used.insert(ident.to_string());
160 }
161 }
162 }
163 intravisit::walk_expr(self, expr);
164 }
165}
166
167pub type AdtImplMap<'tcx> = FxHashMap<DefId, Vec<(DefId, Ty<'tcx>)>>;
173
174pub fn create_adt_impl_map(tcx: TyCtxt<'_>) -> AdtImplMap<'_> {
178 let mut map = FxHashMap::default();
179 for impl_item_id in tcx.hir_crate_items(()).impl_items() {
180 let impl_item = tcx.hir_impl_item(impl_item_id);
181 match impl_item.kind {
182 ImplItemKind::Type(ty) => {
183 let impl_self_ty = tcx.type_of(ty.hir_id.owner).skip_binder();
184 if let ty::Adt(impl_self_adt_def, _impl_substs) = impl_self_ty.kind() {
185 map.entry(impl_self_adt_def.did())
186 .or_insert_with(Vec::new)
187 .push((impl_item_id.owner_id.to_def_id(), impl_self_ty));
188 }
189 }
190 _ => (),
191 }
192 }
193 map
194}