Skip to content

Commit 0d903c4

Browse files
committed
refactor(linter/block-scoped-var): avoid iter_all_scope_child_ids by walking references/redeclarations scope ancestors
1 parent bf74302 commit 0d903c4

1 file changed

Lines changed: 48 additions & 21 deletions

File tree

crates/oxc_linter/src/rules/eslint/block_scoped_var.rs

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use oxc_ast::{
55
};
66
use oxc_diagnostics::OxcDiagnostic;
77
use oxc_macros::declare_oxc_lint;
8+
use oxc_semantic::Scoping;
89
use oxc_span::{GetSpan, Span};
910
use oxc_syntax::{scope::ScopeId, symbol::SymbolId};
1011

@@ -135,27 +136,32 @@ impl Rule for BlockScopedVar {
135136
if !ctx.scoping().scope_flags(scope_id).is_strict_mode() {
136137
return;
137138
}
138-
// `scope_ids` contains all the scopes that are children of the current scope
139-
// we should eliminate all of them
140-
let scope_ids = ctx.scoping().iter_all_scope_child_ids(node.scope_id()).collect::<Vec<_>>();
141139

142140
for item in &decl.declarations {
143-
run_for_declaration(&item.id, &scope_ids, node, ctx);
141+
run_for_declaration(&item.id, node, ctx);
144142
}
145143
}
146144
}
147145

148146
fn run_for_all_references(
149147
(pattern, name, symbol): (&BindingPattern, &str, &SymbolId),
150-
scope_ids: &[ScopeId],
151148
node: &AstNode,
152149
ctx: &LintContext,
153150
) {
154-
ctx.scoping()
155-
.get_resolved_references(*symbol)
151+
let mut references = ctx.scoping().get_resolved_references(*symbol).peekable();
152+
if references.peek().is_none() {
153+
return;
154+
}
155+
156+
let declare_scope_id = node.scope_id();
157+
references
156158
.filter(|reference| {
157159
let reference_scope_id = reference.scope_id();
158-
reference_scope_id != node.scope_id() && !scope_ids.contains(&reference_scope_id)
160+
check_if_has_reference_outside_scope(
161+
declare_scope_id,
162+
reference_scope_id,
163+
ctx.scoping(),
164+
)
159165
})
160166
.for_each(|reference| {
161167
ctx.diagnostic(use_outside_scope_diagnostic(
@@ -168,29 +174,32 @@ fn run_for_all_references(
168174

169175
fn run_for_all_redeclarations(
170176
(pattern, name, symbol): (&BindingPattern, &str, &SymbolId),
171-
scope_ids: &[ScopeId],
172177
node: &AstNode,
173178
ctx: &LintContext,
174179
) {
175-
ctx.scoping()
176-
.symbol_redeclarations(*symbol)
180+
let redeclarations = ctx.scoping().symbol_redeclarations(*symbol);
181+
if redeclarations.is_empty() {
182+
return;
183+
}
184+
185+
let declare_scope_id = node.scope_id();
186+
187+
redeclarations
177188
.iter()
178189
.filter(|redeclaration| {
179190
let redeclare_scope_id = ctx.nodes().get_node(redeclaration.declaration).scope_id();
180-
181-
redeclare_scope_id != node.scope_id() && !scope_ids.contains(&redeclare_scope_id)
191+
check_if_has_reference_outside_scope(
192+
declare_scope_id,
193+
redeclare_scope_id,
194+
ctx.scoping(),
195+
)
182196
})
183197
.for_each(|redeclaration| {
184198
ctx.diagnostic(redeclaration_diagnostic(pattern.span(), redeclaration.span, name));
185199
});
186200
}
187201

188-
fn run_for_declaration(
189-
pattern: &BindingPattern,
190-
scope_ids: &[ScopeId],
191-
node: &AstNode,
192-
ctx: &LintContext,
193-
) {
202+
fn run_for_declaration(pattern: &BindingPattern, node: &AstNode, ctx: &LintContext) {
194203
// e.g. "var [a, b] = [1, 2]"
195204
for ident in pattern.get_binding_identifiers() {
196205
let name = ident.name.as_str();
@@ -203,13 +212,31 @@ fn run_for_declaration(
203212
// e.g. "if (true) { var a = 4; } else { var a = 4; }"
204213
// in this case we can't find the reference of 'a' by call `get_resolved_references`
205214
// so I use `symbol_redeclarations` to find all the redeclarations
206-
run_for_all_redeclarations(binding, scope_ids, node, ctx);
215+
run_for_all_redeclarations(binding, node, ctx);
207216

208217
// e.g. "var a = 4; console.log(a);"
209-
run_for_all_references(binding, scope_ids, node, ctx);
218+
run_for_all_references(binding, node, ctx);
210219
}
211220
}
212221

222+
/// Returns true if the reference is outside the declaration scope
223+
fn check_if_has_reference_outside_scope(
224+
declare_scope_id: ScopeId,
225+
reference_scope_id: ScopeId,
226+
scoping: &Scoping,
227+
) -> bool {
228+
// Walk up the scope chain from the reference scope to see if we reach the declaration scope,
229+
// if we do, then the reference is inside the scope, otherwise it's outside
230+
for ancestor_scope_id in scoping.scope_ancestors(reference_scope_id) {
231+
// Already reached the declaration scope, so the reference is inside the scope
232+
if ancestor_scope_id == declare_scope_id {
233+
return false;
234+
}
235+
}
236+
237+
true
238+
}
239+
213240
#[test]
214241
fn test() {
215242
use crate::tester::Tester;

0 commit comments

Comments
 (0)