react_auditor/rules/quality/
no_shadow.rs1use oxc_ast::ast::Program;
2use oxc_semantic::Semantic;
3
4use crate::rules::{Rule, RuleFinding, RuleMeta, Severity};
5
6pub struct NoShadow;
7
8const RULE_META: RuleMeta = RuleMeta {
9 id: "no-shadow",
10 default_severity: Severity::Warning,
11 category: "quality",
12 description: "No variable shadowing in nested scopes",
13};
14
15impl Rule for NoShadow {
16 fn meta(&self) -> &RuleMeta {
17 &RULE_META
18 }
19
20 fn run(&self, _program: &Program, semantic: &Semantic, source_text: &str) -> Vec<RuleFinding> {
21 let mut findings = Vec::new();
22 let scoping = semantic.scoping();
23
24 for symbol_id in scoping.symbol_ids() {
25 let name = scoping.symbol_name(symbol_id);
26 let scope_id = scoping.symbol_scope_id(symbol_id);
27
28 if name.starts_with('_') || name == "arguments" {
29 continue;
30 }
31
32 let mut ancestors = scoping.scope_ancestors(scope_id);
33 ancestors.next();
34
35 for ancestor_scope_id in ancestors {
36 if scoping
37 .get_binding(ancestor_scope_id, name.into())
38 .is_some()
39 {
40 let span = scoping.symbol_span(symbol_id);
41 let start = span.start as usize;
42 let line = source_text[..start].lines().count().max(1);
43 let col = start - source_text[..start].rfind('\n').map(|i| i + 1).unwrap_or(0);
44
45 findings.push(RuleFinding {
46 line,
47 column: col + 1,
48 message: format!("`{name}` shadows a variable in a parent scope"),
49 });
50 break;
51 }
52 }
53 }
54
55 findings
56 }
57}