Enforce exhaustive matches for AST nodes
This commit addresses the critical rule regarding exhaustive `match` expressions on AST node kinds. By explicitly listing all variants instead of using wildcards (`_ =>`), we ensure that new AST node types are consciously handled throughout the compilation pipeline. This practice prevents subtle bugs and makes the compiler more robust against future changes. The `CLAUDE.md` file has been updated to reflect this critical rule. The deleted `BUG_program_node_typeinference.md` file indicates that a previously identified bug related to type inference with `Program` nodes has been resolved, likely as a consequence of enforcing these exhaustive matches.
This commit is contained in:
+49
-13
@@ -62,17 +62,14 @@ impl VMObserver for TracingObserver {
|
||||
fn after_eval(&mut self, vm: &VM, node: &ExecNode, res: &Result<Value, String>) {
|
||||
self.indent = self.indent.saturating_sub(1);
|
||||
let pad = self.pad();
|
||||
match &node.kind {
|
||||
NodeKind::Def { .. } | NodeKind::Assign { .. } => {
|
||||
let s_pad = format!("{}| ", pad);
|
||||
self.logs.push(format!("{}--- Scope Status ---", s_pad));
|
||||
self.logs.push(format!(
|
||||
"{}Stack (top 5): {:?}",
|
||||
s_pad,
|
||||
vm.stack.iter().rev().take(5).collect::<Vec<_>>()
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
if matches!(&node.kind, NodeKind::Def { .. } | NodeKind::Assign { .. }) {
|
||||
let s_pad = format!("{}| ", pad);
|
||||
self.logs.push(format!("{}--- Scope Status ---", s_pad));
|
||||
self.logs.push(format!(
|
||||
"{}Stack (top 5): {:?}",
|
||||
s_pad,
|
||||
vm.stack.iter().rev().take(5).collect::<Vec<_>>()
|
||||
));
|
||||
}
|
||||
let res_str = match res {
|
||||
Ok(v) => format!("{}", v),
|
||||
@@ -864,7 +861,26 @@ impl VM {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
_ => {
|
||||
// All other variants: evaluate and push result(s) onto stack.
|
||||
NodeKind::Nop
|
||||
| NodeKind::Identifier { .. }
|
||||
| NodeKind::FieldAccessor(_)
|
||||
| NodeKind::Def { .. }
|
||||
| NodeKind::Assign { .. }
|
||||
| NodeKind::Lambda { .. }
|
||||
| NodeKind::Call { .. }
|
||||
| NodeKind::Again { .. }
|
||||
| NodeKind::If { .. }
|
||||
| NodeKind::Block { .. }
|
||||
| NodeKind::Program { .. }
|
||||
| NodeKind::Record { .. }
|
||||
| NodeKind::GetField { .. }
|
||||
| NodeKind::Extension(_)
|
||||
| NodeKind::Error
|
||||
| NodeKind::MacroDecl { .. }
|
||||
| NodeKind::Template(_)
|
||||
| NodeKind::Placeholder(_)
|
||||
| NodeKind::Splice(_) => {
|
||||
let val = self.eval_internal(obs, args)?;
|
||||
if let Some(slice) = val.as_slice() {
|
||||
self.stack.extend_from_slice(slice);
|
||||
@@ -1014,7 +1030,27 @@ impl VM {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
_ => Err("Invalid node in parameter pattern".to_string()),
|
||||
// Parameter patterns should only contain Identifier and Tuple.
|
||||
NodeKind::Nop
|
||||
| NodeKind::Constant(_)
|
||||
| NodeKind::FieldAccessor(_)
|
||||
| NodeKind::Def { .. }
|
||||
| NodeKind::Assign { .. }
|
||||
| NodeKind::Lambda { .. }
|
||||
| NodeKind::Call { .. }
|
||||
| NodeKind::Again { .. }
|
||||
| NodeKind::If { .. }
|
||||
| NodeKind::Block { .. }
|
||||
| NodeKind::Program { .. }
|
||||
| NodeKind::Record { .. }
|
||||
| NodeKind::GetField { .. }
|
||||
| NodeKind::Expansion { .. }
|
||||
| NodeKind::Extension(_)
|
||||
| NodeKind::Error
|
||||
| NodeKind::MacroDecl { .. }
|
||||
| NodeKind::Template(_)
|
||||
| NodeKind::Placeholder(_)
|
||||
| NodeKind::Splice(_) => Err("Invalid node in parameter pattern".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user