mirsa-analysis 0.2.3

Analysis orchestration for mirsa
use mirsa_domains::interval::IntervalState;
use mirsa_domains::nullptr::NullPtrState;
use mirsa_framework::forward::DomainState;
use mirsa_framework::printer::StateEntries;
use mirsa_relations::symbolic::SymbolicState;
use rustc_middle::mir::Place;
use std::fmt;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CombinedState<'tcx> {
    pub symbolic: SymbolicState<'tcx>,
    pub interval: IntervalState<'tcx>,
    pub nullptr: NullPtrState<'tcx>,
    pub reduce_debug: bool,
}

impl<'tcx> CombinedState<'tcx> {
    pub fn new(interval: IntervalState<'tcx>, nullptr: NullPtrState<'tcx>) -> Self {
        Self::new_with_debug(interval, nullptr, false, false)
    }

    pub fn new_with_debug(
        interval: IntervalState<'tcx>,
        nullptr: NullPtrState<'tcx>,
        symbolic_debug: bool,
        reduce_debug: bool,
    ) -> Self {
        let mut symbolic = SymbolicState::new_with_debug(symbolic_debug);
        interval.merge_display_places_into(&mut symbolic);
        nullptr.merge_display_places_into(&mut symbolic);
        Self {
            symbolic,
            interval,
            nullptr,
            reduce_debug,
        }
    }

    pub fn from_parts(
        symbolic: SymbolicState<'tcx>,
        interval: IntervalState<'tcx>,
        nullptr: NullPtrState<'tcx>,
        reduce_debug: bool,
    ) -> Self {
        Self {
            symbolic,
            interval,
            nullptr,
            reduce_debug,
        }
    }

    pub fn debug_reduce(&self, args: fmt::Arguments<'_>) {
        if self.reduce_debug {
            eprintln!("[reduce] {args}");
        }
    }
}

impl<'tcx> DomainState<'tcx> for CombinedState<'tcx> {
    fn join(left: &Self, right: &Self) -> Self {
        let mut out = Self {
            symbolic: SymbolicState::join(&left.symbolic, &right.symbolic),
            interval: IntervalState::join(&left.interval, &right.interval),
            nullptr: NullPtrState::join(&left.nullptr, &right.nullptr),
            reduce_debug: left.reduce_debug || right.reduce_debug,
        };
        out.synchronize_domains();
        out
    }

    fn widen(previous: &Self, next: &Self) -> Self {
        let mut out = Self {
            symbolic: SymbolicState::join(&previous.symbolic, &next.symbolic),
            interval: IntervalState::widen(&previous.interval, &next.interval),
            nullptr: NullPtrState::widen(&previous.nullptr, &next.nullptr),
            reduce_debug: previous.reduce_debug || next.reduce_debug,
        };
        out.synchronize_domains();
        out
    }

    fn state_changed(previous: &Self, next: &Self) -> bool {
        IntervalState::state_changed(&previous.interval, &next.interval)
            || NullPtrState::state_changed(&previous.nullptr, &next.nullptr)
            || previous.symbolic != next.symbolic
            || previous.reduce_debug != next.reduce_debug
    }
}

impl<'tcx> fmt::Display for CombinedState<'tcx> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "interval: {{{}}}, nullptr: {{{}}}",
            self.interval, self.nullptr
        )
    }
}

impl<'tcx> StateEntries<'tcx> for CombinedState<'tcx> {
    fn entries(&self) -> Vec<(Place<'tcx>, String)> {
        let mut entries = Vec::new();
        entries.extend(
            self.interval
                .entries()
                .into_iter()
                .filter(|(place, _)| self.interval.should_print_entry(*place))
                .map(|(place, value)| (place, format!("interval {value}"))),
        );
        entries.extend(
            self.nullptr
                .entries()
                .into_iter()
                .filter(|(place, _)| self.nullptr.should_print_entry(*place))
                .map(|(place, value)| (place, format!("nullptr {value}"))),
        );
        entries
    }

    fn should_print_entry(&self, place: Place<'tcx>) -> bool {
        self.interval.should_print_entry(place) || self.nullptr.should_print_entry(place)
    }
}