rapx/analysis/unsafety_isolation/
hir_visitor.rs

1use 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
10/// Maps `HirId` of a type to `BodyId` of related impls.
11pub 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                    //if let AssocItemKind::Fn(_) = impl_item_ref.kind {
44                    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                    //} else {
48                    //    None
49                    //}
50                }));
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        // We don't process items inside trait blocks
91    }
92
93    fn visit_impl_item(&mut self, _impl_item: &'tcx rustc_hir::ImplItem<'tcx>) {
94        // We don't process items inside impl blocks
95    }
96
97    fn visit_foreign_item(&mut self, _foreign_item: &'tcx rustc_hir::ForeignItem<'tcx>) {
98        // We don't process foreign items
99    }
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    // type NestedFilter = nested_filter::OnlyBodies;
135
136    //fn nested_visit_map(&mut self) -> Self::Map {
137    //    self.tcx.hir()
138    //}
139
140    fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
141        use rustc_hir::BlockCheckMode;
142        if let BlockCheckMode::UnsafeBlock(_unsafe_source) = block.rules {
143            // println!("{:?}",block.clone());
144            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
167/// (`DefId` of ADT) => Vec<(HirId of relevant impl block, impl_self_ty)>
168/// We use this map to quickly access associated impl blocks per ADT.
169/// `impl_self_ty` in the return value may differ from `tcx.type_of(ADT.DefID)`,
170/// as different instantiations of the same ADT are distinct `Ty`s.
171/// (e.g. Foo<i32, i64>, Foo<String, i32>)
172pub type AdtImplMap<'tcx> = FxHashMap<DefId, Vec<(DefId, Ty<'tcx>)>>;
173
174/// Create & initialize `AdtImplMap`.
175/// `AdtImplMap` is initialized before analysis of each crate,
176/// avoiding quadratic complexity of scanning all impl blocks for each ADT.
177pub 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}