use crate::plan::bodies::{ElseArm, IfPlan, LoopPlan, LoweredBlock, LoweredStatement};
pub(crate) fn validate(block: &LoweredBlock) -> Vec<String> {
let mut issues = Vec::new();
walk_block(block, &mut issues, "block");
issues
}
fn walk_block(block: &LoweredBlock, issues: &mut Vec<String>, path: &str) {
for (i, statement) in block.statements.iter().enumerate() {
walk_statement(statement, issues, &format!("{}.statements[{}]", path, i));
}
}
fn walk_statement(statement: &LoweredStatement, issues: &mut Vec<String>, path: &str) {
match statement {
LoweredStatement::If(plan) => walk_if(plan, issues, &format!("{}/If", path)),
LoweredStatement::Loop(plan) => walk_loop(plan, issues, &format!("{}/Loop", path)),
LoweredStatement::Block(body) => walk_block(body, issues, &format!("{}/Block", path)),
LoweredStatement::Break { .. } | LoweredStatement::Continue { .. } => {}
LoweredStatement::Const(_)
| LoweredStatement::Return(_)
| LoweredStatement::BreakValue(_)
| LoweredStatement::Let(_)
| LoweredStatement::Assign(_)
| LoweredStatement::Expression(_)
| LoweredStatement::TempBind { .. }
| LoweredStatement::VarDecl { .. }
| LoweredStatement::RawGo(_)
| LoweredStatement::DivergingRawGo(_)
| LoweredStatement::UnreachablePanic => {}
LoweredStatement::ClosureBind { body, .. } => {
walk_block(body, issues, &format!("{}/ClosureBind", path))
}
LoweredStatement::Match(plan) => walk_block(&plan.body, issues, &format!("{}/Match", path)),
LoweredStatement::Select(plan) => {
for (i, statement) in plan.setup.iter().enumerate() {
walk_statement(statement, issues, &format!("{}/Select.setup[{}]", path, i));
}
for (i, arm) in plan.arms.iter().enumerate() {
walk_block(arm.body(), issues, &format!("{}/Select.arms[{}]", path, i));
}
for (i, statement) in plan.postlude.iter().enumerate() {
walk_statement(
statement,
issues,
&format!("{}/Select.postlude[{}]", path, i),
);
}
}
LoweredStatement::Switch(plan) => {
for (i, case) in plan.cases.iter().enumerate() {
walk_block(&case.body, issues, &format!("{}/Switch.cases[{}]", path, i));
}
if let Some(default_body) = &plan.default {
walk_block(default_body, issues, &format!("{}/Switch.default", path));
}
for (i, statement) in plan.postlude.iter().enumerate() {
walk_statement(
statement,
issues,
&format!("{}/Switch.postlude[{}]", path, i),
);
}
}
LoweredStatement::WhileLet(plan) => {
walk_block(&plan.body, issues, &format!("{}/WhileLet", path))
}
}
}
fn walk_if(plan: &IfPlan, issues: &mut Vec<String>, path: &str) {
if plan.condition.is_empty() {
issues.push(format!("{}: empty condition", path));
}
walk_block(&plan.then_body, issues, &format!("{}.then", path));
walk_else(&plan.else_arm, issues, &format!("{}.else", path));
}
fn walk_else(arm: &ElseArm, issues: &mut Vec<String>, path: &str) {
match arm {
ElseArm::None => {}
ElseArm::ElseIf(plan) => walk_if(plan, issues, path),
ElseArm::Else { body, .. } => walk_block(body, issues, path),
}
}
fn walk_loop(plan: &LoopPlan, issues: &mut Vec<String>, path: &str) {
if plan.header.is_empty() {
issues.push(format!("{}: empty header", path));
}
walk_block(&plan.body, issues, &format!("{}.body", path));
}