use super::{
EnumId, IrEnum, IrEnumVariant, IrExpr, IrField, IrFunction, IrImpl, IrImport, IrLet, IrModule,
IrStruct, IrTrait, StructId, TraitId,
};
pub trait IrVisitor {
fn visit_module(&mut self, module: &IrModule) {
walk_module_children(self, module);
}
fn visit_struct(&mut self, _id: StructId, _s: &IrStruct) {}
fn visit_trait(&mut self, _id: TraitId, _t: &IrTrait) {}
fn visit_enum(&mut self, _id: EnumId, _e: &IrEnum) {}
fn visit_enum_variant(&mut self, _v: &IrEnumVariant) {}
fn visit_impl(&mut self, _i: &IrImpl) {}
fn visit_function(&mut self, _f: &IrFunction) {}
fn visit_let(&mut self, _l: &IrLet) {}
fn visit_import(&mut self, _i: &IrImport) {}
fn visit_field(&mut self, _f: &IrField) {}
fn visit_expr(&mut self, e: &IrExpr) {
walk_expr_children(self, e);
}
}
pub fn walk_module<V: IrVisitor + ?Sized>(visitor: &mut V, module: &IrModule) {
visitor.visit_module(module);
}
pub fn walk_module_children<V: IrVisitor + ?Sized>(visitor: &mut V, module: &IrModule) {
for (idx, s) in module.structs.iter().enumerate() {
#[expect(
clippy::cast_possible_truncation,
reason = "checked by add_struct which errors before len reaches u32::MAX"
)]
visitor.visit_struct(StructId(idx as u32), s);
for field in &s.fields {
visitor.visit_field(field);
if let Some(default) = &field.default {
walk_expr(visitor, default);
}
}
}
for (idx, t) in module.traits.iter().enumerate() {
#[expect(
clippy::cast_possible_truncation,
reason = "checked by add_trait which errors before len reaches u32::MAX"
)]
visitor.visit_trait(TraitId(idx as u32), t);
for field in &t.fields {
visitor.visit_field(field);
}
}
for (idx, e) in module.enums.iter().enumerate() {
#[expect(
clippy::cast_possible_truncation,
reason = "checked by add_enum which errors before len reaches u32::MAX"
)]
visitor.visit_enum(EnumId(idx as u32), e);
for variant in &e.variants {
visitor.visit_enum_variant(variant);
for field in &variant.fields {
visitor.visit_field(field);
}
}
}
for i in &module.impls {
visitor.visit_impl(i);
for f in &i.functions {
visitor.visit_function(f);
if let Some(body) = &f.body {
walk_expr(visitor, body);
}
}
}
for f in &module.functions {
visitor.visit_function(f);
if let Some(body) = &f.body {
walk_expr(visitor, body);
}
}
for l in &module.lets {
visitor.visit_let(l);
walk_expr(visitor, &l.value);
}
for i in &module.imports {
visitor.visit_import(i);
}
}
pub fn walk_expr<V: IrVisitor + ?Sized>(visitor: &mut V, expr: &IrExpr) {
visitor.visit_expr(expr);
}
#[expect(
clippy::too_many_lines,
reason = "exhaustive walk over every IrExpr variant; splitting hides which variants have which children"
)]
pub fn walk_expr_children<V: IrVisitor + ?Sized>(visitor: &mut V, expr: &IrExpr) {
match expr {
IrExpr::Tuple { fields, .. } => {
for (_, e) in fields {
walk_expr(visitor, e);
}
}
IrExpr::StructInst { fields, .. } | IrExpr::EnumInst { fields, .. } => {
for (_, _, e) in fields {
walk_expr(visitor, e);
}
}
IrExpr::Array { elements, .. } => {
for e in elements {
walk_expr(visitor, e);
}
}
IrExpr::FieldAccess { object, .. } => {
walk_expr(visitor, object);
}
IrExpr::BinaryOp { left, right, .. } => {
walk_expr(visitor, left);
walk_expr(visitor, right);
}
IrExpr::UnaryOp { operand, .. } => {
walk_expr(visitor, operand);
}
IrExpr::If {
condition,
then_branch,
else_branch,
..
} => {
walk_expr(visitor, condition);
walk_expr(visitor, then_branch);
if let Some(e) = else_branch {
walk_expr(visitor, e);
}
}
IrExpr::For {
collection, body, ..
} => {
walk_expr(visitor, collection);
walk_expr(visitor, body);
}
IrExpr::Match {
scrutinee, arms, ..
} => {
walk_expr(visitor, scrutinee);
for arm in arms {
walk_expr(visitor, &arm.body);
}
}
IrExpr::FunctionCall { args, .. } => {
for (_, arg) in args {
walk_expr(visitor, arg);
}
}
IrExpr::CallClosure { closure, args, .. } => {
walk_expr(visitor, closure);
for (_, arg) in args {
walk_expr(visitor, arg);
}
}
IrExpr::MethodCall { receiver, args, .. } => {
walk_expr(visitor, receiver);
for (_, arg) in args {
walk_expr(visitor, arg);
}
}
IrExpr::DictLiteral { entries, .. } => {
for (k, v) in entries {
walk_expr(visitor, k);
walk_expr(visitor, v);
}
}
IrExpr::DictAccess { dict, key, .. } => {
walk_expr(visitor, dict);
walk_expr(visitor, key);
}
IrExpr::Block {
statements, result, ..
} => {
for stmt in statements {
walk_block_statement(visitor, stmt);
}
walk_expr(visitor, result);
}
IrExpr::Closure {
params: _,
captures: _,
body,
ty: _,
..
} => {
walk_expr(visitor, body);
}
IrExpr::ClosureRef { env_struct, .. } => {
walk_expr(visitor, env_struct);
}
IrExpr::Literal { .. }
| IrExpr::Reference { .. }
| IrExpr::SelfFieldRef { .. }
| IrExpr::LetRef { .. } => {}
}
}
pub fn walk_block_statement<V: IrVisitor + ?Sized>(
visitor: &mut V,
stmt: &crate::ir::IrBlockStatement,
) {
use crate::ir::IrBlockStatement;
match stmt {
IrBlockStatement::Let { value, .. } => {
walk_expr(visitor, value);
}
IrBlockStatement::Assign { target, value, .. } => {
walk_expr(visitor, target);
walk_expr(visitor, value);
}
IrBlockStatement::Expr(expr) => {
walk_expr(visitor, expr);
}
}
}