use std::rc::Rc;
use foldhash::HashMap;
use foldhash::HashSet;
use mago_word::Word;
use mago_word::WordMap;
use mago_word::WordSet;
use mago_algebra::assertion_set::AssertionSet;
use mago_codex::reference::SymbolReferences;
use mago_codex::ttype::union::TUnion;
use mago_span::HasSpan;
use crate::context::scope::case_scope::CaseScope;
use crate::context::scope::loop_scope::LoopScope;
use crate::readonly::PendingReadonlyPropertyWrite;
#[derive(Debug, Clone)]
pub struct ClosureBindScope {
pub class_name: Option<Word>,
pub has_this: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ResolvedMethodCall {
pub span: (u32, u32),
pub class: Word,
pub method: Word,
}
#[derive(Debug, Clone)]
pub struct AnalysisArtifacts {
pub expression_types: HashMap<(u32, u32), Rc<TUnion>>,
pub if_true_assertions: HashMap<(u32, u32), WordMap<AssertionSet>>,
pub if_false_assertions: HashMap<(u32, u32), WordMap<AssertionSet>>,
pub true_branch_only_assertions: HashMap<(u32, u32), WordMap<AssertionSet>>,
pub inferred_return_types: Vec<Rc<TUnion>>,
pub inferred_yield_key_types: Vec<TUnion>,
pub inferred_yield_value_types: Vec<TUnion>,
pub symbol_references: SymbolReferences,
pub loop_scope: Option<LoopScope>,
pub case_scopes: Vec<CaseScope>,
pub fully_matched_switch_offsets: HashSet<u32>,
pub inferred_parameter_types: Option<HashMap<usize, TUnion>>,
pub method_initialized_properties: HashMap<(Word, Word), WordSet>,
pub method_calls_this_methods: HashMap<(Word, Word), HashSet<Word>>,
pub method_calls_parent_constructor: HashMap<(Word, Word), bool>,
pub method_calls_parent_initializer: HashMap<(Word, Word), Word>,
pub closure_bind_scope: Option<ClosureBindScope>,
pub resolved_method_calls: Vec<ResolvedMethodCall>,
pub(crate) pending_readonly_property_writes: Vec<PendingReadonlyPropertyWrite>,
}
impl Default for AnalysisArtifacts {
fn default() -> Self {
Self::new()
}
}
impl AnalysisArtifacts {
#[must_use]
pub fn new() -> Self {
Self {
expression_types: HashMap::default(),
inferred_return_types: Vec::new(),
inferred_yield_key_types: Vec::new(),
inferred_yield_value_types: Vec::new(),
if_true_assertions: HashMap::default(),
if_false_assertions: HashMap::default(),
true_branch_only_assertions: HashMap::default(),
symbol_references: SymbolReferences::new(),
case_scopes: Vec::new(),
loop_scope: None,
fully_matched_switch_offsets: HashSet::default(),
inferred_parameter_types: None,
method_initialized_properties: HashMap::default(),
method_calls_this_methods: HashMap::default(),
method_calls_parent_constructor: HashMap::default(),
method_calls_parent_initializer: HashMap::default(),
closure_bind_scope: None,
resolved_method_calls: Vec::new(),
pending_readonly_property_writes: Vec::new(),
}
}
pub(crate) fn set_loop_scope(&mut self, loop_scope: LoopScope) {
let previous_scope = self.loop_scope.take().map(Box::new);
self.loop_scope = Some(loop_scope.with_parent_loop(previous_scope));
}
pub(crate) unsafe fn take_loop_scope_unchecked(&mut self) -> LoopScope {
let mut loop_scope = unsafe {
self.loop_scope.take().unwrap_unchecked()
};
match loop_scope.parent_loop.take() {
Some(parent_loop) => {
self.loop_scope = Some(*parent_loop);
}
None => {
self.loop_scope = None;
}
}
loop_scope
}
pub(crate) fn get_loop_scope_mut(&mut self) -> Option<&mut LoopScope> {
self.loop_scope.as_mut()
}
#[inline]
pub fn set_expression_type<T>(&mut self, expression: &T, t: TUnion)
where
T: HasSpan,
{
self.expression_types.insert(get_expression_range(expression), Rc::new(t));
}
#[inline]
pub fn get_expression_type<T>(&self, expression: &T) -> Option<&TUnion>
where
T: HasSpan,
{
let t = self.expression_types.get(&get_expression_range(expression))?;
Some(&**t)
}
#[inline]
pub fn set_rc_expression_type<T>(&mut self, expression: &T, t: Rc<TUnion>)
where
T: HasSpan,
{
self.expression_types.insert(get_expression_range(expression), t);
}
#[inline]
pub fn get_rc_expression_type<T>(&self, expression: &T) -> Option<&Rc<TUnion>>
where
T: HasSpan,
{
self.expression_types.get(&get_expression_range(expression))
}
}
#[inline]
pub fn get_expression_range<T>(expression: &T) -> (u32, u32)
where
T: HasSpan,
{
let span = expression.span();
(span.start.offset, span.end.offset)
}