diff --git a/src/mir/control_tree/step_tree.rs b/src/mir/control_tree/step_tree.rs index 4f7036b3..2e2bfca8 100644 --- a/src/mir/control_tree/step_tree.rs +++ b/src/mir/control_tree/step_tree.rs @@ -471,7 +471,7 @@ fn extract_facts_from_tree(root: &StepNode, features: &StepTreeFeatures) -> Step facts } -/// Walk StepNode tree to collect facts (Phase 120) +/// Walk StepNode tree to collect facts (Phase 120, Phase 124) fn walk_for_facts(node: &StepNode, facts: &mut StepTreeFacts) { match node { StepNode::Block(nodes) => { @@ -481,18 +481,23 @@ fn walk_for_facts(node: &StepNode, facts: &mut StepTreeFacts) { } StepNode::If { cond, + cond_ast, then_branch, else_branch, .. } => { facts.add_cond_sig(cond.to_compact_string()); + // Phase 124: Extract reads from condition AST + extract_variables_from_ast(&cond_ast.0, facts); walk_for_facts(then_branch, facts); if let Some(else_branch) = else_branch { walk_for_facts(else_branch, facts); } } - StepNode::Loop { cond, body, .. } => { + StepNode::Loop { cond, cond_ast, body, .. } => { facts.add_cond_sig(cond.to_compact_string()); + // Phase 124: Extract reads from condition AST + extract_variables_from_ast(&cond_ast.0, facts); walk_for_facts(body, facts); } StepNode::Stmt { kind, .. } => { @@ -506,10 +511,16 @@ fn walk_for_facts(node: &StepNode, facts: &mut StepTreeFacts) { if let Some(name) = target.as_ref() { facts.add_write(name.clone()); } + // Note: Assign RHS is not in StepStmtKind + // We rely on If/Loop condition AST for reads extraction } StepStmtKind::Print => {} - StepStmtKind::Return { .. } => { + StepStmtKind::Return { value_ast } => { facts.add_exit(ExitKind::Return); + // Phase 124: Extract reads from return value AST + if let Some(ast) = value_ast { + extract_variables_from_ast(&ast.0, facts); + } } StepStmtKind::Break => { facts.add_exit(ExitKind::Break); @@ -546,6 +557,60 @@ fn walk_for_facts(node: &StepNode, facts: &mut StepTreeFacts) { } } +/// Extract Variable names from AST (Phase 124: reads collection) +/// +/// SSOT for reads extraction: +/// - Recursively walk AST tree +/// - Add Variable { name } to facts.reads +/// - Ignore other node types +fn extract_variables_from_ast(ast: &ASTNode, facts: &mut StepTreeFacts) { + match ast { + ASTNode::Variable { name, .. } => { + facts.add_read(name.clone()); + } + // Recursively walk binary/unary operations + ASTNode::BinaryOp { left, right, .. } => { + extract_variables_from_ast(left, facts); + extract_variables_from_ast(right, facts); + } + ASTNode::UnaryOp { operand, .. } => { + extract_variables_from_ast(operand, facts); + } + // Function calls + ASTNode::FunctionCall { arguments, .. } => { + for arg in arguments { + extract_variables_from_ast(arg, facts); + } + } + // Method calls + ASTNode::MethodCall { object, arguments, .. } => { + extract_variables_from_ast(object, facts); + for arg in arguments { + extract_variables_from_ast(arg, facts); + } + } + // Field access + ASTNode::FieldAccess { object, .. } => { + extract_variables_from_ast(object, facts); + } + // Array/Index access + ASTNode::Index { target, index, .. } => { + extract_variables_from_ast(target, facts); + extract_variables_from_ast(index, facts); + } + // Assignment (RHS only) + ASTNode::Assignment { value, .. } => { + extract_variables_from_ast(value, facts); + } + // Print + ASTNode::Print { expression, .. } => { + extract_variables_from_ast(expression, facts); + } + // Ignore literals, keywords, and other non-variable nodes + _ => {} + } +} + fn merge_features(mut a: StepTreeFeatures, b: StepTreeFeatures) -> StepTreeFeatures { a.has_if |= b.has_if; a.has_loop |= b.has_loop; diff --git a/src/mir/control_tree/step_tree_facts.rs b/src/mir/control_tree/step_tree_facts.rs index cad0d8eb..2d4c24d0 100644 --- a/src/mir/control_tree/step_tree_facts.rs +++ b/src/mir/control_tree/step_tree_facts.rs @@ -1,7 +1,7 @@ -//! StepTreeFacts - raw structural facts extraction (Phase 120) +//! StepTreeFacts - raw structural facts extraction (Phase 120, Phase 124) //! //! Responsibility: -//! - Collect raw structural facts from StepNode tree (exits/writes/required_caps/cond_sig) +//! - Collect raw structural facts from StepNode tree (exits/writes/reads/required_caps/cond_sig) //! - NO formatting, NO decision-making, NO signature generation //! - Pure "facts only" - data collection without interpretation //! @@ -9,6 +9,10 @@ //! - Facts are collected during tree traversal //! - BTreeSet for deterministic iteration (order stability) //! - No dependency on contract or signature logic +//! +//! Phase 124 Changes: +//! - Added reads: BTreeSet for variable references +//! - reads tracks Variable(name) occurrences in expressions, conditions, and assignments use crate::mir::control_tree::{ExitKind, StepCapability}; use std::collections::BTreeSet; @@ -20,6 +24,9 @@ pub struct StepTreeFacts { pub exits: BTreeSet, /// Variable writes (Local declarations + Assignment targets) pub writes: BTreeSet, + /// Variable reads (Variable references in expressions, conditions, assignments) + /// Phase 124: Tracks all Variable(name) occurrences for Return(Variable) support + pub reads: BTreeSet, /// Required capabilities (structural features like NestedLoop, TryCatch, etc.) pub required_caps: BTreeSet, /// Condition signatures (compact string representations of if/loop conditions) @@ -43,6 +50,11 @@ impl StepTreeFacts { self.writes.insert(var); } + /// Add a variable read (Phase 124) + pub fn add_read(&mut self, var: String) { + self.reads.insert(var); + } + /// Add a required capability pub fn add_capability(&mut self, cap: StepCapability) { self.required_caps.insert(cap); @@ -57,6 +69,7 @@ impl StepTreeFacts { pub fn merge(&mut self, other: StepTreeFacts) { self.exits.extend(other.exits); self.writes.extend(other.writes); + self.reads.extend(other.reads); // Phase 124: merge reads self.required_caps.extend(other.required_caps); self.cond_sig.extend(other.cond_sig); }