swc_ecma_compat_es2015/block_scoping/
vars.rs1use indexmap::IndexMap;
2use rustc_hash::{FxBuildHasher, FxHashMap};
3use swc_atoms::Atom;
4use swc_common::{Mark, SyntaxContext};
5use swc_ecma_ast::*;
6use swc_ecma_transforms_base::{rename::rename_with_config, scope::ScopeKind};
7use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
8use swc_trace_macro::swc_trace;
9
10pub(super) fn block_scoped_vars() -> impl VisitMut {
11 BlockScopedVars::default()
12}
13
14#[derive(Default)]
15struct BlockScopedVars {
16 scope: Scope,
17
18 var_decl_kind: Option<VarDeclKind>,
19 is_param: bool,
20}
21
22#[derive(Debug, Default)]
23struct Scope {
24 kind: ScopeKind,
25
26 vars: IndexMap<Id, VarDeclKind, FxBuildHasher>,
27 usages: Vec<Id>,
28
29 children: Vec<Scope>,
30}
31
32#[derive(Clone, Copy)]
33struct ParentScope<'a> {
34 parent: Option<&'a ParentScope<'a>>,
35
36 vars: &'a IndexMap<Id, VarDeclKind, FxBuildHasher>,
37}
38
39#[swc_trace]
40impl BlockScopedVars {
41 fn add_usage(&mut self, id: Id) {
42 if !self.scope.usages.contains(&id) {
43 self.scope.usages.push(id);
44 }
45 }
46
47 fn handle_program<N>(&mut self, n: &mut N)
60 where
61 N: VisitMutWith<Self> + for<'aa> VisitMutWith<dyn 'aa + VisitMut>,
62 {
63 n.visit_mut_children_with(self);
64
65 let empty_vars = Default::default();
66 let parent = ParentScope {
67 parent: None,
68 vars: &empty_vars,
69 };
70
71 let mut rename_map = FxHashMap::default();
72
73 self.scope.rename(parent, &mut rename_map, true);
76 self.scope.rename(parent, &mut rename_map, false);
77
78 n.visit_mut_with(
81 &mut rename_with_config(&rename_map, Default::default()) as &mut dyn VisitMut
82 );
83 }
84
85 fn with_scope(&mut self, kind: ScopeKind, op: impl FnOnce(&mut Self)) {
86 let scope = Scope {
87 kind,
88 ..Default::default()
89 };
90
91 let mut v = BlockScopedVars { scope, ..*self };
92 op(&mut v);
93
94 if kind == ScopeKind::Block {
95 for (k, v) in &v.scope.vars {
96 if *v == VarDeclKind::Var {
97 self.scope.vars.insert(k.clone(), VarDeclKind::Var);
98 }
99 }
100 }
101
102 self.scope.children.push(v.scope);
103 }
104}
105
106#[swc_trace]
107impl Scope {
108 fn rename(&mut self, parent: ParentScope, rename_map: &mut FxHashMap<Id, Id>, fn_only: bool) {
109 for s in self.children.iter_mut() {
110 let parent = ParentScope {
111 parent: Some(&parent),
112 vars: &self.vars,
113 };
114
115 s.rename(parent, rename_map, fn_only);
116 }
117
118 if fn_only && self.kind != ScopeKind::Fn {
119 return;
120 }
121
122 let mut symbols = Default::default();
123
124 self.collect_candidates(parent, &mut symbols);
125
126 self.rename_decls(&symbols, rename_map);
129 }
130
131 fn can_access(&self, id: &Id, parent: ParentScope, deny_let_const: bool) -> bool {
144 if parent.get_var(id).is_some() {
145 return true;
146 }
147
148 if let Some(kind) = self.vars.get(id).copied() {
149 if deny_let_const && matches!(kind, VarDeclKind::Let | VarDeclKind::Const) {
150 return false;
151 }
152
153 return true;
154 }
155
156 self.children.iter().any(|s| match s.kind {
157 ScopeKind::Block => s.can_access(id, parent, true),
158 ScopeKind::Fn => false,
159 })
160 }
161
162 fn remove_usage(&mut self, id: &Id) {
163 if let Some(pos) = self.usages.iter().position(|i| *i == *id) {
164 self.usages.remove(pos);
165 }
166 }
167
168 fn collect_candidates(&mut self, parent: ParentScope, symbols: &mut Vec<Atom>) {
171 for id in &self.usages {
172 if self.can_access(id, parent, false) {
173 self.children.iter_mut().for_each(|s| {
174 s.remove_usage(id);
175 });
176 } else if !symbols.contains(&id.0) {
177 symbols.push(id.0.clone());
178 }
179 }
180 self.usages.clear();
181
182 let parent = ParentScope {
183 parent: Some(&parent),
184 vars: &self.vars,
185 };
186
187 self.children
188 .iter_mut()
189 .for_each(|s| s.collect_candidates(parent, symbols));
190 }
191
192 fn rename_decls(&self, symbols: &[Atom], rename_map: &mut FxHashMap<Id, Id>) {
193 for (id, _) in &self.vars {
194 if !symbols.contains(&id.0) {
195 continue;
196 }
197 if rename_map.contains_key(id) {
198 continue;
199 }
200
201 let sym = format!("_${}", id.0);
204
205 let ctxt = SyntaxContext::empty().apply_mark(Mark::fresh(Mark::root()));
217
218 rename_map.insert(id.clone(), (sym.into(), ctxt));
219 }
220
221 self.children
222 .iter()
223 .for_each(|s| s.rename_decls(symbols, rename_map));
224 }
225}
226
227impl ParentScope<'_> {
228 fn get_var(&self, id: &Id) -> Option<VarDeclKind> {
229 if let Some(kind) = self.vars.get(id).copied() {
230 return Some(kind);
231 }
232
233 self.parent?.get_var(id)
234 }
235}
236
237#[swc_trace]
238impl VisitMut for BlockScopedVars {
239 noop_visit_mut_type!(fail);
240
241 fn visit_mut_arrow_expr(&mut self, n: &mut ArrowExpr) {
242 self.with_scope(ScopeKind::Fn, |v| {
243 let old = v.is_param;
244 v.is_param = true;
245 n.params.visit_mut_with(v);
246 v.is_param = old;
247
248 match &mut *n.body {
249 BlockStmtOrExpr::BlockStmt(b) => {
250 b.visit_mut_children_with(v);
251 }
252 BlockStmtOrExpr::Expr(b) => {
253 b.visit_mut_with(v);
254 }
255 #[cfg(swc_ast_unknown)]
256 _ => panic!("unable to access unknown nodes"),
257 }
258 });
259 }
260
261 fn visit_mut_assign_pat_prop(&mut self, n: &mut AssignPatProp) {
262 n.visit_mut_children_with(self);
263
264 if let Some(kind) = self.var_decl_kind {
265 self.scope.vars.insert(n.key.to_id(), kind);
266 } else if !self.is_param {
267 self.add_usage(n.key.to_id())
268 }
269 }
270
271 fn visit_mut_binding_ident(&mut self, i: &mut BindingIdent) {
272 if let Some(kind) = self.var_decl_kind {
273 self.scope.vars.insert(i.to_id(), kind);
274 } else if !self.is_param {
275 self.add_usage(i.to_id())
276 }
277 }
278
279 fn visit_mut_block_stmt(&mut self, n: &mut BlockStmt) {
280 self.with_scope(ScopeKind::Block, |v| {
281 n.visit_mut_children_with(v);
282 });
283 }
284
285 fn visit_mut_catch_clause(&mut self, n: &mut CatchClause) {
286 let old_is_param = self.is_param;
287 self.is_param = true;
288
289 let old_var_decl_kind = self.var_decl_kind;
290 self.var_decl_kind = None;
291
292 n.visit_mut_children_with(self);
293
294 self.var_decl_kind = old_var_decl_kind;
295 self.is_param = old_is_param;
296 }
297
298 fn visit_mut_constructor(&mut self, n: &mut Constructor) {
299 self.with_scope(ScopeKind::Fn, |v| {
300 n.params.visit_mut_with(v);
301
302 if let Some(body) = &mut n.body {
303 body.visit_mut_children_with(v);
304 }
305 });
306 }
307
308 fn visit_mut_expr(&mut self, n: &mut Expr) {
309 let old_var_decl_kind = self.var_decl_kind;
310 self.var_decl_kind = None;
311
312 n.visit_mut_children_with(self);
313
314 if let Expr::Ident(i) = n {
315 self.add_usage(i.to_id());
316 }
317
318 self.var_decl_kind = old_var_decl_kind;
319 }
320
321 fn visit_mut_for_in_stmt(&mut self, n: &mut ForInStmt) {
322 n.right.visit_mut_with(self);
323
324 match &n.left {
325 ForHead::VarDecl(v)
326 if matches!(
327 &**v,
328 VarDecl {
329 kind: VarDeclKind::Let | VarDeclKind::Const,
330 ..
331 }
332 ) =>
333 {
334 self.with_scope(ScopeKind::Block, |v| {
335 n.left.visit_mut_with(v);
336 n.body.visit_mut_with(v);
337 });
338 }
339 _ => {
340 n.left.visit_mut_with(self);
341 n.body.visit_mut_with(self);
342 }
343 }
344 }
345
346 fn visit_mut_for_of_stmt(&mut self, n: &mut ForOfStmt) {
347 n.right.visit_mut_with(self);
348
349 match &n.left {
350 ForHead::VarDecl(v)
351 if matches!(
352 &**v,
353 VarDecl {
354 kind: VarDeclKind::Let | VarDeclKind::Const,
355 ..
356 }
357 ) =>
358 {
359 self.with_scope(ScopeKind::Block, |v| {
360 n.left.visit_mut_with(v);
361 n.body.visit_mut_with(v);
362 });
363 }
364 _ => {
365 n.left.visit_mut_with(self);
366 n.body.visit_mut_with(self);
367 }
368 }
369 }
370
371 fn visit_mut_for_stmt(&mut self, n: &mut ForStmt) {
372 match &n.init {
373 Some(VarDeclOrExpr::VarDecl(v))
374 if matches!(
375 &**v,
376 VarDecl {
377 kind: VarDeclKind::Let | VarDeclKind::Const,
378 ..
379 }
380 ) =>
381 {
382 self.with_scope(ScopeKind::Block, |v| {
383 n.init.visit_mut_with(v);
384 n.update.visit_mut_with(v);
385 n.test.visit_mut_with(v);
386
387 n.body.visit_mut_with(v);
388 });
389 }
390 _ => {
391 n.init.visit_mut_with(self);
392 n.update.visit_mut_with(self);
393 n.test.visit_mut_with(self);
394
395 n.body.visit_mut_with(self);
396 }
397 }
398 }
399
400 fn visit_mut_function(&mut self, n: &mut Function) {
401 n.decorators.visit_mut_with(self);
402
403 self.with_scope(ScopeKind::Fn, |v| {
404 n.params.visit_mut_with(v);
405
406 if let Some(body) = &mut n.body {
407 body.visit_mut_children_with(v);
408 }
409 });
410 }
411
412 fn visit_mut_module(&mut self, n: &mut Module) {
413 self.handle_program(n)
414 }
415
416 fn visit_mut_param(&mut self, n: &mut Param) {
417 let old_is_param = self.is_param;
418 self.is_param = true;
419
420 let old_var_decl_kind = self.var_decl_kind;
421 self.var_decl_kind = None;
422
423 n.visit_mut_children_with(self);
424
425 self.var_decl_kind = old_var_decl_kind;
426 self.is_param = old_is_param;
427 }
428
429 fn visit_mut_prop(&mut self, n: &mut Prop) {
430 n.visit_mut_children_with(self);
431
432 if let Prop::Shorthand(i) = n {
433 self.add_usage(i.to_id());
434 }
435 }
436
437 fn visit_mut_script(&mut self, n: &mut Script) {
438 self.handle_program(n)
439 }
440
441 fn visit_mut_var_decl(&mut self, n: &mut VarDecl) {
442 let old_var_decl_kind = self.var_decl_kind;
443 self.var_decl_kind = Some(n.kind);
444
445 n.visit_mut_children_with(self);
446
447 self.var_decl_kind = old_var_decl_kind;
448 }
449}