Skip to content

Commit 3cd9d5e

Browse files
feat(lint): add [shadow-var] rule — detect variable redeclaration in same scope
Flags `h x = ...; h x = ...` in the same statement list with a hint suggesting `x = ...` assignment instead. Correctly scoped: re-declaring inside an inner block (if/while body) does not trigger the rule. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 819a614 commit 3cd9d5e

1 file changed

Lines changed: 17 additions & 1 deletion

File tree

omnimcode-cli/src/main.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1402,8 +1402,24 @@ fn lint_source(content: &str) -> Vec<LintHint> {
14021402

14031403
/// AST-level lint: walk the statement tree looking for structural issues.
14041404
fn lint_ast_stmts(stmts: &[Statement], in_fn: Option<&str>, hints: &mut Vec<LintHint>) {
1405-
// Dead code: any statement(s) after an unconditional control-flow terminator.
1405+
use std::collections::HashSet;
14061406
let ctx = in_fn.unwrap_or("<top-level>");
1407+
1408+
// Shadow-var: flag `h x = ...; ... h x = ...` re-declaration in the same scope.
1409+
{
1410+
let mut seen: HashSet<&str> = HashSet::new();
1411+
for stmt in stmts {
1412+
if let Statement::VarDecl { name, .. } = stmt {
1413+
if !seen.insert(name.as_str()) {
1414+
hints.push((None, "shadow-var",
1415+
format!("`h {}` re-declares a variable already declared in `{}` — use assignment `{} = ...` instead",
1416+
name, ctx, name)));
1417+
}
1418+
}
1419+
}
1420+
}
1421+
1422+
// Dead code: any statement(s) after an unconditional control-flow terminator.
14071423
for i in 0..stmts.len().saturating_sub(1) {
14081424
let is_terminal = matches!(&stmts[i],
14091425
Statement::Return(_) | Statement::Break | Statement::Continue | Statement::Throw(_));

0 commit comments

Comments
 (0)