rapx/analysis/safetyflow_analysis/
safetyflow_unit.rs

1use crate::helpers::fn_info::*;
2use rustc_hir::{Safety, def_id::DefId};
3use rustc_middle::mir::Local;
4use std::collections::HashSet;
5
6#[derive(Debug, Clone)]
7pub struct SafetyFlowUnit {
8    pub caller: FnInfo,
9    pub callees: HashSet<FnInfo>,
10    pub raw_ptrs: HashSet<Local>,
11    pub static_muts: HashSet<DefId>,
12    pub caller_cons: HashSet<FnInfo>,
13    pub mut_methods: HashSet<DefId>,
14}
15
16/// Counts of different unit categories collected across all SafetyFlow units.
17///
18/// Fields are indexed as per the original comment:
19///   [uf/um, sf-uf, sf-um, uf-uf, uf-um, um(sf)-uf, um(uf)-uf, um(sf)-um,
20///    um(uf)-um, sm(sf)-uf, sm(uf)-uf, sm(sf)-um, sm(uf)-um]
21#[derive(Debug, Clone, Default)]
22pub struct BasicUnitCounts {
23    /// uf/um — unsafe caller with no callees
24    pub unsafe_fn_or_method_no_callees: u32,
25    /// sf-uf — safe non-method caller calling unsafe non-method
26    pub safe_fn_call_unsafe_fn: u32,
27    /// sf-um — safe non-method caller calling unsafe method
28    pub safe_fn_call_unsafe_method: u32,
29    /// uf-uf — unsafe non-method caller calling unsafe non-method
30    pub unsafe_fn_call_unsafe_fn: u32,
31    /// uf-um — unsafe non-method caller calling unsafe method
32    pub unsafe_fn_call_unsafe_method: u32,
33    /// um(sf)-uf — unsafe method with safe cons calling unsafe non-method
34    pub unsafe_method_safe_cons_call_unsafe_fn: u32,
35    /// um(uf)-uf — unsafe method with unsafe cons calling unsafe non-method
36    pub unsafe_method_unsafe_cons_call_unsafe_fn: u32,
37    /// um(sf)-um — unsafe method with safe cons calling unsafe method
38    pub unsafe_method_safe_cons_call_unsafe_method: u32,
39    /// um(uf)-um — unsafe method with unsafe cons calling unsafe method
40    pub unsafe_method_unsafe_cons_call_unsafe_method: u32,
41    /// sm(sf)-uf — safe method with safe cons calling unsafe non-method
42    pub safe_method_safe_cons_call_unsafe_fn: u32,
43    /// sm(uf)-uf — safe method with unsafe cons calling unsafe non-method
44    pub safe_method_unsafe_cons_call_unsafe_fn: u32,
45    /// sm(sf)-um — safe method with safe cons calling unsafe method
46    pub safe_method_safe_cons_call_unsafe_method: u32,
47    /// sm(uf)-um — safe method with unsafe cons calling unsafe method
48    pub safe_method_unsafe_cons_call_unsafe_method: u32,
49}
50
51impl BasicUnitCounts {
52    pub fn total(&self) -> u32 {
53        self.unsafe_fn_or_method_no_callees
54            + self.safe_fn_call_unsafe_fn
55            + self.safe_fn_call_unsafe_method
56            + self.unsafe_fn_call_unsafe_fn
57            + self.unsafe_fn_call_unsafe_method
58            + self.unsafe_method_safe_cons_call_unsafe_fn
59            + self.unsafe_method_unsafe_cons_call_unsafe_fn
60            + self.unsafe_method_safe_cons_call_unsafe_method
61            + self.unsafe_method_unsafe_cons_call_unsafe_method
62            + self.safe_method_safe_cons_call_unsafe_fn
63            + self.safe_method_unsafe_cons_call_unsafe_fn
64            + self.safe_method_safe_cons_call_unsafe_method
65            + self.safe_method_unsafe_cons_call_unsafe_method
66    }
67}
68
69impl SafetyFlowUnit {
70    pub fn new(
71        caller: FnInfo,
72        callees: HashSet<FnInfo>,
73        raw_ptrs: HashSet<Local>,
74        static_muts: HashSet<DefId>,
75        caller_cons: HashSet<FnInfo>,
76        mut_methods: HashSet<DefId>,
77    ) -> Self {
78        Self {
79            caller,
80            callees,
81            raw_ptrs,
82            static_muts,
83            caller_cons,
84            mut_methods,
85        }
86    }
87
88    pub fn count_basic_units(&self, c: &mut BasicUnitCounts) {
89        if self.caller.fn_safety == Safety::Unsafe && self.callees.is_empty() {
90            c.unsafe_fn_or_method_no_callees += 1;
91        }
92        if self.caller.fn_safety == Safety::Safe && self.caller.fn_kind != FnKind::Method {
93            for callee in &self.callees {
94                if callee.fn_kind == FnKind::Method {
95                    c.safe_fn_call_unsafe_method += 1;
96                } else {
97                    c.safe_fn_call_unsafe_fn += 1;
98                }
99            }
100        }
101        if self.caller.fn_safety == Safety::Unsafe && self.caller.fn_kind != FnKind::Method {
102            for callee in &self.callees {
103                if callee.fn_kind == FnKind::Method {
104                    c.unsafe_fn_call_unsafe_method += 1;
105                } else {
106                    c.unsafe_fn_call_unsafe_fn += 1;
107                }
108            }
109        }
110        if self.caller.fn_safety == Safety::Unsafe && self.caller.fn_kind == FnKind::Method {
111            let mut unsafe_cons = 0;
112            let mut safe_cons = 0;
113            for cons in &self.caller_cons {
114                if cons.fn_safety == Safety::Unsafe {
115                    unsafe_cons += 1;
116                } else {
117                    safe_cons += 1;
118                }
119            }
120            if unsafe_cons == 0 && safe_cons == 0 {
121                safe_cons = 1;
122            }
123            for callee in &self.callees {
124                if callee.fn_kind == FnKind::Method {
125                    c.unsafe_method_safe_cons_call_unsafe_method += safe_cons;
126                    c.unsafe_method_unsafe_cons_call_unsafe_method += unsafe_cons;
127                } else {
128                    c.unsafe_method_safe_cons_call_unsafe_fn += safe_cons;
129                    c.unsafe_method_unsafe_cons_call_unsafe_fn += unsafe_cons;
130                }
131            }
132        }
133        if self.caller.fn_safety == Safety::Safe && self.caller.fn_kind == FnKind::Method {
134            let mut unsafe_cons = 0;
135            let mut safe_cons = 0;
136            for cons in &self.caller_cons {
137                if cons.fn_safety == Safety::Unsafe {
138                    unsafe_cons += 1;
139                } else {
140                    safe_cons += 1;
141                }
142            }
143            if unsafe_cons == 0 && safe_cons == 0 {
144                safe_cons = 1;
145            }
146            for callee in &self.callees {
147                if callee.fn_kind == FnKind::Method {
148                    c.safe_method_safe_cons_call_unsafe_method += safe_cons;
149                    c.safe_method_unsafe_cons_call_unsafe_method += unsafe_cons;
150                } else {
151                    c.safe_method_safe_cons_call_unsafe_fn += safe_cons;
152                    c.safe_method_unsafe_cons_call_unsafe_fn += unsafe_cons;
153                }
154            }
155        }
156    }
157}