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