use crate::helpers::fn_info::get_adt_def_id_by_adt_method;
use crate::helpers::mir_scan::{collect_global_local_pairs, get_rawptr_deref, get_unsafe_callees};
use crate::helpers::mir_utils::{has_rapx_attr, is_trait_unsafe};
use rustc_hir::{BodyId, def_id::DefId};
use rustc_middle::{mir::Local, ty::TyCtxt};
use rustc_span::Symbol;
use std::collections::HashSet;
use super::hir_visitor::ContainsUnsafe;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnsafeOpKind {
CallsUnsafeFn,
DerefsRawPtr,
AccessesStaticMut,
}
#[derive(Debug, Clone)]
pub struct UnsafeRoot {
pub def_id: DefId,
pub kinds: Vec<UnsafeOpKind>,
pub unsafe_callees: HashSet<DefId>,
pub raw_ptr_locals: HashSet<Local>,
pub static_muts: HashSet<DefId>,
}
pub fn hir_contains_unsafe(tcx: TyCtxt<'_>, body_id: BodyId) -> bool {
let (fn_unsafe, block_unsafe) = ContainsUnsafe::contains_unsafe(tcx, body_id);
fn_unsafe || block_unsafe
}
pub fn has_struct_invariant(tcx: TyCtxt<'_>, struct_def_id: DefId) -> bool {
let Some(local_def_id) = struct_def_id.as_local() else {
return false;
};
has_rapx_attr(tcx, local_def_id, Symbol::intern("invariant"))
}
pub fn function_has_struct_invariant(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
get_adt_def_id_by_adt_method(tcx, def_id)
.map(|struct_def_id| has_struct_invariant(tcx, struct_def_id))
.unwrap_or(false)
}
pub fn function_has_trait_ensurance(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
let Some(assoc_item) = tcx.opt_associated_item(def_id) else {
return false;
};
let Some(impl_id) = assoc_item.impl_container(tcx) else {
return false;
};
let Some(trait_ref) = tcx.impl_opt_trait_ref(impl_id) else {
return false;
};
is_trait_unsafe(tcx, trait_ref.skip_binder().def_id)
}
pub fn scan_mir(tcx: TyCtxt<'_>, def_id: DefId) -> Option<UnsafeRoot> {
if !tcx.is_mir_available(def_id) {
return None;
}
let unsafe_callees = get_unsafe_callees(tcx, def_id);
let raw_ptr_locals = get_rawptr_deref(tcx, def_id);
let global_locals = collect_global_local_pairs(tcx, def_id);
let static_muts: HashSet<DefId> = global_locals.keys().copied().collect();
let global_locals_set: HashSet<Local> = global_locals.values().flatten().copied().collect();
let raw_ptr_locals: HashSet<Local> = raw_ptr_locals
.difference(&global_locals_set)
.copied()
.collect();
if unsafe_callees.is_empty() && raw_ptr_locals.is_empty() && static_muts.is_empty() {
return None;
}
let mut kinds = Vec::new();
if !unsafe_callees.is_empty() {
kinds.push(UnsafeOpKind::CallsUnsafeFn);
}
if !raw_ptr_locals.is_empty() {
kinds.push(UnsafeOpKind::DerefsRawPtr);
}
if !static_muts.is_empty() {
kinds.push(UnsafeOpKind::AccessesStaticMut);
}
Some(UnsafeRoot {
def_id,
kinds,
unsafe_callees,
raw_ptr_locals,
static_muts,
})
}