mirror of
https://github.com/imjasonh/nescript
synced 2026-07-08 08:55:38 +00:00
Analyzer: W0103 also fires on unused state-local variables
Previously the unused-variable warning only ran on globals. Now it walks \`state.locals\` too, so state-scoped variables that are declared and never read also emit W0103. Factored out to \`check_unused_var\` to share the \`_\`-prefix exemption and help text. https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3
This commit is contained in:
parent
b7c806523d
commit
3f87aa1e30
2 changed files with 53 additions and 18 deletions
|
|
@ -211,25 +211,15 @@ 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.
|
||||
// Check for unused variables (W0103). Variables whose names
|
||||
// start with '_' are exempt by convention. Both globals and
|
||||
// state-local variables are checked.
|
||||
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::<Label>::new(),
|
||||
help: Some(
|
||||
"prefix with '_' to silence this warning, or remove the declaration".into(),
|
||||
),
|
||||
note: None,
|
||||
});
|
||||
self.check_unused_var(var);
|
||||
}
|
||||
for state in &program.states {
|
||||
for var in &state.locals {
|
||||
self.check_unused_var(var);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -242,6 +232,26 @@ impl Analyzer {
|
|||
self.used_vars.insert(name.to_string());
|
||||
}
|
||||
|
||||
/// Emit W0103 if `var` is never read anywhere. Variables named
|
||||
/// with a leading `_` are exempt by convention.
|
||||
fn check_unused_var(&mut self, var: &VarDecl) {
|
||||
if var.name.starts_with('_') {
|
||||
return;
|
||||
}
|
||||
if self.used_vars.contains(&var.name) {
|
||||
return;
|
||||
}
|
||||
self.diagnostics.push(Diagnostic {
|
||||
level: Level::Warning,
|
||||
code: ErrorCode::W0103,
|
||||
message: format!("unused variable '{}'", var.name),
|
||||
span: var.span,
|
||||
labels: Vec::<Label>::new(),
|
||||
help: Some("prefix with '_' to silence this warning, or remove the declaration".into()),
|
||||
note: None,
|
||||
});
|
||||
}
|
||||
|
||||
/// Recursively walk an expression tree and mark every identifier that
|
||||
/// appears as an `Expr::Ident` (or as an `Expr::ArrayIndex` base) as
|
||||
/// "read". Used by the W0103 unused-variable analysis. Also emits
|
||||
|
|
|
|||
|
|
@ -562,6 +562,31 @@ fn analyze_unused_variable_warning() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn analyze_unused_state_local_warning() {
|
||||
// State-local `bonus` is declared but never read — W0103 should fire.
|
||||
let (prog, diags) = parser::parse(
|
||||
r#"
|
||||
game "Test" { mapper: NROM }
|
||||
state Main {
|
||||
var bonus: u8 = 0
|
||||
on frame { wait_frame }
|
||||
}
|
||||
start Main
|
||||
"#,
|
||||
);
|
||||
assert!(diags.is_empty(), "parse errors: {diags:?}");
|
||||
let result = analyze(&prog.unwrap());
|
||||
assert!(
|
||||
result
|
||||
.diagnostics
|
||||
.iter()
|
||||
.any(|d| d.code == ErrorCode::W0103 && d.message.contains("bonus")),
|
||||
"expected W0103 for unused state-local 'bonus', got: {:?}",
|
||||
result.diagnostics
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn analyze_unused_variable_no_warning_when_read() {
|
||||
// `counter` is both written and read (in the `if` condition),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue