1use std::{iter::once, mem::take};
2
3use indexmap::IndexMap;
4use rustc_hash::{FxHashMap, FxHashSet};
5use smallvec::SmallVec;
6use swc_atoms::Atom;
7use swc_common::{util::take::Take, Mark, Spanned, SyntaxContext, DUMMY_SP};
8use swc_ecma_ast::*;
9use swc_ecma_transforms_base::helper;
10use swc_ecma_utils::{
11 find_pat_ids, function::FnEnvHoister, prepend_stmt, private_ident, quote_ident, quote_str,
12 ExprFactory, StmtLike,
13};
14use swc_ecma_visit::{
15 noop_visit_mut_type, visit_mut_obj_and_computed, visit_mut_pass, VisitMut, VisitMutWith,
16};
17use swc_trace_macro::swc_trace;
18
19mod vars;
20
21pub fn block_scoping(unresolved_mark: Mark) -> impl Pass {
35 (
36 visit_mut_pass(self::vars::block_scoped_vars()),
37 visit_mut_pass(BlockScoping {
38 unresolved_mark,
39 scope: Default::default(),
40 vars: Vec::new(),
41 var_decl_kind: VarDeclKind::Var,
42 }),
43 )
44}
45
46type ScopeStack = SmallVec<[ScopeKind; 8]>;
47
48#[derive(Debug, PartialEq, Eq)]
49enum ScopeKind {
50 Loop {
51 lexical_var: Vec<Id>,
52 args: Vec<Id>,
53 used: Vec<Id>,
55 mutated: FxHashMap<Id, SyntaxContext>,
57 },
58 Fn,
59 Block,
60}
61
62impl ScopeKind {
63 fn new_loop() -> Self {
64 ScopeKind::Loop {
65 lexical_var: Vec::new(),
66 args: Vec::new(),
67 used: Vec::new(),
68 mutated: Default::default(),
69 }
70 }
71}
72
73struct BlockScoping {
74 unresolved_mark: Mark,
75 scope: ScopeStack,
76 vars: Vec<VarDeclarator>,
77 var_decl_kind: VarDeclKind,
78}
79
80impl BlockScoping {
81 fn visit_mut_with_scope<T>(&mut self, kind: ScopeKind, node: &mut T)
84 where
85 T: VisitMutWith<Self>,
86 {
87 let remove = !matches!(kind, ScopeKind::Loop { .. });
88 self.scope.push(kind);
89
90 node.visit_mut_with(self);
91
92 if remove {
93 self.scope.pop();
94 }
95 }
96
97 fn mark_as_used(&mut self, i: Id) {
98 for scope in self
101 .scope
102 .iter_mut()
103 .rev()
104 .skip_while(|scope| matches!(scope, ScopeKind::Loop { .. }))
105 {
106 if let ScopeKind::Loop {
107 lexical_var, used, ..
108 } = scope
109 {
110 if lexical_var.contains(&i) {
111 used.push(i);
112 return;
113 }
114 }
115 }
116 }
117
118 fn in_loop_body(&self) -> bool {
119 self.scope
120 .last()
121 .map(|scope| matches!(scope, ScopeKind::Loop { .. }))
122 .unwrap_or(false)
123 }
124
125 fn handle_capture_of_vars(&mut self, body: &mut Box<Stmt>) {
126 let body_stmt = &mut **body;
127
128 if let Some(ScopeKind::Loop {
129 args,
130 used,
131 mutated,
132 ..
133 }) = self.scope.pop()
134 {
135 if used.is_empty() {
136 return;
137 }
138
139 let mut env_hoister =
140 FnEnvHoister::new(SyntaxContext::empty().apply_mark(self.unresolved_mark));
141 body_stmt.visit_mut_with(&mut env_hoister);
142 let mut inits: Vec<Box<Expr>> = Vec::new();
143
144 for mut var in env_hoister.to_decl() {
145 if let Some(init) = var.init.take() {
146 inits.push(
147 AssignExpr {
148 span: DUMMY_SP,
149 op: op!("="),
150 left: var.name.clone().try_into().unwrap(),
151 right: init,
152 }
153 .into(),
154 );
155 }
156
157 self.vars.push(var);
158 }
159
160 let mut flow_helper = FlowHelper {
161 all: &args,
162 has_break: false,
163 has_return: false,
164 has_yield: false,
165 has_await: false,
166 label: IndexMap::new(),
167 inner_label: FxHashSet::default(),
168 mutated,
169 in_switch_case: false,
170 in_nested_loop: false,
171 };
172
173 body_stmt.visit_mut_with(&mut flow_helper);
174
175 let mut body_stmt = match &mut body_stmt.take() {
176 Stmt::Block(bs) => bs.take(),
177 body => BlockStmt {
178 span: DUMMY_SP,
179 stmts: vec![body.take()],
180 ..Default::default()
181 },
182 };
183
184 if !flow_helper.mutated.is_empty() {
185 let no_modification = flow_helper.mutated.is_empty();
186 let mut v = MutationHandler {
187 map: &mut flow_helper.mutated,
188 in_function: false,
189 };
190
191 body_stmt.visit_mut_with(&mut v);
193
194 if !no_modification
195 && body_stmt
196 .stmts
197 .last()
198 .map(|s| !matches!(s, Stmt::Return(..)))
199 .unwrap_or(true)
200 {
201 body_stmt.stmts.push(v.make_reassignment(None).into_stmt());
202 }
203 }
204
205 let var_name = private_ident!("_loop");
206
207 self.vars.push(VarDeclarator {
208 span: DUMMY_SP,
209 name: var_name.clone().into(),
210 init: Some(
211 Function {
212 span: DUMMY_SP,
213 params: args
214 .iter()
215 .map(|i| {
216 let ctxt = flow_helper.mutated.get(i).copied().unwrap_or(i.1);
217
218 Param {
219 span: DUMMY_SP,
220 decorators: Default::default(),
221 pat: Ident::new(i.0.clone(), DUMMY_SP, ctxt).into(),
222 }
223 })
224 .collect(),
225 decorators: Default::default(),
226 body: Some(body_stmt),
227 is_generator: flow_helper.has_yield,
228 is_async: flow_helper.has_await,
229 ..Default::default()
230 }
231 .into(),
232 ),
233 definite: false,
234 });
235
236 let mut call: Expr = CallExpr {
237 span: DUMMY_SP,
238 callee: var_name.as_callee(),
239 args: args
240 .iter()
241 .cloned()
242 .map(|i| Ident::new(i.0, DUMMY_SP, i.1).as_arg())
243 .collect(),
244 ..Default::default()
245 }
246 .into();
247
248 if flow_helper.has_await {
249 call = AwaitExpr {
250 span: DUMMY_SP,
251 arg: call.into(),
252 }
253 .into();
254 }
255
256 if flow_helper.has_yield {
257 call = YieldExpr {
258 span: DUMMY_SP,
259 arg: Some(call.into()),
260 delegate: true,
261 }
262 .into();
263 }
264
265 if !inits.is_empty() {
266 call = SeqExpr {
267 span: DUMMY_SP,
268 exprs: inits.into_iter().chain(once(Box::new(call))).collect(),
269 }
270 .into()
271 }
272
273 if flow_helper.has_return || flow_helper.has_break || !flow_helper.label.is_empty() {
274 let ret = private_ident!("_ret");
275
276 let mut stmts = vec![
277 VarDecl {
279 span: DUMMY_SP,
280 kind: VarDeclKind::Var,
281 decls: vec![VarDeclarator {
282 span: DUMMY_SP,
283 name: ret.clone().into(),
284 init: Some(Box::new(call.take())),
285 definite: false,
286 }],
287 ..Default::default()
288 }
289 .into(),
290 ];
291
292 if flow_helper.has_return {
293 stmts.push(
295 IfStmt {
296 span: DUMMY_SP,
297 test: BinExpr {
298 span: DUMMY_SP,
299 op: op!("==="),
300 left: {
301 let callee = helper!(type_of);
303
304 CallExpr {
305 span: Default::default(),
306 callee,
307 args: vec![ret.clone().as_arg()],
308 ..Default::default()
309 }
310 .into()
311 },
312 right: "object".into(),
314 }
315 .into(),
316 cons: Box::new(
317 ReturnStmt {
318 span: DUMMY_SP,
319 arg: Some(ret.clone().make_member(quote_ident!("v")).into()),
320 }
321 .into(),
322 ),
323 alt: None,
324 }
325 .into(),
326 )
327 }
328
329 if flow_helper.has_break {
330 stmts.push(
331 IfStmt {
332 span: DUMMY_SP,
333 test: ret.clone().make_eq(quote_str!("break")).into(),
334 cons: BreakStmt {
335 span: DUMMY_SP,
336 label: None,
337 }
338 .into(),
339 alt: None,
340 }
341 .into(),
342 );
343 }
344
345 if !flow_helper.label.is_empty() {
346 stmts.push(
347 SwitchStmt {
348 span: DUMMY_SP,
349 discriminant: Box::new(ret.into()),
350 cases: flow_helper
351 .label
352 .into_iter()
353 .map(|(key, label)| SwitchCase {
354 span: DUMMY_SP,
355 test: Some(Box::new(key.into())),
356 cons: vec![match label {
357 Label::Break(id) => Stmt::Break(BreakStmt {
358 span: DUMMY_SP,
359 label: Some(id),
360 }),
361
362 Label::Continue(id) => Stmt::Continue(ContinueStmt {
363 span: DUMMY_SP,
364 label: Some(id),
365 }),
366 }],
367 })
368 .collect(),
369 }
370 .into(),
371 );
372 }
373
374 *body = Box::new(
375 BlockStmt {
376 span: DUMMY_SP,
377 stmts,
378 ..Default::default()
379 }
380 .into(),
381 );
382 return;
383 }
384
385 *body = Box::new(call.take().into_stmt());
386 }
387 }
388
389 fn blockify_for_stmt_body(&self, body: &mut Box<Stmt>) -> bool {
405 if !body.is_block() {
406 *body = Box::new(
407 BlockStmt {
408 span: Default::default(),
409 stmts: vec![*body.take()],
410 ..Default::default()
411 }
412 .into(),
413 );
414 true
415 } else {
416 false
417 }
418 }
419
420 fn undo_blockify_for_stmt_body(&self, body: &mut Box<Stmt>, blockifyed: bool) {
421 if blockifyed {
422 let stmt = body
423 .as_mut_block()
424 .and_then(|block| (block.stmts.len() == 1).then(|| block.stmts[0].take()));
425 if let Some(stmt) = stmt {
426 *body = Box::new(stmt)
427 }
428 }
429 }
430}
431
432#[swc_trace]
433impl VisitMut for BlockScoping {
434 noop_visit_mut_type!(fail);
435
436 fn visit_mut_arrow_expr(&mut self, n: &mut ArrowExpr) {
437 n.params.visit_mut_with(self);
438 self.visit_mut_with_scope(ScopeKind::Fn, &mut n.body);
439 }
440
441 fn visit_mut_block_stmt(&mut self, n: &mut BlockStmt) {
442 let vars = take(&mut self.vars);
443 n.visit_mut_children_with(self);
444 debug_assert_eq!(self.vars, Vec::new());
445 self.vars = vars;
446 }
447
448 fn visit_mut_constructor(&mut self, f: &mut Constructor) {
449 f.key.visit_mut_with(self);
450 f.params.visit_mut_with(self);
451 self.visit_mut_with_scope(ScopeKind::Fn, &mut f.body);
452 }
453
454 fn visit_mut_do_while_stmt(&mut self, node: &mut DoWhileStmt) {
455 self.visit_mut_with_scope(ScopeKind::new_loop(), &mut node.body);
456
457 node.test.visit_mut_with(self);
458 self.handle_capture_of_vars(&mut node.body);
459 }
460
461 fn visit_mut_for_in_stmt(&mut self, node: &mut ForInStmt) {
462 let blockifyed = self.blockify_for_stmt_body(&mut node.body);
463 let lexical_var = if let ForHead::VarDecl(decl) = &node.left {
464 find_lexical_vars(decl)
465 } else {
466 Vec::new()
467 };
468 let args = lexical_var.clone();
469
470 self.visit_mut_with_scope(ScopeKind::Block, &mut node.left);
471
472 node.right.visit_mut_with(self);
473
474 let kind = ScopeKind::Loop {
475 lexical_var,
476 args,
477 used: Vec::new(),
478 mutated: Default::default(),
479 };
480
481 self.visit_mut_with_scope(kind, &mut node.body);
482 self.handle_capture_of_vars(&mut node.body);
483 self.undo_blockify_for_stmt_body(&mut node.body, blockifyed);
484 }
485
486 fn visit_mut_for_of_stmt(&mut self, node: &mut ForOfStmt) {
487 let blockifyed = self.blockify_for_stmt_body(&mut node.body);
488 let vars = if let ForHead::VarDecl(decl) = &node.left {
489 find_lexical_vars(decl)
490 } else {
491 Vec::new()
492 };
493
494 self.visit_mut_with_scope(ScopeKind::Block, &mut node.left);
495
496 let args = vars.clone();
497
498 node.right.visit_mut_with(self);
499
500 let kind = ScopeKind::Loop {
501 lexical_var: vars,
502 args,
503 used: Vec::new(),
504 mutated: Default::default(),
505 };
506
507 self.visit_mut_with_scope(kind, &mut node.body);
508 self.handle_capture_of_vars(&mut node.body);
509 self.undo_blockify_for_stmt_body(&mut node.body, blockifyed);
510 }
511
512 fn visit_mut_for_stmt(&mut self, node: &mut ForStmt) {
513 let blockifyed = self.blockify_for_stmt_body(&mut node.body);
514 let lexical_var = if let Some(VarDeclOrExpr::VarDecl(decl)) = &node.init {
515 find_lexical_vars(decl)
516 } else {
517 Vec::new()
518 };
519
520 node.init.visit_mut_with(self);
521 let args = lexical_var.clone();
522
523 node.test.visit_mut_with(self);
524 node.update.visit_mut_with(self);
525
526 let kind = ScopeKind::Loop {
527 lexical_var,
528 args,
529 used: Vec::new(),
530 mutated: Default::default(),
531 };
532 self.visit_mut_with_scope(kind, &mut node.body);
533 self.handle_capture_of_vars(&mut node.body);
534 self.undo_blockify_for_stmt_body(&mut node.body, blockifyed);
535 }
536
537 fn visit_mut_function(&mut self, f: &mut Function) {
538 f.params.visit_mut_with(self);
539 f.decorators.visit_mut_with(self);
540 self.visit_mut_with_scope(ScopeKind::Fn, &mut f.body);
541 }
542
543 fn visit_mut_getter_prop(&mut self, f: &mut GetterProp) {
544 f.key.visit_mut_with(self);
545 self.visit_mut_with_scope(ScopeKind::Fn, &mut f.body);
546 }
547
548 fn visit_mut_ident(&mut self, node: &mut Ident) {
549 let id = node.to_id();
550 self.mark_as_used(id);
551 }
552
553 fn visit_mut_module_items(&mut self, stmts: &mut Vec<ModuleItem>) {
554 self.visit_mut_stmt_like(stmts);
555 }
556
557 fn visit_mut_setter_prop(&mut self, f: &mut SetterProp) {
558 f.key.visit_mut_with(self);
559 f.param.visit_mut_with(self);
560 self.visit_mut_with_scope(ScopeKind::Fn, &mut f.body);
561 }
562
563 fn visit_mut_stmts(&mut self, n: &mut Vec<Stmt>) {
564 self.visit_mut_stmt_like(n);
565 }
566
567 fn visit_mut_switch_case(&mut self, n: &mut SwitchCase) {
568 let old_vars = self.vars.take();
569
570 n.visit_mut_children_with(self);
571
572 self.vars = old_vars;
573 }
574
575 fn visit_mut_var_decl(&mut self, var: &mut VarDecl) {
576 let old = self.var_decl_kind;
577 self.var_decl_kind = var.kind;
578 if let Some(ScopeKind::Loop { lexical_var, .. }) = self.scope.last_mut() {
579 lexical_var.extend(find_lexical_vars(var));
580 }
581
582 var.visit_mut_children_with(self);
583
584 self.var_decl_kind = old;
585
586 var.kind = VarDeclKind::Var;
587 }
588
589 fn visit_mut_var_declarator(&mut self, var: &mut VarDeclarator) {
590 var.visit_mut_children_with(self);
591
592 if self.in_loop_body() && var.init.is_none() {
593 if self.var_decl_kind == VarDeclKind::Var {
594 var.init = None
595 } else {
596 var.init = Some(Expr::undefined(var.span()))
597 }
598 }
599 }
600
601 fn visit_mut_while_stmt(&mut self, node: &mut WhileStmt) {
602 self.visit_mut_with_scope(ScopeKind::new_loop(), &mut node.body);
603
604 node.test.visit_mut_with(self);
605 self.handle_capture_of_vars(&mut node.body);
606 }
607}
608
609impl BlockScoping {
610 fn visit_mut_stmt_like<T>(&mut self, stmts: &mut Vec<T>)
611 where
612 T: StmtLike,
613 Vec<T>: VisitMutWith<Self>,
614 {
615 stmts.visit_mut_children_with(self);
616
617 if !self.vars.is_empty() {
618 prepend_stmt(
619 stmts,
620 T::from(
621 VarDecl {
622 span: DUMMY_SP,
623 kind: VarDeclKind::Var,
624 declare: false,
625 decls: take(&mut self.vars),
626 ..Default::default()
627 }
628 .into(),
629 ),
630 );
631 }
632 }
633}
634
635fn find_lexical_vars(node: &VarDecl) -> Vec<Id> {
636 if node.kind == VarDeclKind::Var {
637 return Vec::new();
638 }
639
640 find_pat_ids(&node.decls)
641}
642
643struct FlowHelper<'a> {
644 has_break: bool,
645 has_return: bool,
646 has_yield: bool,
647 has_await: bool,
648
649 label: IndexMap<Atom, Label>,
651 inner_label: FxHashSet<Atom>,
652 all: &'a Vec<Id>,
653 mutated: FxHashMap<Id, SyntaxContext>,
654 in_switch_case: bool,
655
656 in_nested_loop: bool,
657}
658
659enum Label {
660 Break(Ident),
661 Continue(Ident),
662}
663
664impl FlowHelper<'_> {
665 fn check(&mut self, i: Id) {
666 if self.all.contains(&i) {
667 self.mutated.insert(
668 i,
669 SyntaxContext::empty().apply_mark(Mark::fresh(Mark::root())),
670 );
671 }
672 }
673
674 fn has_outer_label(&self, label: &Option<Ident>) -> bool {
675 match label {
676 Some(l) => !self.inner_label.contains(&l.sym),
677 None => false,
678 }
679 }
680}
681
682#[swc_trace]
683impl VisitMut for FlowHelper<'_> {
684 noop_visit_mut_type!(fail);
685
686 fn visit_mut_arrow_expr(&mut self, _n: &mut ArrowExpr) {}
688
689 fn visit_mut_assign_expr(&mut self, n: &mut AssignExpr) {
690 match &n.left {
691 AssignTarget::Simple(e) => {
692 if let SimpleAssignTarget::Ident(i) = e {
693 self.check(i.to_id());
694 }
695 }
696 AssignTarget::Pat(p) => {
697 let ids: Vec<Id> = find_pat_ids(p);
698
699 for id in ids {
700 self.check(id);
701 }
702 }
703 }
704
705 n.visit_mut_children_with(self);
706 }
707
708 fn visit_mut_await_expr(&mut self, e: &mut AwaitExpr) {
709 e.visit_mut_children_with(self);
710
711 self.has_await = true;
712 }
713
714 fn visit_mut_do_while_stmt(&mut self, s: &mut DoWhileStmt) {
716 let old = self.in_nested_loop;
717 self.in_nested_loop = true;
718 s.visit_mut_children_with(self);
719 self.in_nested_loop = old;
720 }
721
722 fn visit_mut_for_in_stmt(&mut self, s: &mut ForInStmt) {
724 let old = self.in_nested_loop;
725 self.in_nested_loop = true;
726 s.visit_mut_children_with(self);
727 self.in_nested_loop = old;
728 }
729
730 fn visit_mut_for_of_stmt(&mut self, s: &mut ForOfStmt) {
732 let old = self.in_nested_loop;
733 self.in_nested_loop = true;
734 s.visit_mut_children_with(self);
735 self.in_nested_loop = old;
736 }
737
738 fn visit_mut_for_stmt(&mut self, s: &mut ForStmt) {
740 let old = self.in_nested_loop;
741 self.in_nested_loop = true;
742 s.visit_mut_children_with(self);
743 self.in_nested_loop = old;
744 }
745
746 fn visit_mut_function(&mut self, _f: &mut Function) {}
748
749 fn visit_mut_getter_prop(&mut self, _f: &mut GetterProp) {}
751
752 fn visit_mut_setter_prop(&mut self, _f: &mut SetterProp) {}
754
755 fn visit_mut_labeled_stmt(&mut self, l: &mut LabeledStmt) {
756 self.inner_label.insert(l.label.sym.clone());
757
758 l.visit_mut_children_with(self);
759 }
760
761 fn visit_mut_stmt(&mut self, node: &mut Stmt) {
762 let span = node.span();
763
764 match node {
765 Stmt::Continue(ContinueStmt { label, .. }) => {
766 if self.in_nested_loop && !self.has_outer_label(label) {
767 return;
768 }
769 let value = if let Some(label) = label {
770 let value: Atom = format!("continue|{}", label.sym).into();
771 self.label
772 .insert(value.clone(), Label::Continue(label.clone()));
773 value
774 } else {
775 "continue".into()
776 };
777
778 *node = ReturnStmt {
779 span,
780 arg: Some(
781 Lit::Str(Str {
782 span,
783 value,
784 raw: None,
785 })
786 .into(),
787 ),
788 }
789 .into();
790 }
791 Stmt::Break(BreakStmt { label, .. }) => {
792 if (self.in_switch_case || self.in_nested_loop) && !self.has_outer_label(label) {
793 return;
794 }
795 let value = if let Some(label) = label {
796 let value: Atom = format!("break|{}", label.sym).into();
797 self.label
798 .insert(value.clone(), Label::Break(label.clone()));
799 value
800 } else {
801 self.has_break = true;
802 "break".into()
803 };
804 *node = ReturnStmt {
805 span,
806 arg: Some(
807 Lit::Str(Str {
808 span,
809 value,
810 raw: None,
811 })
812 .into(),
813 ),
814 }
815 .into();
816 }
817 Stmt::Return(s) => {
818 self.has_return = true;
819 s.visit_mut_with(self);
820
821 *node = ReturnStmt {
822 span,
823 arg: Some(
824 ObjectLit {
825 span,
826 props: vec![PropOrSpread::Prop(Box::new(Prop::KeyValue(
827 KeyValueProp {
828 key: PropName::Ident(IdentName::new("v".into(), DUMMY_SP)),
829 value: s.arg.take().unwrap_or_else(|| {
830 Box::new(Expr::Unary(UnaryExpr {
831 span: DUMMY_SP,
832 op: op!("void"),
833 arg: Expr::undefined(DUMMY_SP),
834 }))
835 }),
836 },
837 )))],
838 }
839 .into(),
840 ),
841 }
842 .into();
843 }
844 _ => node.visit_mut_children_with(self),
845 }
846 }
847
848 fn visit_mut_switch_case(&mut self, n: &mut SwitchCase) {
849 let old = self.in_switch_case;
850 self.in_switch_case = true;
851
852 n.visit_mut_children_with(self);
853
854 self.in_switch_case = old;
855 }
856
857 fn visit_mut_update_expr(&mut self, n: &mut UpdateExpr) {
858 if let Expr::Ident(ref i) = *n.arg {
859 self.check(i.to_id())
860 }
861 n.visit_mut_children_with(self);
862 }
863
864 fn visit_mut_while_stmt(&mut self, s: &mut WhileStmt) {
866 let old = self.in_nested_loop;
867 self.in_nested_loop = true;
868 s.visit_mut_children_with(self);
869 self.in_nested_loop = old;
870 }
871
872 fn visit_mut_yield_expr(&mut self, e: &mut YieldExpr) {
873 e.visit_mut_children_with(self);
874
875 self.has_yield = true;
876 }
877}
878
879struct MutationHandler<'a> {
880 map: &'a mut FxHashMap<Id, SyntaxContext>,
881 in_function: bool,
882}
883
884impl MutationHandler<'_> {
885 fn make_reassignment(&self, orig: Option<Box<Expr>>) -> Expr {
886 if self.map.is_empty() {
887 return *orig.unwrap_or_else(|| Expr::undefined(DUMMY_SP));
888 }
889
890 let mut exprs = Vec::with_capacity(self.map.len() + 1);
891
892 for (id, ctxt) in &*self.map {
893 exprs.push(
894 AssignExpr {
895 span: DUMMY_SP,
896 left: Ident::new(id.0.clone(), DUMMY_SP, id.1).into(),
897 op: op!("="),
898 right: Box::new(Ident::new(id.0.clone(), DUMMY_SP, *ctxt).into()),
899 }
900 .into(),
901 );
902 }
903 exprs.push(orig.unwrap_or_else(|| Expr::undefined(DUMMY_SP)));
904
905 SeqExpr {
906 span: DUMMY_SP,
907 exprs,
908 }
909 .into()
910 }
911}
912
913#[swc_trace]
914impl VisitMut for MutationHandler<'_> {
915 noop_visit_mut_type!(fail);
916
917 visit_mut_obj_and_computed!();
918
919 fn visit_mut_arrow_expr(&mut self, n: &mut ArrowExpr) {
920 let old = self.in_function;
921 self.in_function = true;
922
923 n.visit_mut_children_with(self);
924
925 self.in_function = old;
926 }
927
928 fn visit_mut_function(&mut self, n: &mut Function) {
929 let old = self.in_function;
930 self.in_function = true;
931
932 n.visit_mut_children_with(self);
933
934 self.in_function = old;
935 }
936
937 fn visit_mut_ident(&mut self, n: &mut Ident) {
938 if let Some(&ctxt) = self.map.get(&n.to_id()) {
939 n.ctxt = ctxt;
940 }
941 }
942
943 fn visit_mut_return_stmt(&mut self, n: &mut ReturnStmt) {
944 n.visit_mut_children_with(self);
945 if self.in_function || self.map.is_empty() {
946 return;
947 }
948
949 let val = n.arg.take();
950
951 n.arg = Some(Box::new(self.make_reassignment(val)))
952 }
953}