rapx/analysis/safetyflow_analysis/
root.rs1use crate::helpers::mir_scan::{collect_global_local_pairs, get_rawptr_deref, get_unsafe_callees};
2use rustc_hir::{BodyId, ItemKind, def_id::DefId};
3use rustc_middle::{mir::Local, ty::TyCtxt};
4use rustc_span::Symbol;
5use std::collections::HashSet;
6
7use super::hir_visitor::ContainsUnsafe;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum UnsafeOpKind {
12 CallsUnsafeFn,
13 DerefsRawPtr,
14 AccessesStaticMut,
15}
16
17#[derive(Debug, Clone)]
22pub struct UnsafeRoot {
23 pub def_id: DefId,
24 pub kinds: Vec<UnsafeOpKind>,
25 pub unsafe_callees: HashSet<DefId>,
26 pub raw_ptr_locals: HashSet<Local>,
27 pub static_muts: HashSet<DefId>,
28}
29
30pub fn hir_contains_unsafe(tcx: TyCtxt<'_>, body_id: BodyId) -> bool {
36 let (fn_unsafe, block_unsafe) = ContainsUnsafe::contains_unsafe(tcx, body_id);
37 fn_unsafe || block_unsafe
38}
39
40pub fn has_struct_invariant(tcx: TyCtxt<'_>, struct_def_id: DefId) -> bool {
45 let Some(local_def_id) = struct_def_id.as_local() else {
46 return false;
47 };
48 let rapx = Symbol::intern("rapx");
49 let invariant = Symbol::intern("invariant");
50 let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(local_def_id));
51 attrs.iter().any(|attr| {
52 #[cfg(rapx_rustc_ge_193)]
53 if attr.is_doc_comment().is_some() {
54 return false;
55 }
56 #[cfg(not(rapx_rustc_ge_193))]
57 if attr.is_doc_comment() {
58 return false;
59 }
60 let path = attr.path();
61 path.len() == 2 && path[0] == rapx && path[1] == invariant
62 })
63}
64
65pub fn function_has_struct_invariant(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
67 let Some(assoc_item) = tcx.opt_associated_item(def_id) else {
68 return false;
69 };
70 let Some(impl_id) = assoc_item.impl_container(tcx) else {
71 return false;
72 };
73 let self_ty = tcx.type_of(impl_id).skip_binder();
74 match self_ty.kind() {
75 rustc_middle::ty::TyKind::Adt(adt_def, _) => has_struct_invariant(tcx, adt_def.did()),
76 _ => false,
77 }
78}
79
80pub fn function_has_trait_ensurance(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
84 let Some(assoc_item) = tcx.opt_associated_item(def_id) else {
85 return false;
86 };
87 let Some(impl_id) = assoc_item.impl_container(tcx) else {
88 return false;
89 };
90
91 let trait_def_id = {
92 #[cfg(rapx_rustc_ge_193)]
93 {
94 tcx.impl_opt_trait_ref(impl_id)
95 }
96 #[cfg(not(rapx_rustc_ge_193))]
97 {
98 tcx.impl_trait_ref(impl_id)
99 }
100 };
101 let Some(trait_ref) = trait_def_id else {
102 return false;
103 };
104 let trait_def_id = trait_ref.skip_binder().def_id;
105
106 let Some(local_id) = trait_def_id.as_local() else {
107 return false;
108 };
109
110 let item = tcx.hir_expect_item(local_id);
112 #[cfg(not(rapx_rustc_ge_198))]
113 if let ItemKind::Trait(_, _, unsafety, _, _, _, _) = &item.kind {
114 return matches!(unsafety, rustc_hir::Safety::Unsafe);
115 }
116 #[cfg(rapx_rustc_ge_198)]
117 if let ItemKind::Trait { safety, .. } = &item.kind {
118 return matches!(safety, rustc_hir::Safety::Unsafe);
119 }
120
121 false
122}
123
124pub fn scan_mir(tcx: TyCtxt<'_>, def_id: DefId) -> Option<UnsafeRoot> {
129 if !tcx.is_mir_available(def_id) {
130 return None;
131 }
132
133 let unsafe_callees = get_unsafe_callees(tcx, def_id);
134 let raw_ptr_locals = get_rawptr_deref(tcx, def_id);
135 let global_locals = collect_global_local_pairs(tcx, def_id);
136 let static_muts: HashSet<DefId> = global_locals.keys().copied().collect();
137
138 let global_locals_set: HashSet<Local> = global_locals.values().flatten().copied().collect();
139 let raw_ptr_locals: HashSet<Local> = raw_ptr_locals
140 .difference(&global_locals_set)
141 .copied()
142 .collect();
143
144 if unsafe_callees.is_empty() && raw_ptr_locals.is_empty() && static_muts.is_empty() {
145 return None;
146 }
147
148 let mut kinds = Vec::new();
149 if !unsafe_callees.is_empty() {
150 kinds.push(UnsafeOpKind::CallsUnsafeFn);
151 }
152 if !raw_ptr_locals.is_empty() {
153 kinds.push(UnsafeOpKind::DerefsRawPtr);
154 }
155 if !static_muts.is_empty() {
156 kinds.push(UnsafeOpKind::AccessesStaticMut);
157 }
158
159 Some(UnsafeRoot {
160 def_id,
161 kinds,
162 unsafe_callees,
163 raw_ptr_locals,
164 static_muts,
165 })
166}