@@ -5,6 +5,7 @@ use oxc_ast::{
55} ;
66use oxc_diagnostics:: OxcDiagnostic ;
77use oxc_macros:: declare_oxc_lint;
8+ use oxc_semantic:: Scoping ;
89use oxc_span:: { GetSpan , Span } ;
910use 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
148146fn 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
169175fn 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]
214241fn test ( ) {
215242 use crate :: tester:: Tester ;
0 commit comments