rapx/analysis/upg/
hir_visitor.rs

1use rustc_data_structures::fx::FxHashMap;
2use rustc_hir::{
3    Block, BlockCheckMode, Body, BodyId, ExprKind, ImplItemKind, QPath, def_id::DefId, intravisit,
4    intravisit::Visitor,
5};
6use rustc_middle::ty::{self, Ty, TyCtxt};
7use std::collections::HashSet;
8
9pub struct ContainsUnsafe<'tcx> {
10    tcx: TyCtxt<'tcx>,
11    fn_unsafe: bool,
12    block_unsafe: bool,
13}
14
15impl<'tcx> ContainsUnsafe<'tcx> {
16    pub fn contains_unsafe(tcx: TyCtxt<'tcx>, body_id: BodyId) -> (bool, bool) {
17        let mut visitor = ContainsUnsafe {
18            tcx,
19            fn_unsafe: false,
20            block_unsafe: false,
21        };
22
23        let body = visitor.tcx.hir_body(body_id);
24        visitor.fn_unsafety(body);
25        visitor.visit_body(body);
26
27        (visitor.fn_unsafe, visitor.block_unsafe)
28    }
29
30    /*
31     * Check if the body corresponds to a function;
32     * If yes, check the function safety declaration based on its signature.
33     */
34    fn fn_unsafety(&mut self, body: &'tcx Body<'tcx>) {
35        let did = body.value.hir_id.owner.to_def_id();
36        if self.tcx.def_kind(did) == rustc_hir::def::DefKind::Fn
37            || self.tcx.def_kind(did) == rustc_hir::def::DefKind::AssocFn
38        {
39            let sig = self.tcx.fn_sig(did);
40            if let rustc_hir::Safety::Unsafe = sig.skip_binder().safety() {
41                self.fn_unsafe = true;
42                return;
43            }
44        }
45    }
46}
47
48/*
49 * Check if each block contains unsafe marker.
50 */
51impl<'tcx> Visitor<'tcx> for ContainsUnsafe<'tcx> {
52    fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
53        if let BlockCheckMode::UnsafeBlock(_unsafe_source) = block.rules {
54            self.block_unsafe = true;
55        }
56        intravisit::walk_block(self, block);
57    }
58}
59
60pub struct ContainsLit {
61    pub structs_used: HashSet<String>,
62}
63
64impl<'tcx> Visitor<'tcx> for ContainsLit {
65    fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) {
66        if let ExprKind::Struct(ref qpath, _, _) = expr.kind {
67            if let QPath::Resolved(_, path) = qpath {
68                if let Some(ident) = path.segments.last().map(|segment| segment.ident) {
69                    self.structs_used.insert(ident.to_string());
70                }
71            }
72        }
73        intravisit::walk_expr(self, expr);
74    }
75}
76
77/// (`DefId` of ADT) => Vec<(HirId of relevant impl block, impl_self_ty)>
78/// We use this map to quickly access associated impl blocks per ADT.
79/// `impl_self_ty` in the return value may differ from `tcx.type_of(ADT.DefID)`,
80/// as different instantiations of the same ADT are distinct `Ty`s.
81/// (e.g. Foo<i32, i64>, Foo<String, i32>)
82pub type AdtImplMap<'tcx> = FxHashMap<DefId, Vec<(DefId, Ty<'tcx>)>>;
83
84/// Create & initialize `AdtImplMap`.
85/// `AdtImplMap` is initialized before analysis of each crate,
86/// avoiding quadratic complexity of scanning all impl blocks for each ADT.
87pub fn create_adt_impl_map(tcx: TyCtxt<'_>) -> AdtImplMap<'_> {
88    let mut map = FxHashMap::default();
89    for impl_item_id in tcx.hir_crate_items(()).impl_items() {
90        let impl_item = tcx.hir_impl_item(impl_item_id);
91        match impl_item.kind {
92            ImplItemKind::Type(ty) => {
93                let impl_self_ty = tcx.type_of(ty.hir_id.owner).skip_binder();
94                if let ty::Adt(impl_self_adt_def, _impl_substs) = impl_self_ty.kind() {
95                    map.entry(impl_self_adt_def.did())
96                        .or_insert_with(Vec::new)
97                        .push((impl_item_id.owner_id.to_def_id(), impl_self_ty));
98                }
99            }
100            _ => (),
101        }
102    }
103    map
104}