mirror of
https://github.com/imjasonh/nescript
synced 2026-07-18 06:36:57 +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)
|
// Compute max call depths from entry points (state handlers)
|
||||||
self.compute_max_depths(program);
|
self.compute_max_depths(program);
|
||||||
|
|
||||||
// Check for unused global variables (W0103). Variables whose names
|
// Check for unused variables (W0103). Variables whose names
|
||||||
// start with '_' are exempt by convention. State-local variables are
|
// start with '_' are exempt by convention. Both globals and
|
||||||
// left out for now to avoid noise during early development.
|
// state-local variables are checked.
|
||||||
for var in &program.globals {
|
for var in &program.globals {
|
||||||
if var.name.starts_with('_') {
|
self.check_unused_var(var);
|
||||||
continue;
|
}
|
||||||
}
|
for state in &program.states {
|
||||||
if !self.used_vars.contains(&var.name) {
|
for var in &state.locals {
|
||||||
self.diagnostics.push(Diagnostic {
|
self.check_unused_var(var);
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -242,6 +232,26 @@ impl Analyzer {
|
||||||
self.used_vars.insert(name.to_string());
|
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
|
/// Recursively walk an expression tree and mark every identifier that
|
||||||
/// appears as an `Expr::Ident` (or as an `Expr::ArrayIndex` base) as
|
/// appears as an `Expr::Ident` (or as an `Expr::ArrayIndex` base) as
|
||||||
/// "read". Used by the W0103 unused-variable analysis. Also emits
|
/// "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]
|
#[test]
|
||||||
fn analyze_unused_variable_no_warning_when_read() {
|
fn analyze_unused_variable_no_warning_when_read() {
|
||||||
// `counter` is both written and read (in the `if` condition),
|
// `counter` is both written and read (in the `if` condition),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue