use crate::ir::{Expr, Statement};
use super::expr_has_side_effects;
fn is_read_only_chain(expr: &Expr) -> bool {
if let Expr::MethodChain { calls, .. } = expr {
if let Some(last) = calls.last() {
return matches!(last.name.as_str(),
"unwrap_or" | "unwrap_or_default" | "get" | "has" | "len"
);
}
}
false
}
fn is_raw_expr(expr: &Expr) -> bool {
matches!(expr, Expr::Raw(_))
}
fn is_panic_only_body(stmts: &[Statement]) -> bool {
if stmts.is_empty() { return false; }
stmts.iter().all(|s| match s {
Statement::Expr(Expr::MacroCall { name, .. }) => name == "panic",
Statement::Expr(Expr::HostCall { name, .. }) => name == "panic",
_ => false,
})
}
pub fn eliminate_dead_vars(stmts: Vec<Statement>) -> Vec<Statement> {
let mut referenced = std::collections::HashSet::new();
for stmt in &stmts {
collect_stmt_refs(stmt, &mut referenced);
}
eliminate_dead_vars_with_refs(stmts, &referenced)
}
fn eliminate_dead_vars_with_refs(
stmts: Vec<Statement>,
referenced: &std::collections::HashSet<String>,
) -> Vec<Statement> {
stmts.into_iter().filter_map(|stmt| {
match stmt {
Statement::Let { name, value, mutable } => {
let check_name = name.replace('.', "_").replace('[', "_").replace(']', "");
if referenced.contains(&name) || referenced.contains(&check_name) {
return Some(Statement::Let { name, value, mutable });
}
if name.contains('.') {
let base = name.split('.').next().unwrap_or(&name);
if referenced.contains(base) {
return Some(Statement::Let { name, value, mutable });
}
}
if expr_has_side_effects(&value) {
Some(Statement::Expr(value))
} else {
None
}
}
Statement::Expr(ref e) if is_read_only_chain(e) => None,
Statement::If { condition, then_body, else_body } => {
if is_raw_expr(&condition) && is_panic_only_body(&then_body) && else_body.is_empty() {
return None;
}
let then_clean = eliminate_dead_vars_with_refs(then_body, referenced);
let else_clean = eliminate_dead_vars_with_refs(else_body, referenced);
if then_clean.is_empty() && else_clean.is_empty() {
None
} else {
Some(Statement::If {
condition,
then_body: then_clean,
else_body: else_clean,
})
}
}
Statement::While { condition, body } => {
Some(Statement::While {
condition,
body: eliminate_dead_vars_with_refs(body, referenced),
})
}
Statement::Loop { body } => {
Some(Statement::Loop {
body: eliminate_dead_vars_with_refs(body, referenced),
})
}
Statement::ForEach { var_name, collection, body } => {
Some(Statement::ForEach {
var_name,
collection,
body: eliminate_dead_vars_with_refs(body, referenced),
})
}
Statement::ForRange { var_name, bound, body } => {
Some(Statement::ForRange {
var_name,
bound,
body: eliminate_dead_vars_with_refs(body, referenced),
})
}
other => Some(other),
}
}).collect()
}
pub(super) fn collect_stmt_refs(stmt: &Statement, refs: &mut std::collections::HashSet<String>) {
match stmt {
Statement::Let { value, .. } => collect_expr_refs(value, refs),
Statement::Assign { target, value } => {
collect_expr_refs(target, refs);
collect_expr_refs(value, refs);
}
Statement::Expr(e) => collect_expr_refs(e, refs),
Statement::Return(Some(e)) => collect_expr_refs(e, refs),
Statement::Return(None) => {}
Statement::If { condition, then_body, else_body } => {
collect_expr_refs(condition, refs);
for s in then_body { collect_stmt_refs(s, refs); }
for s in else_body { collect_stmt_refs(s, refs); }
}
Statement::While { condition, body } => {
collect_expr_refs(condition, refs);
for s in body { collect_stmt_refs(s, refs); }
}
Statement::Loop { body } => {
for s in body { collect_stmt_refs(s, refs); }
}
Statement::ForEach { collection, body, .. } => {
collect_expr_refs(collection, refs);
for s in body { collect_stmt_refs(s, refs); }
}
Statement::ForRange { bound, body, .. } => {
collect_expr_refs(bound, refs);
for s in body { collect_stmt_refs(s, refs); }
}
}
}
fn collect_expr_refs(expr: &Expr, refs: &mut std::collections::HashSet<String>) {
match expr {
Expr::Var(name) => {
let bare = name.strip_prefix('&').unwrap_or(name);
refs.insert(bare.to_string());
if bare.contains('.') {
if let Some(base) = bare.split('.').next() {
refs.insert(base.to_string());
}
}
}
Expr::BinOp { left, right, .. } => {
collect_expr_refs(left, refs);
collect_expr_refs(right, refs);
}
Expr::UnOp { operand, .. } => collect_expr_refs(operand, refs),
Expr::MethodChain { receiver, calls } => {
collect_expr_refs(receiver, refs);
for call in calls {
for arg in &call.args { collect_expr_refs(arg, refs); }
}
}
Expr::HostCall { args, .. } => {
for arg in args { collect_expr_refs(arg, refs); }
}
Expr::MacroCall { args, .. } => {
for arg in args { collect_expr_refs(arg, refs); }
}
Expr::StructLiteral { fields, .. } => {
for (_, val) in fields { collect_expr_refs(val, refs); }
}
Expr::EnumVariant { fields, .. } => {
for f in fields { collect_expr_refs(f, refs); }
}
Expr::Ref(inner) => collect_expr_refs(inner, refs),
Expr::Literal(_) | Expr::Raw(_) => {}
}
}