rapx 0.7.63

A static analysis platform for Rust program analysis and verification
//! APIs that can lack MIR and the unavailable `DefId`s in this compilation.

use std::collections::HashSet;
use std::sync::OnceLock;

use rustc_hir::def_id::DefId;
use rustc_middle::ty::TyCtxt;
use rustc_public::{CrateDef, rustc_internal};

use super::intrinsic_api;

const PATHS: [&[&str]; 5] = [
    &[
        "std::ops::FnMut::call_mut",
        "core::ops::FnMut::call_mut",
        "std::ops::function::FnMut::call_mut",
        "core::ops::function::FnMut::call_mut",
    ],
    &["std::clone::Clone::clone", "core::clone::Clone::clone"],
    &[
        "std::ffi::CString::from_raw",
        "alloc::ffi::CString::from_raw",
        "std::ffi::c_str::CString::from_raw",
        "alloc::ffi::c_str::CString::from_raw",
    ],
    &[
        "std::ffi::CString::from_vec_with_nul_unchecked",
        "alloc::ffi::CString::from_vec_with_nul_unchecked",
        "std::ffi::c_str::CString::from_vec_with_nul_unchecked",
        "alloc::ffi::c_str::CString::from_vec_with_nul_unchecked",
    ],
    &[
        "std::slice::<impl [T]>::to_vec_in::ConvertVec::to_vec",
        "alloc::slice::<impl [T]>::to_vec_in::ConvertVec::to_vec",
    ],
];

struct NoMirIds {
    known: [Option<DefId>; 5],
    unavailable: HashSet<DefId>,
}

static IDS: OnceLock<NoMirIds> = OnceLock::new();

pub fn init(tcx: TyCtxt<'_>) {
    IDS.get_or_init(|| {
        let mut known = [None; 5];
        let mut unavailable = HashSet::new();
        for krate in std::iter::once(rustc_public::local_crate())
            .chain(rustc_public::external_crates())
            .filter(|krate| {
                krate.is_local || matches!(krate.name.as_str(), "core" | "std" | "alloc")
            })
        {
            for function in krate.fn_defs() {
                let id = rustc_internal::internal(tcx, function.def_id());
                if !tcx.is_mir_available(id) && !intrinsic_api::is_intrinsic(tcx, id) {
                    unavailable.insert(id);
                }
                if matches!(krate.name.as_str(), "core" | "std" | "alloc") {
                    let name = function.name();
                    for (index, paths) in PATHS.iter().enumerate() {
                        if paths.iter().any(|path| {
                            name == *path
                                || path
                                    .split_once("::")
                                    .is_some_and(|(_, suffix)| name == suffix)
                        }) {
                            known[index].get_or_insert(id);
                        }
                    }
                }
            }
        }
        NoMirIds { known, unavailable }
    });
}

fn known(index: usize) -> Option<DefId> {
    IDS.get()
        .expect("No-MIR API IDs have not been initialized")
        .known[index]
}

pub fn is_no_mir(callee: Option<DefId>) -> bool {
    callee.is_some_and(|id| {
        IDS.get()
            .expect("No-MIR API IDs have not been initialized")
            .unavailable
            .contains(&id)
    })
}

pub fn call_mut() -> Option<DefId> {
    known(0)
}

pub fn clone() -> Option<DefId> {
    known(1)
}

pub fn cstring_from_raw() -> Option<DefId> {
    known(2)
}

pub fn cstring_from_vec_with_nul_unchecked() -> Option<DefId> {
    known(3)
}

pub fn slice_to_vec() -> Option<DefId> {
    known(4)
}

pub fn is_slice_to_vec(callee: Option<DefId>) -> bool {
    is_no_mir(callee) && callee == slice_to_vec()
}