rapx/analysis/unsafety_isolation/
hir_visitor.rs

1use 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
11/// Maps `HirId` of a type to `BodyId` of related impls.
12pub 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        // We don't process items inside trait blocks
85    }
86
87    fn visit_impl_item(&mut self, _impl_item: &'tcx rustc_hir::ImplItem<'tcx>) {
88        // We don't process items inside impl blocks
89    }
90
91    fn visit_foreign_item(&mut self, _foreign_item: &'tcx rustc_hir::ForeignItem<'tcx>) {
92        // We don't process foreign items
93    }
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    // type NestedFilter = nested_filter::OnlyBodies;
129
130    //fn nested_visit_map(&mut self) -> Self::Map {
131    //    self.tcx.hir()
132    //}
133
134    fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
135        use rustc_hir::BlockCheckMode;
136        if let BlockCheckMode::UnsafeBlock(_unsafe_source) = block.rules {
137            // println!("{:?}",block.clone());
138            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
161/// (`DefId` of ADT) => Vec<(HirId of relevant impl block, impl_self_ty)>
162/// We use this map to quickly access associated impl blocks per ADT.
163/// `impl_self_ty` in the return value may differ from `tcx.type_of(ADT.DefID)`,
164/// as different instantiations of the same ADT are distinct `Ty`s.
165/// (e.g. Foo<i32, i64>, Foo<String, i32>)
166pub type AdtImplMap<'tcx> = FxHashMap<DefId, Vec<(DefId, Ty<'tcx>)>>;
167
168/// Create & initialize `AdtImplMap`.
169/// `AdtImplMap` is initialized before analysis of each crate,
170/// avoiding quadratic complexity of scanning all impl blocks for each ADT.
171pub 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}