use crate::parser::*;
use super::Analyzer;
impl<'ast> Analyzer<'ast> {
pub(crate) fn for_loop_body_mutates_element_of_self_iterable(
&self,
pattern: &Pattern,
iterable: &Expression<'ast>,
body: &[&Statement<'ast>],
) -> bool {
let loop_vars = match pattern {
Pattern::Identifier(var) => vec![var.clone()],
Pattern::Tuple(patterns) => {
let mut vars = Vec::new();
for pat in patterns {
if let Pattern::Identifier(var) = pat {
vars.push(var.clone());
}
}
vars
}
_ => return false,
};
let peeled = Self::peel_ref_expr(iterable);
if !self.expression_traces_to_self(peeled) {
return false;
}
loop_vars.iter().any(|loop_var| {
body.iter()
.any(|s| self.statement_tree_mutates_binding(s, loop_var.as_str()))
})
}
pub(crate) fn assignment_target_starts_with_var(expr: &Expression, var: &str) -> bool {
match expr {
Expression::Identifier { name, .. } => name == var,
Expression::FieldAccess { object, .. } => {
Self::assignment_target_starts_with_var(object, var)
}
Expression::Index { object, .. } => {
Self::assignment_target_starts_with_var(object, var)
}
Expression::Unary {
op: UnaryOp::Deref,
operand,
..
} => Self::assignment_target_starts_with_var(operand, var),
_ => false,
}
}
pub(crate) fn statement_tree_mutates_binding(&self, stmt: &Statement, var: &str) -> bool {
match stmt {
Statement::Assignment { target, .. } => {
Self::assignment_target_starts_with_var(target, var)
}
Statement::If {
then_block,
else_block,
..
} => {
then_block
.iter()
.any(|s| self.statement_tree_mutates_binding(s, var))
|| else_block.as_ref().is_some_and(|block| {
block
.iter()
.any(|s| self.statement_tree_mutates_binding(s, var))
})
}
Statement::While { body, .. } | Statement::Loop { body, .. } => body
.iter()
.any(|s| self.statement_tree_mutates_binding(s, var)),
Statement::For {
pattern,
iterable,
body,
..
} => {
self.for_loop_body_mutates_element_of_self_iterable(pattern, iterable, body)
|| body
.iter()
.any(|s| self.statement_tree_mutates_binding(s, var))
}
Statement::Match { arms, .. } => arms
.iter()
.any(|arm| self.expression_tree_mutates_binding(arm.body, var)),
Statement::Expression { expr, .. } => self.expression_tree_mutates_binding(expr, var),
Statement::Let {
value, else_block, ..
} => {
self.expression_tree_mutates_binding(value, var)
|| else_block.as_ref().is_some_and(|block| {
block
.iter()
.any(|s| self.statement_tree_mutates_binding(s, var))
})
}
Statement::Return {
value: Some(expr), ..
} => self.expression_tree_mutates_binding(expr, var),
_ => false,
}
}
pub(crate) fn expression_tree_mutates_binding(&self, expr: &Expression, var: &str) -> bool {
match expr {
Expression::Block { statements, .. } => statements
.iter()
.any(|s| self.statement_tree_mutates_binding(s, var)),
_ => false,
}
}
pub(crate) fn expression_traces_to_self(&self, expr: &Expression) -> bool {
match expr {
Expression::FieldAccess { object, .. } => {
if let Expression::Identifier { name, .. } = &**object {
name == "self"
} else {
self.expression_traces_to_self(object)
}
}
Expression::Index { object, .. } => self.expression_traces_to_self(object),
_ => false,
}
}
}