From 6430c3a93588e5a6e0fa8c2f79d5d5597b1b2877 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 12 Apr 2026 10:01:44 +0000 Subject: [PATCH] Sprite resolution, asset wiring, shift-assign, unreachable state warning Sprite/asset pipeline: - Linker::link_with_assets() places sprite CHR data in ROM at correct tile - assets::resolve_sprites() walks Program for inline sprite bytes - CodeGen::with_sprites() maps sprite names to tile indices - gen_draw() uses correct tile index from sprite declarations - main.rs wires the full resolution pipeline Shift-assign operators (<<= and >>=): - AssignOp::ShiftLeftAssign and ShiftRightAssign variants - Parser handles in both statement and array index contexts - Codegen emits ASL A / LSR A - IR lowering maps to ShiftLeft/ShiftRight ops Unreachable state warning (W0104): - BFS from start state finds reachable states via transitions - States not reached produce W0104 warning Error polish helpers: - suggest_var_name() for "did you mean" suggestions - emit_undefined_var() for E0502 with typo hints - Used by analyzer for better diagnostics 242 tests pass, clippy clean. https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3 --- src/analyzer/mod.rs | 237 +++++++++++++++++++++++++++++++++++++- src/analyzer/tests.rs | 176 ++++++++++++++++++++++++++++ src/assets/mod.rs | 2 + src/assets/resolve.rs | 55 +++++++++ src/codegen/mod.rs | 63 +++++++++- src/errors/diagnostic.rs | 4 +- src/ir/lowering.rs | 4 + src/linker/mod.rs | 29 ++++- src/linker/tests.rs | 56 +++++++++ src/main.rs | 17 ++- src/parser/ast.rs | 2 + src/parser/mod.rs | 28 +++++ tests/integration_test.rs | 81 ++++++++++++- 13 files changed, 737 insertions(+), 17 deletions(-) create mode 100644 src/assets/resolve.rs diff --git a/src/analyzer/mod.rs b/src/analyzer/mod.rs index 76c5cc9..5391382 100644 --- a/src/analyzer/mod.rs +++ b/src/analyzer/mod.rs @@ -3,7 +3,7 @@ mod tests; use std::collections::{HashMap, HashSet}; -use crate::errors::{Diagnostic, ErrorCode}; +use crate::errors::{Diagnostic, ErrorCode, Label, Level}; use crate::lexer::Span; use crate::parser::ast::*; @@ -48,6 +48,7 @@ pub fn analyze(program: &Program) -> AnalysisResult { max_depths: HashMap::new(), stack_depth_limit: DEFAULT_STACK_DEPTH, in_loop: false, + used_vars: HashSet::new(), }; analyzer.analyze_program(program); @@ -70,6 +71,9 @@ struct Analyzer { max_depths: HashMap, stack_depth_limit: u32, in_loop: bool, + /// Names of variables that have been read somewhere in the program. + /// Used for the W0103 unused-variable warning. + used_vars: HashSet, } impl Analyzer { @@ -121,9 +125,34 @@ impl Analyzer { } } - // Type-check function bodies + // Type-check function bodies. Parameters are registered as + // symbols for the duration of the body check so that identifier + // references (and the W0103 used-variable tracker) can resolve + // them. They are unregistered afterwards to avoid leaking into + // the global scope. Parameters are also pre-marked as "used" so + // we do not emit W0103 for unused function arguments (which are + // a common and deliberate pattern). for fun in &program.functions { + let mut added_params = Vec::new(); + for param in &fun.params { + if !self.symbols.contains_key(¶m.name) { + self.symbols.insert( + param.name.clone(), + Symbol { + name: param.name.clone(), + sym_type: param.param_type.clone(), + is_const: false, + span: fun.span, + }, + ); + added_params.push(param.name.clone()); + } + self.mark_var_used(¶m.name); + } self.check_block(&fun.body, &state_names); + for name in &added_params { + self.symbols.remove(name); + } } // Build call graph @@ -141,6 +170,145 @@ impl Analyzer { // Compute max call depths from entry points (state handlers) self.compute_max_depths(program); + + // Check for unused global variables (W0103). Variables whose names + // start with '_' are exempt by convention. State-local variables are + // left out for now to avoid noise during early development. + for var in &program.globals { + if var.name.starts_with('_') { + continue; + } + if !self.used_vars.contains(&var.name) { + self.diagnostics.push(Diagnostic { + level: Level::Warning, + code: ErrorCode::W0103, + message: format!("unused variable '{}'", var.name), + span: var.span, + labels: Vec::