1use std::collections::HashSet;
40
41use phf::{phf_map, phf_set};
42use swc_core::{
43 common::{Mark, comments::Comments},
44 ecma::{
45 ast::*,
46 utils::prop_name_eq,
47 visit::{Visit, VisitWith, noop_visit_type},
48 },
49};
50use turbopack_core::module::ModuleSideEffects;
51
52use crate::{
53 analyzer::cjs_ast::{
54 as_exports_define_property, is_cjs_export_member, is_global, is_module_dot_exports,
55 is_module_exports_chain,
56 },
57 utils::unparen,
58};
59
60macro_rules! check_side_effects {
63 ($self:expr) => {
64 if $self.has_side_effects {
65 return;
66 }
67 };
68}
69
70static KNOWN_PURE_FUNCTIONS: phf::Map<&'static str, phf::Set<&'static str>> = phf_map! {
81 "Math" => phf_set! {
82 "abs", "acos", "acosh", "asin", "asinh", "atan", "atan2", "atanh", "cbrt", "ceil",
83 "clz32", "cos", "cosh", "exp", "expm1", "floor", "fround", "hypot", "imul", "log",
84 "log10", "log1p", "log2", "max", "min", "pow", "round", "sign", "sin", "sinh",
85 "sqrt", "tan", "tanh", "trunc",
86 },
87 "String" => phf_set! {
89 "fromCharCode", "fromCodePoint", "raw",
90 },
91 "Number" => phf_set! {
93 "isFinite", "isInteger", "isNaN", "isSafeInteger", "parseFloat", "parseInt",
94 },
95 "Object" => phf_set! {
97 "keys", "values", "entries", "hasOwn", "getOwnPropertyNames", "getOwnPropertySymbols",
98 "getOwnPropertyDescriptor", "getOwnPropertyDescriptors", "getPrototypeOf", "is",
99 "isExtensible", "isFrozen", "isSealed",
100 },
101 "Array" => phf_set! {
103 "isArray", "from", "of",
104 },
105 "Symbol" => phf_set! {
107 "for", "keyFor"
108 },
109};
110
111static KNOWN_PURE_GLOBAL_FUNCTIONS: phf::Set<&'static str> = phf_set! {
116 "String",
117 "Number",
118 "Symbol",
119 "Boolean",
120 "isNaN",
121 "isFinite",
122 "parseInt",
123 "parseFloat",
124 "decodeURI",
125 "decodeURIComponent",
126};
127
128static KNOWN_PURE_CONSTRUCTORS: phf::Set<&'static str> = phf_set! {
133 "Set",
135 "Map",
136 "WeakSet",
137 "WeakMap",
138 "RegExp",
140 "Array",
142 "Object",
143 "Int8Array",
145 "Uint8Array",
146 "Uint8ClampedArray",
147 "Int16Array",
148 "Uint16Array",
149 "Int32Array",
150 "Uint32Array",
151 "Float32Array",
152 "Float64Array",
153 "BigInt64Array",
154 "BigUint64Array",
155 "Date",
157 "Error",
158 "TypeError",
159 "RangeError",
160 "SyntaxError",
161 "ReferenceError",
162 "URIError",
163 "EvalError",
164 "Promise",
165 "ArrayBuffer",
166 "DataView",
167 "URL",
168 "URLSearchParams",
169 "String",
171 "Number",
172 "Symbol",
173 "Boolean",
174};
175
176static KNOWN_PURE_STRING_PROTOTYPE_METHODS: phf::Set<&'static str> = phf_set! {
184 "toLowerCase",
186 "toUpperCase",
187 "toLocaleLowerCase",
188 "toLocaleUpperCase",
189 "charAt",
190 "charCodeAt",
191 "codePointAt",
192 "slice",
193 "substring",
194 "substr",
195 "indexOf",
196 "lastIndexOf",
197 "includes",
198 "startsWith",
199 "endsWith",
200 "search",
201 "match",
202 "matchAll",
203 "trim",
204 "trimStart",
205 "trimEnd",
206 "trimLeft",
207 "trimRight",
208 "repeat",
209 "padStart",
210 "padEnd",
211 "concat",
212 "split",
213 "replace",
214 "replaceAll",
215 "normalize",
216 "localeCompare",
217 "isWellFormed",
218 "toString",
219 "valueOf",
220};
221
222static KNOWN_PURE_ARRAY_PROTOTYPE_METHODS: phf::Set<&'static str> = phf_set! {
224 "map",
226 "filter",
227 "reduce",
228 "reduceRight",
229 "find",
230 "findIndex",
231 "findLast",
232 "findLastIndex",
233 "some",
234 "every",
235 "flat",
236 "flatMap",
237 "at",
239 "slice",
240 "concat",
241 "includes",
242 "indexOf",
243 "lastIndexOf",
244 "join",
245 "toLocaleString",
247 "toReversed",
248 "toSorted",
249 "toSpliced",
250 "with",
251};
252
253static KNOWN_PURE_OBJECT_PROTOTYPE_METHODS: phf::Set<&'static str> = phf_set! {
254 "hasOwnProperty",
255 "propertyIsEnumerable",
256 "toString",
257 "valueOf",
258};
259
260static KNOWN_PURE_NUMBER_PROTOTYPE_METHODS: phf::Set<&'static str> = phf_set! {
262 "toExponential", "toFixed", "toPrecision", "toLocaleString",
263};
264
265static KNOWN_PURE_REGEXP_PROTOTYPE_METHODS: phf::Set<&'static str> = phf_set! {
275 "test", "exec",
276};
277
278fn is_object_or_array_literal(expr: &Expr) -> bool {
280 matches!(unparen(expr), Expr::Object(_) | Expr::Array(_))
281}
282
283fn root_identifier(expr: &Expr) -> Option<&Ident> {
286 match unparen(expr) {
287 Expr::Ident(ident) => Some(ident),
288 Expr::Member(member) => root_identifier(&member.obj),
289 _ => None,
290 }
291}
292
293fn collect_safe_assignment_constant_ids(program: &Program) -> HashSet<Id> {
305 struct Collector {
309 ids: HashSet<Id>,
310 }
311 impl Visit for Collector {
312 noop_visit_type!();
313 fn visit_var_decl(&mut self, decl: &VarDecl) {
314 if decl.kind == VarDeclKind::Const {
315 for d in &decl.decls {
316 if let (Pat::Ident(binding), Some(init)) = (&d.name, d.init.as_deref())
317 && is_object_or_array_literal(init)
318 && is_fresh_value(init)
319 && !contains_getters_or_setters(init)
320 {
321 self.ids.insert(binding.id.to_id());
322 }
323 }
324 }
325 decl.visit_children_with(self);
326 }
327 fn visit_function(&mut self, _: &Function) {}
328 fn visit_arrow_expr(&mut self, _: &ArrowExpr) {}
329 }
330 let mut collector = Collector {
331 ids: HashSet::new(),
332 };
333 program.visit_with(&mut collector);
334 let mut ids = collector.ids;
335
336 for_each_top_level_expr(program, |expr| {
341 if let Expr::Assign(assign) = expr
342 && assign.op == AssignOp::Assign
343 && let AssignTarget::Simple(SimpleAssignTarget::Member(member)) = &assign.left
344 && (!is_fresh_value(&assign.right) || contains_getters_or_setters(&assign.right))
345 && let Some(root) = root_identifier(&member.obj)
346 {
347 ids.remove(&root.to_id());
348 }
349 });
350
351 ids
352}
353
354fn is_fresh_value(expr: &Expr) -> bool {
357 match unparen(expr) {
358 Expr::Lit(_) | Expr::Tpl(_) | Expr::Fn(_) | Expr::Arrow(_) => true,
359 Expr::Object(obj) => obj.props.iter().all(|prop| match prop {
360 PropOrSpread::Prop(prop) => match &**prop {
361 Prop::KeyValue(kv) => is_fresh_value(&kv.value),
362 Prop::Method(_) => true,
363 Prop::Shorthand(_) | Prop::Getter(_) | Prop::Setter(_) | Prop::Assign(_) => false,
364 },
365 PropOrSpread::Spread(spread) => is_fresh_value(&spread.expr),
366 }),
367 Expr::Array(arr) => arr
368 .elems
369 .iter()
370 .flatten()
371 .all(|elem| is_fresh_value(&elem.expr)),
372 _ => false,
373 }
374}
375
376fn contains_getters_or_setters(expr: &Expr) -> bool {
383 match unparen(expr) {
384 Expr::Object(obj) => obj.props.iter().any(|prop| match prop {
385 PropOrSpread::Prop(prop) => match &**prop {
386 Prop::Getter(_) | Prop::Setter(_) => true,
387 Prop::KeyValue(kv) => contains_getters_or_setters(&kv.value),
388 Prop::Method(_) | Prop::Shorthand(_) | Prop::Assign(_) => false,
389 },
390 PropOrSpread::Spread(spread) => contains_getters_or_setters(&spread.expr),
391 }),
392 Expr::Array(arr) => arr
393 .elems
394 .iter()
395 .flatten()
396 .any(|elem| contains_getters_or_setters(&elem.expr)),
397 _ => false,
398 }
399}
400
401fn for_each_top_level_expr(program: &Program, f: impl FnMut(&Expr)) {
410 struct Collector<F> {
411 f: F,
412 }
413 impl<F: FnMut(&Expr)> Visit for Collector<F> {
414 noop_visit_type!();
415 fn visit_expr(&mut self, n: &Expr) {
416 (self.f)(n);
417 n.visit_children_with(self);
418 }
419 fn visit_function(&mut self, _: &Function) {}
421 fn visit_arrow_expr(&mut self, _: &ArrowExpr) {}
422 fn visit_constructor(&mut self, _: &Constructor) {}
423 }
424 program.visit_with(&mut Collector { f });
425}
426
427fn module_exports_is_tainted(program: &Program, unresolved_mark: Mark) -> bool {
436 let mut tainted = false;
437 for_each_top_level_expr(program, |expr| {
438 if let Expr::Assign(assign) = expr
439 && assign.op == AssignOp::Assign
440 && let AssignTarget::Simple(SimpleAssignTarget::Member(member)) = &assign.left
441 && is_module_dot_exports(member, unresolved_mark)
442 && (!is_object_or_array_literal(&assign.right) || !is_fresh_value(&assign.right))
443 {
444 tainted = true;
445 }
446 });
447 tainted
448}
449
450fn module_exports_has_accessor(program: &Program, unresolved_mark: Mark) -> bool {
458 let mut found = false;
459 for_each_top_level_expr(program, |expr| match expr {
460 Expr::Assign(assign) => {
461 if assign.op == AssignOp::Assign
462 && let AssignTarget::Simple(SimpleAssignTarget::Member(member)) = &assign.left
463 && is_cjs_export_member(member, unresolved_mark)
464 && contains_getters_or_setters(&assign.right)
465 {
466 found = true;
467 }
468 }
469 Expr::Call(call) => {
471 let is_accessor_key = |key: &PropName| {
472 matches!(key, PropName::Computed(_))
475 || prop_name_eq(key, "get")
476 || prop_name_eq(key, "set")
477 };
478 if let Some((_, descriptor)) = as_exports_define_property(call, unresolved_mark)
479 && descriptor.props.iter().any(|prop| match prop {
480 PropOrSpread::Prop(prop) => match &**prop {
481 Prop::KeyValue(kv) => {
484 is_accessor_key(&kv.key) || contains_getters_or_setters(&kv.value)
485 }
486 Prop::Method(method) => is_accessor_key(&method.key),
487 Prop::Shorthand(ident) => matches!(ident.sym.as_ref(), "get" | "set"),
488 _ => false,
489 },
490 PropOrSpread::Spread(_) => true,
491 })
492 {
493 found = true;
494 }
495 }
496 _ => {}
497 });
498 found
499}
500
501pub fn compute_module_evaluation_side_effects(
503 program: &Program,
504 comments: &dyn Comments,
505 unresolved_mark: Mark,
506) -> ModuleSideEffects {
507 let module_exports_tainted = module_exports_is_tainted(program, unresolved_mark);
508 let module_exports_has_accessor = module_exports_has_accessor(program, unresolved_mark);
509 let safe_assignment_constant_ids = collect_safe_assignment_constant_ids(program);
510 let mut visitor = SideEffectVisitor::new(
511 comments,
512 unresolved_mark,
513 module_exports_tainted,
514 module_exports_has_accessor,
515 safe_assignment_constant_ids,
516 );
517 program.visit_with(&mut visitor);
518 if visitor.has_side_effects {
519 ModuleSideEffects::SideEffectful
520 } else if visitor.has_imports {
521 ModuleSideEffects::ModuleEvaluationIsSideEffectFree
522 } else {
523 ModuleSideEffects::SideEffectFree
524 }
525}
526
527struct SideEffectVisitor<'a> {
528 comments: &'a dyn Comments,
529 unresolved_mark: Mark,
530 module_exports_tainted: bool,
533 module_exports_has_accessor: bool,
536 safe_assignment_constant_ids: HashSet<Id>,
539 has_side_effects: bool,
540 will_invoke_fn_exprs: bool,
541 has_imports: bool,
542}
543
544impl<'a> SideEffectVisitor<'a> {
545 fn new(
546 comments: &'a dyn Comments,
547 unresolved_mark: Mark,
548 module_exports_tainted: bool,
549 module_exports_has_accessor: bool,
550 safe_assignment_constant_ids: HashSet<Id>,
551 ) -> Self {
552 Self {
553 comments,
554 unresolved_mark,
555 module_exports_tainted,
556 module_exports_has_accessor,
557 safe_assignment_constant_ids,
558 has_side_effects: false,
559 will_invoke_fn_exprs: false,
560 has_imports: false,
561 }
562 }
563
564 fn mark_side_effect(&mut self) {
566 self.has_side_effects = true;
567 }
568
569 fn with_will_invoke_fn_exprs<F>(&mut self, value: bool, f: F)
575 where
576 F: FnOnce(&mut Self),
577 {
578 let old_value = self.will_invoke_fn_exprs;
579 self.will_invoke_fn_exprs = value;
580 f(self);
581 self.will_invoke_fn_exprs = old_value;
582 }
583
584 fn is_pure_annotated(&self, span: swc_core::common::Span) -> bool {
586 self.comments.has_flag(span.lo, "PURE")
587 }
588
589 fn is_known_pure_builtin(&self, callee: &Callee) -> bool {
593 match callee {
594 Callee::Expr(expr) => self.is_known_pure_builtin_function(expr),
595 _ => false,
596 }
597 }
598 fn is_require_or_import(&self, callee: &Callee) -> bool {
602 match callee {
603 Callee::Expr(expr) => {
604 let expr = unparen(expr);
605 if let Expr::Ident(ident) = expr {
606 is_global(ident, "require", self.unresolved_mark)
607 } else {
608 false
609 }
610 }
611
612 Callee::Import(_) => true,
613 _ => false,
614 }
615 }
616
617 fn assign_target_is_pure(&self, target: &AssignTarget) -> bool {
624 match target {
625 AssignTarget::Simple(SimpleAssignTarget::Member(member)) => {
626 self.member_target_is_pure(member)
627 }
628 _ => false,
629 }
630 }
631
632 fn member_target_is_pure(&self, member: &MemberExpr) -> bool {
635 if self.module_exports_tainted && is_module_exports_chain(&member.obj, self.unresolved_mark)
639 {
640 return false;
641 }
642 if is_cjs_export_member(member, self.unresolved_mark) {
646 return !self.module_exports_has_accessor;
647 }
648 let Some(root) = root_identifier(&member.obj) else {
651 return false;
652 };
653 self.safe_assignment_constant_ids.contains(&root.to_id())
654 }
655
656 fn define_property_target_is_pure(&self, call: &CallExpr) -> bool {
659 let Some((_, descriptor)) = as_exports_define_property(call, self.unresolved_mark) else {
660 return false;
661 };
662 let [target, ..] = &call.args[..] else {
663 return false;
664 };
665 if self.module_exports_tainted
666 && is_module_exports_chain(&target.expr, self.unresolved_mark)
667 {
668 return false;
669 }
670 !descriptor.props.iter().any(|prop| {
672 matches!(prop, PropOrSpread::Prop(prop) if matches!(&**prop, Prop::Getter(_) | Prop::Setter(_)))
673 })
674 }
675
676 fn is_known_pure_builtin_function(&self, expr: &Expr) -> bool {
686 match expr {
687 Expr::Member(member) => {
688 let receiver = unparen(&member.obj);
689 match (receiver, &member.prop) {
690 (Expr::Ident(obj), MemberProp::Ident(prop)) => {
692 if obj.ctxt.outer() != self.unresolved_mark {
696 return false;
698 }
699
700 KNOWN_PURE_FUNCTIONS
703 .get(obj.sym.as_ref())
704 .map(|methods| methods.contains(prop.sym.as_ref()))
705 .unwrap_or(false)
706 }
707 (Expr::Lit(lit), MemberProp::Ident(prop)) => {
710 let method_name = prop.sym.as_ref();
711 match lit {
712 Lit::Str(_) => {
713 KNOWN_PURE_STRING_PROTOTYPE_METHODS.contains(method_name)
714 || KNOWN_PURE_OBJECT_PROTOTYPE_METHODS.contains(method_name)
715 }
716 Lit::Num(_) => {
717 KNOWN_PURE_NUMBER_PROTOTYPE_METHODS.contains(method_name)
718 || KNOWN_PURE_OBJECT_PROTOTYPE_METHODS.contains(method_name)
719 }
720 Lit::Bool(_) => {
721 KNOWN_PURE_OBJECT_PROTOTYPE_METHODS.contains(method_name)
722 }
723 Lit::Regex(_) => {
724 KNOWN_PURE_REGEXP_PROTOTYPE_METHODS.contains(method_name)
725 || KNOWN_PURE_OBJECT_PROTOTYPE_METHODS.contains(method_name)
726 }
727 _ => false,
728 }
729 }
730 (Expr::Array(_), MemberProp::Ident(prop)) => {
733 let method_name = prop.sym.as_ref();
734 KNOWN_PURE_ARRAY_PROTOTYPE_METHODS.contains(method_name)
735 || KNOWN_PURE_OBJECT_PROTOTYPE_METHODS.contains(method_name)
736 }
737 (Expr::Object(_), MemberProp::Ident(prop)) => {
738 KNOWN_PURE_OBJECT_PROTOTYPE_METHODS.contains(prop.sym.as_ref())
739 }
740 _ => false,
741 }
742 }
743 Expr::Ident(ident) => {
744 if ident.ctxt.outer() != self.unresolved_mark {
747 return false;
748 }
749
750 KNOWN_PURE_GLOBAL_FUNCTIONS.contains(ident.sym.as_ref())
752 }
753 _ => false,
754 }
755 }
756
757 fn is_known_pure_constructor(&self, expr: &Expr) -> bool {
763 match expr {
764 Expr::Ident(ident) => {
765 if ident.ctxt.outer() != self.unresolved_mark {
768 return false;
769 }
770
771 KNOWN_PURE_CONSTRUCTORS.contains(ident.sym.as_ref())
773 }
774 _ => false,
775 }
776 }
777}
778
779impl<'a> Visit for SideEffectVisitor<'a> {
780 noop_visit_type!();
781 fn visit_program(&mut self, program: &Program) {
783 check_side_effects!(self);
784 program.visit_children_with(self);
785 }
786
787 fn visit_module(&mut self, module: &Module) {
788 check_side_effects!(self);
789
790 for item in &module.body {
792 check_side_effects!(self);
793 item.visit_with(self);
794 }
795 }
796
797 fn visit_script(&mut self, script: &Script) {
798 check_side_effects!(self);
799
800 for stmt in &script.body {
802 check_side_effects!(self);
803 stmt.visit_with(self);
804 }
805 }
806
807 fn visit_module_decl(&mut self, decl: &ModuleDecl) {
809 check_side_effects!(self);
810
811 match decl {
812 ModuleDecl::Import(_) => {
816 self.has_imports = true;
817 }
818
819 ModuleDecl::ExportDecl(export_decl) => {
821 match &export_decl.decl {
823 Decl::Fn(_) => {
824 }
826 Decl::Class(class_decl) => {
827 class_decl.visit_with(self);
830 }
831 Decl::Var(var_decl) => {
832 var_decl.visit_with(self);
834 }
835 _ => {
836 export_decl.decl.visit_with(self);
838 }
839 }
840 }
841
842 ModuleDecl::ExportDefaultDecl(export_default_decl) => {
843 match &export_default_decl.decl {
845 DefaultDecl::Class(cls) => {
846 cls.visit_with(self);
849 }
850 DefaultDecl::Fn(_) => {
851 }
853 DefaultDecl::TsInterfaceDecl(_) => {
854 }
856 }
857 }
858
859 ModuleDecl::ExportDefaultExpr(export_default_expr) => {
860 export_default_expr.expr.visit_with(self);
862 }
863
864 ModuleDecl::ExportNamed(e) => {
866 if e.src.is_some() {
867 self.has_imports = true;
869 }
870 }
871 ModuleDecl::ExportAll(_) => {
872 self.has_imports = true;
874 }
875
876 ModuleDecl::TsExportAssignment(_) | ModuleDecl::TsNamespaceExport(_) => {}
878 ModuleDecl::TsImportEquals(e) => {
879 match &e.module_ref {
882 TsModuleRef::TsEntityName(_) => {}
883 TsModuleRef::TsExternalModuleRef(_) => {
884 self.has_imports = true
886 }
887 }
888 }
889 }
890 }
891
892 fn visit_stmt(&mut self, stmt: &Stmt) {
894 check_side_effects!(self);
895
896 match stmt {
897 Stmt::Expr(expr_stmt) => {
899 expr_stmt.visit_with(self);
900 }
901 Stmt::Decl(Decl::Var(var_decl)) => {
903 var_decl.visit_with(self);
904 }
905 Stmt::Decl(Decl::Fn(_)) => {
907 }
909 Stmt::Decl(Decl::Class(class_decl)) => {
911 class_decl.visit_with(self);
912 }
913 Stmt::Decl(decl) => {
915 decl.visit_with(self);
916 }
917 _ => {
919 self.mark_side_effect();
922 }
923 }
924 }
925
926 fn visit_var_declarator(&mut self, var_decl: &VarDeclarator) {
927 check_side_effects!(self);
928
929 var_decl.name.visit_with(self);
931
932 if let Some(init) = &var_decl.init {
934 init.visit_with(self);
935 }
936 }
937
938 fn visit_expr(&mut self, expr: &Expr) {
940 check_side_effects!(self);
941
942 match expr {
943 Expr::Lit(_) => {
945 }
947 Expr::Ident(_) => {
948 }
950 Expr::Arrow(_) | Expr::Fn(_) => {
951 if self.will_invoke_fn_exprs {
953 self.with_will_invoke_fn_exprs(false, |this| {
955 expr.visit_children_with(this);
956 });
957 }
958 }
959 Expr::Class(class_expr) => {
960 class_expr.class.visit_with(self);
962 }
963 Expr::Array(arr) => {
964 for elem in arr.elems.iter().flatten() {
966 elem.visit_with(self);
967 }
968 }
969 Expr::Object(obj) => {
970 for prop in &obj.props {
972 prop.visit_with(self);
973 }
974 }
975 Expr::Unary(unary) => {
976 if unary.op == UnaryOp::Delete {
978 self.mark_side_effect();
981 } else {
982 unary.arg.visit_with(self);
983 }
984 }
985 Expr::Bin(bin) => {
986 bin.left.visit_with(self);
988 bin.right.visit_with(self);
989 }
990 Expr::Cond(cond) => {
991 cond.test.visit_with(self);
993 cond.cons.visit_with(self);
994 cond.alt.visit_with(self);
995 }
996 Expr::Member(member) => {
997 member.obj.visit_with(self);
1005 member.prop.visit_with(self);
1006 }
1007 Expr::Paren(paren) => {
1008 paren.expr.visit_with(self);
1010 }
1011 Expr::Tpl(tpl) => {
1012 for expr in &tpl.exprs {
1014 expr.visit_with(self);
1015 }
1016 }
1017
1018 Expr::Call(call) => {
1020 if self.is_pure_annotated(call.span) || self.is_known_pure_builtin(&call.callee) {
1022 call.callee.visit_with(self);
1028
1029 self.with_will_invoke_fn_exprs(true, |this| {
1032 call.args.visit_children_with(this);
1033 });
1034 } else if self.is_require_or_import(&call.callee) {
1035 self.has_imports = true;
1036 call.args.visit_children_with(self);
1039 } else if self.define_property_target_is_pure(call) {
1040 call.args.visit_children_with(self);
1041 } else {
1042 self.mark_side_effect();
1044 }
1045 }
1046 Expr::New(new) => {
1047 if self.is_pure_annotated(new.span) || self.is_known_pure_constructor(&new.callee) {
1049 self.with_will_invoke_fn_exprs(true, |this| {
1051 new.args.visit_children_with(this);
1052 });
1053 } else {
1054 self.mark_side_effect();
1056 }
1057 }
1058 Expr::Assign(assign) => {
1059 if assign.op == AssignOp::Assign && self.assign_target_is_pure(&assign.left) {
1068 assign.left.visit_with(self);
1071 assign.right.visit_with(self);
1072 } else {
1073 self.mark_side_effect();
1074 }
1075 }
1076 Expr::Update(_) => {
1077 self.mark_side_effect();
1080 }
1081 Expr::Await(e) => {
1082 e.arg.visit_with(self);
1083 }
1084 Expr::Yield(e) => {
1085 e.arg.visit_with(self);
1086 }
1087 Expr::TaggedTpl(tagged_tpl)
1088 if self.is_known_pure_builtin_function(&tagged_tpl.tag) => {
1091 for arg in &tagged_tpl.tpl.exprs {
1092 arg.visit_with(self);
1093 }
1094 }
1095 Expr::OptChain(opt_chain) => {
1096 opt_chain.base.visit_with(self);
1099 }
1100 Expr::Seq(seq) => {
1101 seq.exprs.visit_children_with(self);
1103 }
1104 Expr::SuperProp(super_prop) => {
1105 super_prop.prop.visit_with(self);
1108 }
1109 Expr::MetaProp(_) => {
1110 }
1113 Expr::JSXMember(_) | Expr::JSXNamespacedName(_) | Expr::JSXEmpty(_) => {
1114 }
1116 Expr::JSXElement(_) | Expr::JSXFragment(_) => {
1117 self.mark_side_effect();
1122 }
1123 Expr::PrivateName(_) => {
1124 }
1126
1127 _ => {
1129 self.mark_side_effect();
1130 }
1131 }
1132 }
1133
1134 fn visit_opt_chain_base(&mut self, base: &OptChainBase) {
1135 check_side_effects!(self);
1136
1137 match base {
1138 OptChainBase::Member(member) => {
1139 member.visit_with(self);
1140 }
1141 OptChainBase::Call(_opt_call) => {
1142 self.mark_side_effect();
1146 }
1147 }
1148 }
1149
1150 fn visit_prop_or_spread(&mut self, prop: &PropOrSpread) {
1151 check_side_effects!(self);
1152
1153 match prop {
1154 PropOrSpread::Spread(spread) => {
1155 spread.expr.visit_with(self);
1156 }
1157 PropOrSpread::Prop(prop) => {
1158 prop.visit_with(self);
1159 }
1160 }
1161 }
1162
1163 fn visit_prop(&mut self, prop: &Prop) {
1164 check_side_effects!(self);
1165
1166 match prop {
1167 Prop::KeyValue(kv) => {
1168 kv.key.visit_with(self);
1169 kv.value.visit_with(self);
1170 }
1171 Prop::Getter(getter) => {
1172 getter.key.visit_with(self);
1173 }
1175 Prop::Setter(setter) => {
1176 setter.key.visit_with(self);
1177 }
1179 Prop::Method(method) => {
1180 method.key.visit_with(self);
1181 }
1183 Prop::Shorthand(_) => {
1184 }
1186 Prop::Assign(_) => {
1187 }
1190 }
1191 }
1192
1193 fn visit_prop_name(&mut self, prop_name: &PropName) {
1194 check_side_effects!(self);
1195
1196 match prop_name {
1197 PropName::Computed(computed) => {
1198 computed.expr.visit_with(self);
1200 }
1201 _ => {
1202 }
1204 }
1205 }
1206
1207 fn visit_class(&mut self, class: &Class) {
1208 check_side_effects!(self);
1209
1210 for decorator in &class.decorators {
1212 decorator.visit_with(self);
1213 }
1214
1215 if let Some(super_class) = &class.super_class {
1217 super_class.visit_with(self);
1218 }
1219
1220 for member in &class.body {
1222 member.visit_with(self);
1223 }
1224 }
1225
1226 fn visit_class_member(&mut self, member: &ClassMember) {
1227 check_side_effects!(self);
1228
1229 match member {
1230 ClassMember::StaticBlock(block) => {
1232 for stmt in &block.body.stmts {
1235 stmt.visit_with(self);
1236 }
1237 }
1238 ClassMember::ClassProp(class_prop) if class_prop.is_static => {
1240 for decorator in &class_prop.decorators {
1242 decorator.visit_with(self);
1243 }
1244 class_prop.key.visit_with(self);
1246 if let Some(value) = &class_prop.value {
1248 value.visit_with(self);
1249 }
1250 }
1251 ClassMember::Method(method) => {
1253 for decorator in &method.function.decorators {
1255 decorator.visit_with(self);
1256 }
1257 method.key.visit_with(self);
1258 }
1260 ClassMember::Constructor(constructor) => {
1261 constructor.key.visit_with(self);
1262 }
1264 ClassMember::PrivateMethod(private_method) => {
1265 for decorator in &private_method.function.decorators {
1267 decorator.visit_with(self);
1268 }
1269 private_method.key.visit_with(self);
1270 }
1272 ClassMember::ClassProp(class_prop) => {
1273 for decorator in &class_prop.decorators {
1275 decorator.visit_with(self);
1276 }
1277 class_prop.key.visit_with(self);
1279 }
1281 ClassMember::PrivateProp(private_prop) => {
1282 for decorator in &private_prop.decorators {
1284 decorator.visit_with(self);
1285 }
1286 private_prop.key.visit_with(self);
1287 }
1289 ClassMember::AutoAccessor(auto_accessor) if auto_accessor.is_static => {
1290 for decorator in &auto_accessor.decorators {
1292 decorator.visit_with(self);
1293 }
1294 auto_accessor.key.visit_with(self);
1296 if let Some(value) = &auto_accessor.value {
1297 value.visit_with(self);
1298 }
1299 }
1300 ClassMember::AutoAccessor(auto_accessor) => {
1301 for decorator in &auto_accessor.decorators {
1303 decorator.visit_with(self);
1304 }
1305 auto_accessor.key.visit_with(self);
1307 }
1308 ClassMember::Empty(_) => {
1309 }
1311 ClassMember::TsIndexSignature(_) => {
1312 }
1314 }
1315 }
1316
1317 fn visit_decorator(&mut self, _decorator: &Decorator) {
1318 if self.has_side_effects {
1319 return;
1320 }
1321
1322 self.mark_side_effect();
1326 }
1327
1328 fn visit_pat(&mut self, pat: &Pat) {
1329 check_side_effects!(self);
1330
1331 match pat {
1332 Pat::Object(object_pat) => {
1334 for prop in &object_pat.props {
1335 match prop {
1336 ObjectPatProp::KeyValue(kv) => {
1337 kv.key.visit_with(self);
1339 kv.value.visit_with(self);
1341 }
1342 ObjectPatProp::Assign(assign) => {
1343 if let Some(value) = &assign.value {
1345 value.visit_with(self);
1346 }
1347 }
1348 ObjectPatProp::Rest(rest) => {
1349 rest.arg.visit_with(self);
1351 }
1352 }
1353 }
1354 }
1355 Pat::Array(array_pat) => {
1357 for elem in array_pat.elems.iter().flatten() {
1358 elem.visit_with(self);
1359 }
1360 }
1361 Pat::Assign(assign_pat) => {
1363 assign_pat.right.visit_with(self);
1365 assign_pat.left.visit_with(self);
1367 }
1368 _ => {}
1370 }
1371 }
1372}
1373
1374#[cfg(test)]
1375mod tests {
1376 use swc_core::{
1377 common::{FileName, GLOBALS, Mark, SourceMap, comments::SingleThreadedComments, sync::Lrc},
1378 ecma::{
1379 ast::EsVersion,
1380 parser::{EsSyntax, Syntax, parse_file_as_program},
1381 transforms::base::resolver,
1382 visit::VisitMutWith,
1383 },
1384 };
1385
1386 use super::*;
1387
1388 fn parse_and_check_for_side_effects(code: &str, expected: ModuleSideEffects) {
1390 GLOBALS.set(&Default::default(), || {
1391 let cm = Lrc::new(SourceMap::default());
1392 let fm = cm.new_source_file(Lrc::new(FileName::Anon), code.to_string());
1393
1394 let comments = SingleThreadedComments::default();
1395 let mut errors = vec![];
1396
1397 let mut program = parse_file_as_program(
1398 &fm,
1399 Syntax::Es(EsSyntax {
1400 jsx: true,
1401 decorators: true,
1402 ..Default::default()
1403 }),
1404 EsVersion::latest(),
1405 Some(&comments),
1406 &mut errors,
1407 )
1408 .expect("Failed to parse");
1409
1410 let unresolved_mark = Mark::new();
1412 let top_level_mark = Mark::new();
1413 program.visit_mut_with(&mut resolver(unresolved_mark, top_level_mark, false));
1414
1415 let actual =
1416 compute_module_evaluation_side_effects(&program, &comments, unresolved_mark);
1417
1418 let msg = match expected {
1419 ModuleSideEffects::ModuleEvaluationIsSideEffectFree => {
1420 "Expected code to have no local side effects"
1421 }
1422 ModuleSideEffects::SideEffectFree => "Expected code to be side effect free",
1423 ModuleSideEffects::SideEffectful => "Expected code to have side effects",
1424 };
1425 assert_eq!(actual, expected, "{}:\n{}", msg, code);
1426 })
1427 }
1428
1429 macro_rules! assert_side_effects {
1431 ($name:ident, $code:expr, $expected:expr) => {
1432 #[test]
1433 fn $name() {
1434 parse_and_check_for_side_effects($code, $expected);
1435 }
1436 };
1437 }
1438
1439 macro_rules! side_effects {
1440 ($name:ident, $code:expr) => {
1441 assert_side_effects!($name, $code, ModuleSideEffects::SideEffectful);
1442 };
1443 }
1444
1445 macro_rules! no_side_effects {
1446 ($name:ident, $code:expr) => {
1447 assert_side_effects!($name, $code, ModuleSideEffects::SideEffectFree);
1448 };
1449 }
1450 macro_rules! module_evaluation_is_side_effect_free {
1451 ($name:ident, $code:expr) => {
1452 assert_side_effects!(
1453 $name,
1454 $code,
1455 ModuleSideEffects::ModuleEvaluationIsSideEffectFree
1456 );
1457 };
1458 }
1459
1460 mod basic_tests {
1461 use super::*;
1462
1463 no_side_effects!(test_empty_program, "");
1464
1465 no_side_effects!(test_simple_const_declaration, "const x = 5;");
1466
1467 no_side_effects!(test_simple_let_declaration, "let y = 'string';");
1468
1469 no_side_effects!(test_array_literal, "const arr = [1, 2, 3];");
1470
1471 no_side_effects!(test_object_literal, "const obj = { a: 1, b: 2 };");
1472
1473 no_side_effects!(test_function_declaration, "function foo() { return 1; }");
1474
1475 no_side_effects!(
1476 test_function_expression,
1477 "const foo = function() { return 1; };"
1478 );
1479
1480 no_side_effects!(test_arrow_function, "const foo = () => 1;");
1481 }
1482
1483 mod side_effects_tests {
1484 use super::*;
1485
1486 side_effects!(test_console_log, "console.log('hello');");
1487
1488 side_effects!(test_function_call, "foo();");
1489
1490 side_effects!(test_method_call, "obj.method();");
1491
1492 side_effects!(test_assignment, "x = 5;");
1493
1494 side_effects!(test_member_assignment, "obj.prop = 5;");
1495
1496 side_effects!(test_constructor_call, "new SideEffect();");
1497
1498 side_effects!(test_update_expression, "x++;");
1499 }
1500
1501 mod pure_expressions_tests {
1502 use super::*;
1503
1504 no_side_effects!(test_binary_expression, "const x = 1 + 2;");
1505
1506 no_side_effects!(test_unary_expression, "const x = -5;");
1507
1508 no_side_effects!(test_conditional_expression, "const x = true ? 1 : 2;");
1509
1510 no_side_effects!(test_template_literal, "const x = `hello ${world}`;");
1511
1512 no_side_effects!(test_nested_object, "const obj = { a: { b: { c: 1 } } };");
1513
1514 no_side_effects!(test_nested_array, "const arr = [[1, 2], [3, 4]];");
1515 }
1516
1517 mod import_export_tests {
1518 use super::*;
1519
1520 module_evaluation_is_side_effect_free!(test_import_statement, "import x from 'y';");
1521 module_evaluation_is_side_effect_free!(test_require_statement, "const x = require('y');");
1522
1523 no_side_effects!(test_export_statement, "export default 5;");
1524
1525 no_side_effects!(test_export_const, "export const x = 5;");
1526
1527 side_effects!(
1528 test_export_const_with_side_effect,
1529 "export const x = foo();"
1530 );
1531 }
1532
1533 mod mixed_cases_tests {
1534 use super::*;
1535
1536 side_effects!(test_call_in_initializer, "const x = foo();");
1537
1538 side_effects!(test_call_in_array, "const arr = [1, foo(), 3];");
1539
1540 side_effects!(test_call_in_object, "const obj = { a: foo() };");
1541
1542 no_side_effects!(
1543 test_multiple_declarations_pure,
1544 "const x = 1;\nconst y = 2;\nconst z = 3;"
1545 );
1546
1547 side_effects!(
1548 test_multiple_declarations_with_side_effect,
1549 "const x = 1;\nfoo();\nconst z = 3;"
1550 );
1551
1552 no_side_effects!(test_class_declaration, "class Foo {}");
1553
1554 no_side_effects!(
1555 test_class_with_methods,
1556 "class Foo { method() { return 1; } }"
1557 );
1558 }
1559
1560 mod pure_annotations_tests {
1561 use super::*;
1562
1563 no_side_effects!(test_pure_annotation_function_call, "/*#__PURE__*/ foo();");
1564
1565 no_side_effects!(test_pure_annotation_with_at, "/*@__PURE__*/ foo();");
1566
1567 no_side_effects!(test_pure_annotation_constructor, "/*#__PURE__*/ new Foo();");
1568
1569 no_side_effects!(
1570 test_pure_annotation_in_variable,
1571 "const x = /*#__PURE__*/ foo();"
1572 );
1573
1574 no_side_effects!(
1575 test_pure_annotation_with_pure_args,
1576 "/*#__PURE__*/ foo(1, 2, 3);"
1577 );
1578
1579 side_effects!(
1581 test_pure_annotation_with_impure_args,
1582 "/*#__PURE__*/ foo(bar());"
1583 );
1584
1585 side_effects!(test_without_pure_annotation, "foo();");
1587
1588 no_side_effects!(
1589 test_pure_nested_in_object,
1590 "const obj = { x: /*#__PURE__*/ foo() };"
1591 );
1592
1593 no_side_effects!(test_pure_in_array, "const arr = [/*#__PURE__*/ foo()];");
1594
1595 no_side_effects!(
1596 test_multiple_pure_calls,
1597 "const x = /*#__PURE__*/ foo();\nconst y = /*#__PURE__*/ bar();"
1598 );
1599
1600 side_effects!(
1601 test_mixed_pure_and_impure,
1602 "const x = /*#__PURE__*/ foo();\nbar();\nconst z = /*#__PURE__*/ baz();"
1603 );
1604 }
1605
1606 mod known_pure_builtins_tests {
1607 use super::*;
1608
1609 no_side_effects!(test_math_abs, "const x = Math.abs(-5);");
1610
1611 no_side_effects!(test_math_floor, "const x = Math.floor(3.14);");
1612
1613 no_side_effects!(test_math_max, "const x = Math.max(1, 2, 3);");
1614
1615 no_side_effects!(test_object_keys, "const keys = Object.keys(obj);");
1616
1617 no_side_effects!(test_object_values, "const values = Object.values(obj);");
1618
1619 no_side_effects!(test_object_entries, "const entries = Object.entries(obj);");
1620
1621 no_side_effects!(test_array_is_array, "const result = Array.isArray([]);");
1622
1623 no_side_effects!(
1624 test_string_from_char_code,
1625 "const char = String.fromCharCode(65);"
1626 );
1627
1628 no_side_effects!(test_number_is_nan, "const result = Number.isNaN(x);");
1629
1630 no_side_effects!(
1631 test_multiple_math_calls,
1632 "const x = Math.abs(-5);\nconst y = Math.floor(3.14);\nconst z = Math.max(x, y);"
1633 );
1634
1635 side_effects!(
1637 test_pure_builtin_with_impure_arg,
1638 "const x = Math.abs(foo());"
1639 );
1640
1641 no_side_effects!(
1642 test_pure_builtin_in_expression,
1643 "const x = Math.abs(-5) + Math.floor(3.14);"
1644 );
1645
1646 side_effects!(
1647 test_mixed_builtin_and_impure,
1648 "const x = Math.abs(-5);\nfoo();\nconst z = Object.keys({});"
1649 );
1650
1651 side_effects!(test_unknown_math_property, "const x = Math.random();");
1653
1654 side_effects!(test_object_assign, "Object.assign(target, source);");
1656
1657 no_side_effects!(test_array_from, "const arr = Array.from(iterable);");
1658
1659 no_side_effects!(test_global_is_nan, "const result = isNaN(value);");
1660
1661 no_side_effects!(test_global_is_finite, "const result = isFinite(value);");
1662
1663 no_side_effects!(test_global_parse_int, "const num = parseInt('42', 10);");
1664
1665 no_side_effects!(test_global_parse_float, "const num = parseFloat('3.14');");
1666
1667 no_side_effects!(
1668 test_global_decode_uri,
1669 "const decoded = decodeURI(encoded);"
1670 );
1671
1672 no_side_effects!(
1673 test_global_decode_uri_component,
1674 "const decoded = decodeURIComponent(encoded);"
1675 );
1676
1677 no_side_effects!(
1679 test_global_string_constructor_as_function,
1680 "const str = String(123);"
1681 );
1682
1683 no_side_effects!(
1685 test_global_number_constructor_as_function,
1686 "const num = Number('123');"
1687 );
1688
1689 no_side_effects!(
1691 test_global_boolean_constructor_as_function,
1692 "const bool = Boolean(value);"
1693 );
1694
1695 no_side_effects!(
1697 test_global_symbol_constructor_as_function,
1698 "const sym = Symbol('description');"
1699 );
1700
1701 no_side_effects!(test_symbol_for, "const sym = Symbol.for('description');");
1703
1704 no_side_effects!(
1706 test_symbol_key_for,
1707 "const description = Symbol.keyFor(sym);"
1708 );
1709
1710 side_effects!(
1712 test_global_pure_with_impure_arg,
1713 "const result = isNaN(foo());"
1714 );
1715
1716 side_effects!(
1718 test_shadowed_global_is_nan,
1719 r#"
1720 const isNaN = () => sideEffect();
1721 const result = isNaN(value);
1722 "#
1723 );
1724 }
1725
1726 mod edge_cases_tests {
1727 use super::*;
1728
1729 no_side_effects!(test_computed_property, "const obj = { [key]: value };");
1730
1731 side_effects!(
1732 test_computed_property_with_call,
1733 "const obj = { [foo()]: value };"
1734 );
1735
1736 no_side_effects!(test_spread_in_array, "const arr = [...other];");
1737
1738 no_side_effects!(test_spread_in_object, "const obj = { ...other };");
1739
1740 no_side_effects!(test_destructuring_assignment, "const { a, b } = obj;");
1741
1742 no_side_effects!(test_array_destructuring, "const [a, b] = arr;");
1743
1744 no_side_effects!(test_nested_ternary, "const x = a ? (b ? 1 : 2) : 3;");
1745
1746 no_side_effects!(test_logical_and, "const x = a && b;");
1747
1748 no_side_effects!(test_logical_or, "const x = a || b;");
1749
1750 no_side_effects!(test_nullish_coalescing, "const x = a ?? b;");
1751
1752 no_side_effects!(test_typeof_operator, "const x = typeof y;");
1753
1754 no_side_effects!(test_void_operator, "const x = void 0;");
1755
1756 side_effects!(test_delete_expression, "delete obj.prop;");
1758
1759 no_side_effects!(test_sequence_expression_pure, "const x = (1, 2, 3);");
1760
1761 side_effects!(test_sequence_expression_impure, "const x = (foo(), 2, 3);");
1762
1763 no_side_effects!(test_arrow_with_block, "const foo = () => { return 1; };");
1764
1765 no_side_effects!(
1766 test_class_with_constructor,
1767 "class Foo { constructor() { this.x = 1; } }"
1768 );
1769
1770 no_side_effects!(test_class_extends, "class Foo extends Bar {}");
1771
1772 no_side_effects!(test_async_function, "async function foo() { return 1; }");
1773
1774 no_side_effects!(test_generator_function, "function* foo() { yield 1; }");
1775
1776 side_effects!(test_tagged_template, "const x = tag`hello`;");
1778
1779 no_side_effects!(
1781 test_tagged_template_string_raw,
1782 "const x = String.raw`hello ${world}`;"
1783 );
1784
1785 no_side_effects!(test_regex_literal, "const re = /pattern/g;");
1786
1787 no_side_effects!(test_bigint_literal, "const big = 123n;");
1788
1789 no_side_effects!(test_optional_chaining_pure, "const x = obj?.prop;");
1790
1791 side_effects!(test_optional_chaining_call, "const x = obj?.method();");
1793
1794 no_side_effects!(
1795 test_multiple_exports_pure,
1796 "export const a = 1;\nexport const b = 2;\nexport const c = 3;"
1797 );
1798
1799 no_side_effects!(test_export_function, "export function foo() { return 1; }");
1800
1801 no_side_effects!(test_export_class, "export class Foo {}");
1802
1803 module_evaluation_is_side_effect_free!(test_reexport, "export { foo } from 'bar';");
1804
1805 module_evaluation_is_side_effect_free!(
1807 test_dynamic_import,
1808 "const mod = import('./module');"
1809 );
1810
1811 module_evaluation_is_side_effect_free!(
1812 test_dynamic_import_with_await,
1813 "const mod = await import('./module');"
1814 );
1815
1816 no_side_effects!(test_export_default_expression, "export default 1 + 2;");
1817
1818 side_effects!(
1819 test_export_default_expression_with_side_effect,
1820 "export default foo();"
1821 );
1822
1823 no_side_effects!(
1824 test_export_default_function,
1825 "export default function() { return 1; }"
1826 );
1827
1828 no_side_effects!(test_export_default_class, "export default class Foo {}");
1829
1830 no_side_effects!(
1831 test_export_named_with_pure_builtin,
1832 "export const result = Math.abs(-5);"
1833 );
1834
1835 side_effects!(
1836 test_multiple_exports_mixed,
1837 "export const a = 1;\nexport const b = foo();\nexport const c = 3;"
1838 );
1839 }
1840
1841 mod pure_constructors_tests {
1842 use super::*;
1843
1844 no_side_effects!(test_new_set, "const s = new Set();");
1845
1846 no_side_effects!(test_new_map, "const m = new Map();");
1847
1848 no_side_effects!(test_new_weakset, "const ws = new WeakSet();");
1849
1850 no_side_effects!(test_new_weakmap, "const wm = new WeakMap();");
1851
1852 no_side_effects!(test_new_regexp, "const re = new RegExp('pattern');");
1853
1854 no_side_effects!(test_new_date, "const d = new Date();");
1855
1856 no_side_effects!(test_new_error, "const e = new Error('message');");
1857
1858 no_side_effects!(test_new_promise, "const p = new Promise(() => {});");
1859 side_effects!(
1860 test_new_promise_effectful,
1861 "const p = new Promise(() => {console.log('hello')});"
1862 );
1863
1864 no_side_effects!(test_new_array, "const arr = new Array(10);");
1865
1866 no_side_effects!(test_new_object, "const obj = new Object();");
1867
1868 no_side_effects!(test_new_typed_array, "const arr = new Uint8Array(10);");
1869
1870 no_side_effects!(test_new_url, "const url = new URL('https://example.com');");
1871
1872 no_side_effects!(
1873 test_new_url_search_params,
1874 "const params = new URLSearchParams();"
1875 );
1876
1877 side_effects!(
1879 test_pure_constructor_with_impure_args,
1880 "const s = new Set([foo()]);"
1881 );
1882
1883 no_side_effects!(
1884 test_multiple_pure_constructors,
1885 "const s = new Set();\nconst m = new Map();\nconst re = new RegExp('test');"
1886 );
1887
1888 side_effects!(
1890 test_unknown_constructor,
1891 "const custom = new CustomClass();"
1892 );
1893
1894 side_effects!(
1895 test_mixed_constructors,
1896 "const s = new Set();\nconst custom = new CustomClass();\nconst m = new Map();"
1897 );
1898 }
1899
1900 mod shadowing_detection_tests {
1901 use super::*;
1902
1903 side_effects!(
1905 test_shadowed_math,
1906 r#"
1907 const Math = { abs: () => console.log('side effect') };
1908 const result = Math.abs(-5);
1909 "#
1910 );
1911
1912 side_effects!(
1914 test_shadowed_object,
1915 r#"
1916 const Object = { keys: () => sideEffect() };
1917 const result = Object.keys({});
1918 "#
1919 );
1920
1921 side_effects!(
1923 test_shadowed_array_constructor,
1924 r#"
1925 const Array = class { constructor() { sideEffect(); } };
1926 const arr = new Array();
1927 "#
1928 );
1929
1930 side_effects!(
1932 test_shadowed_set_constructor,
1933 r#"
1934 const Set = class { constructor() { sideEffect(); } };
1935 const s = new Set();
1936 "#
1937 );
1938
1939 side_effects!(
1941 test_shadowed_map_constructor,
1942 r#"
1943 {
1944 const Map = class { constructor() { sideEffect(); } };
1945 const m = new Map();
1946 }
1947 "#
1948 );
1949
1950 no_side_effects!(
1952 test_global_math_not_shadowed,
1953 r#"
1954 const result = Math.abs(-5);
1955 "#
1956 );
1957
1958 no_side_effects!(
1960 test_global_object_not_shadowed,
1961 r#"
1962 const keys = Object.keys({ a: 1, b: 2 });
1963 "#
1964 );
1965
1966 no_side_effects!(
1968 test_global_array_constructor_not_shadowed,
1969 r#"
1970 const arr = new Array(1, 2, 3);
1971 "#
1972 );
1973
1974 side_effects!(
1976 test_shadowed_by_import,
1977 r#"
1978 import { Math } from './custom-math';
1979 const result = Math.abs(-5);
1980 "#
1981 );
1982
1983 side_effects!(
1985 test_nested_scope_shadowing,
1986 r#"
1987 {
1988 const Math = { floor: () => sideEffect() };
1989 const result = Math.floor(4.5);
1990 }
1991 "#
1992 );
1993
1994 no_side_effects!(
1998 test_parameter_shadowing,
1999 r#"
2000 function test(RegExp) {
2001 return new RegExp('test');
2002 }
2003 "#
2004 );
2005
2006 side_effects!(
2008 test_shadowing_with_var,
2009 r#"
2010 var Number = { isNaN: () => sideEffect() };
2011 const check = Number.isNaN(123);
2012 "#
2013 );
2014
2015 no_side_effects!(
2017 test_global_regexp_not_shadowed,
2018 r#"
2019 const re = new RegExp('[a-z]+');
2020 "#
2021 );
2022 }
2023
2024 mod literal_receiver_methods_tests {
2025 use super::*;
2026
2027 no_side_effects!(
2029 test_string_literal_to_lower_case,
2030 r#"const result = "HELLO".toLowerCase();"#
2031 );
2032
2033 no_side_effects!(
2034 test_string_literal_to_upper_case,
2035 r#"const result = "hello".toUpperCase();"#
2036 );
2037
2038 no_side_effects!(
2039 test_string_literal_slice,
2040 r#"const result = "hello world".slice(0, 5);"#
2041 );
2042
2043 no_side_effects!(
2044 test_string_literal_split,
2045 r#"const result = "a,b,c".split(',');"#
2046 );
2047
2048 no_side_effects!(
2049 test_string_literal_trim,
2050 r#"const result = " hello ".trim();"#
2051 );
2052
2053 no_side_effects!(
2054 test_string_literal_replace,
2055 r#"const result = "hello".replace('h', 'H');"#
2056 );
2057
2058 no_side_effects!(
2059 test_string_literal_includes,
2060 r#"const result = "hello world".includes('world');"#
2061 );
2062
2063 no_side_effects!(
2065 test_array_literal_map,
2066 r#"const result = [1, 2, 3].map(x => x * 2);"#
2067 );
2068 side_effects!(
2069 test_array_literal_map_with_effectful_callback,
2070 r#"const result = [1, 2, 3].map(x => {globalThis.something.push(x)});"#
2071 );
2072
2073 no_side_effects!(
2075 test_number_literal_to_fixed,
2076 r#"const result = (3.14159).toFixed(2);"#
2077 );
2078
2079 no_side_effects!(
2080 test_number_literal_to_string,
2081 r#"const result = (42).toString();"#
2082 );
2083
2084 no_side_effects!(
2085 test_number_literal_to_exponential,
2086 r#"const result = (123.456).toExponential(2);"#
2087 );
2088
2089 no_side_effects!(
2091 test_boolean_literal_to_string,
2092 r#"const result = true.toString();"#
2093 );
2094
2095 no_side_effects!(
2096 test_boolean_literal_value_of,
2097 r#"const result = false.valueOf();"#
2098 );
2099
2100 no_side_effects!(
2102 test_regexp_literal_to_string,
2103 r#"const result = /[a-z]+/.toString();"#
2104 );
2105
2106 no_side_effects!(
2109 test_regexp_literal_test,
2110 r#"const result = /[a-z]+/g.test("hello");"#
2111 );
2112
2113 no_side_effects!(
2114 test_regexp_literal_exec,
2115 r#"const result = /(\d+)/g.exec("test123");"#
2116 );
2117
2118 side_effects!(
2121 test_array_literal_with_impure_elements,
2122 r#"const result = [foo(), 2, 3].map(x => x * 2);"#
2123 );
2124
2125 no_side_effects!(
2129 test_array_literal_map_with_callback,
2130 r#"const result = [1, 2, 3].map(x => x * 2);"#
2131 );
2132 }
2133
2134 mod class_expression_side_effects_tests {
2135 use super::*;
2136
2137 no_side_effects!(test_class_no_extends_no_static, "class Foo {}");
2139
2140 no_side_effects!(test_class_pure_extends, "class Foo extends Bar {}");
2142
2143 side_effects!(
2145 test_class_extends_with_call,
2146 "class Foo extends someMixinFunction() {}"
2147 );
2148
2149 side_effects!(
2151 test_class_extends_with_complex_expr,
2152 "class Foo extends (Bar || Baz()) {}"
2153 );
2154
2155 side_effects!(
2157 test_class_static_property_with_call,
2158 r#"
2159 class Foo {
2160 static foo = someFunction();
2161 }
2162 "#
2163 );
2164
2165 no_side_effects!(
2167 test_class_static_property_pure,
2168 r#"
2169 class Foo {
2170 static foo = 42;
2171 }
2172 "#
2173 );
2174
2175 no_side_effects!(
2177 test_class_static_property_array_literal,
2178 r#"
2179 class Foo {
2180 static foo = [1, 2, 3];
2181 }
2182 "#
2183 );
2184
2185 side_effects!(
2187 test_class_static_block,
2188 r#"
2189 class Foo {
2190 static {
2191 console.log("hello");
2192 }
2193 }
2194 "#
2195 );
2196
2197 no_side_effects!(
2198 test_class_static_block_empty,
2199 r#"
2200 class Foo {
2201 static {}
2202 }
2203 "#
2204 );
2205
2206 no_side_effects!(
2208 test_class_instance_property_with_call,
2209 r#"
2210 class Foo {
2211 foo = someFunction();
2212 }
2213 "#
2214 );
2215
2216 no_side_effects!(
2218 test_class_constructor_with_side_effects,
2219 r#"
2220 class Foo {
2221 constructor() {
2222 console.log("constructor");
2223 }
2224 }
2225 "#
2226 );
2227
2228 no_side_effects!(
2230 test_class_method,
2231 r#"
2232 class Foo {
2233 method() {
2234 console.log("method");
2235 }
2236 }
2237 "#
2238 );
2239
2240 side_effects!(
2242 test_class_expr_extends_with_call,
2243 "const Foo = class extends getMixin() {};"
2244 );
2245
2246 side_effects!(
2248 test_class_expr_static_with_call,
2249 r#"
2250 const Foo = class {
2251 static prop = initValue();
2252 };
2253 "#
2254 );
2255
2256 no_side_effects!(
2258 test_class_expr_static_pure,
2259 r#"
2260 const Foo = class {
2261 static prop = "hello";
2262 };
2263 "#
2264 );
2265
2266 side_effects!(
2268 test_export_class_with_side_effects,
2269 r#"
2270 export class Foo extends getMixin() {
2271 static prop = init();
2272 }
2273 "#
2274 );
2275
2276 side_effects!(
2278 test_export_default_class_with_side_effects,
2279 r#"
2280 export default class Foo {
2281 static { console.log("init"); }
2282 }
2283 "#
2284 );
2285
2286 no_side_effects!(
2288 test_export_class_no_side_effects,
2289 r#"
2290 export class Foo {
2291 method() {
2292 console.log("method");
2293 }
2294 }
2295 "#
2296 );
2297
2298 side_effects!(
2300 test_class_mixed_static_properties,
2301 r#"
2302 class Foo {
2303 static a = 1;
2304 static b = impureCall();
2305 static c = 3;
2306 }
2307 "#
2308 );
2309
2310 no_side_effects!(
2312 test_class_static_property_pure_builtin,
2313 r#"
2314 class Foo {
2315 static value = Math.abs(-5);
2316 }
2317 "#
2318 );
2319
2320 side_effects!(
2322 test_class_computed_property_with_call,
2323 r#"
2324 class Foo {
2325 [computeName()]() {
2326 return 42;
2327 }
2328 }
2329 "#
2330 );
2331
2332 no_side_effects!(
2334 test_class_computed_property_pure,
2335 r#"
2336 class Foo {
2337 ['method']() {
2338 return 42;
2339 }
2340 }
2341 "#
2342 );
2343 }
2344
2345 mod complex_variable_declarations_tests {
2346 use super::*;
2347
2348 no_side_effects!(test_destructure_simple, "const { foo } = obj;");
2350
2351 side_effects!(
2353 test_destructure_default_with_call,
2354 "const { foo = someFunction() } = obj;"
2355 );
2356
2357 no_side_effects!(test_destructure_default_pure, "const { foo = 42 } = obj;");
2359
2360 no_side_effects!(
2362 test_destructure_default_array_literal,
2363 "const { foo = ['hello'] } = obj;"
2364 );
2365
2366 no_side_effects!(
2368 test_destructure_default_object_literal,
2369 "const { foo = { bar: 'baz' } } = obj;"
2370 );
2371
2372 side_effects!(
2374 test_destructure_nested_with_call,
2375 "const { a: { b = sideEffect() } } = obj;"
2376 );
2377
2378 side_effects!(
2380 test_array_destructure_default_with_call,
2381 "const [a, b = getDefault()] = arr;"
2382 );
2383
2384 no_side_effects!(
2386 test_array_destructure_default_pure,
2387 "const [a, b = 10] = arr;"
2388 );
2389
2390 side_effects!(
2392 test_multiple_destructure_mixed,
2393 "const { foo = 1, bar = compute() } = obj;"
2394 );
2395
2396 no_side_effects!(test_destructure_rest_pure, "const { foo, ...rest } = obj;");
2398
2399 side_effects!(
2401 test_destructure_complex_with_side_effect,
2402 r#"
2403 const {
2404 a,
2405 b: { c = sideEffect() },
2406 d = [1, 2, 3]
2407 } = obj;
2408 "#
2409 );
2410
2411 no_side_effects!(
2413 test_destructure_complex_pure,
2414 r#"
2415 const {
2416 a,
2417 b: { c = 5 },
2418 d = [1, 2, 3]
2419 } = obj;
2420 "#
2421 );
2422
2423 side_effects!(
2425 test_export_destructure_with_side_effect,
2426 "export const { foo = init() } = obj;"
2427 );
2428
2429 no_side_effects!(
2431 test_export_destructure_pure,
2432 "export const { foo = 42 } = obj;"
2433 );
2434
2435 no_side_effects!(
2437 test_destructure_default_pure_builtin,
2438 "const { foo = Math.abs(-5) } = obj;"
2439 );
2440
2441 no_side_effects!(
2443 test_destructure_default_pure_annotation,
2444 "const { foo = /*#__PURE__*/ compute() } = obj;"
2445 );
2446 }
2447
2448 mod decorator_side_effects_tests {
2449 use super::*;
2450
2451 side_effects!(
2453 test_class_decorator,
2454 r#"
2455 @decorator
2456 class Foo {}
2457 "#
2458 );
2459
2460 side_effects!(
2462 test_method_decorator,
2463 r#"
2464 class Foo {
2465 @decorator
2466 method() {}
2467 }
2468 "#
2469 );
2470
2471 side_effects!(
2473 test_property_decorator,
2474 r#"
2475 class Foo {
2476 @decorator
2477 prop = 1;
2478 }
2479 "#
2480 );
2481
2482 side_effects!(
2484 test_multiple_decorators,
2485 r#"
2486 @decorator1
2487 @decorator2
2488 class Foo {
2489 @propDecorator
2490 prop = 1;
2491
2492 @methodDecorator
2493 method() {}
2494 }
2495 "#
2496 );
2497
2498 side_effects!(
2500 test_decorator_with_args,
2501 r#"
2502 @decorator(config())
2503 class Foo {}
2504 "#
2505 );
2506 }
2507
2508 mod additional_edge_cases_tests {
2509 use super::*;
2510
2511 no_side_effects!(
2513 test_super_property_pure,
2514 r#"
2515 class Foo extends Bar {
2516 method() {
2517 return super.parentMethod;
2518 }
2519 }
2520 "#
2521 );
2522
2523 no_side_effects!(
2525 test_super_call_in_method,
2526 r#"
2527 class Foo extends Bar {
2528 method() {
2529 return super.parentMethod();
2530 }
2531 }
2532 "#
2533 );
2534
2535 no_side_effects!(test_import_meta, "const url = import.meta.url;");
2537
2538 no_side_effects!(
2540 test_new_target,
2541 r#"
2542 function Foo() {
2543 console.log(new.target);
2544 }
2545 "#
2546 );
2547
2548 side_effects!(test_jsx_element, "const el = <div>Hello</div>;");
2550
2551 side_effects!(test_jsx_fragment, "const el = <>Hello</>;");
2553
2554 no_side_effects!(
2556 test_private_field_access,
2557 r#"
2558 class Foo {
2559 #privateField = 42;
2560 method() {
2561 return this.#privateField;
2562 }
2563 }
2564 "#
2565 );
2566
2567 no_side_effects!(
2569 test_super_computed_property_pure,
2570 r#"
2571 class Foo extends Bar {
2572 method() {
2573 return super['prop'];
2574 }
2575 }
2576 "#
2577 );
2578
2579 no_side_effects!(
2581 test_static_block_pure_content,
2582 r#"
2583 class Foo {
2584 static {
2585 const x = 1;
2586 const y = 2;
2587 }
2588 }
2589 "#
2590 );
2591
2592 side_effects!(
2594 test_static_block_with_side_effect_inside,
2595 r#"
2596 class Foo {
2597 static {
2598 sideEffect();
2599 }
2600 }
2601 "#
2602 );
2603
2604 no_side_effects!(
2606 test_this_expression,
2607 r#"
2608 class Foo {
2609 method() {
2610 return this;
2611 }
2612 }
2613 "#
2614 );
2615
2616 no_side_effects!(
2618 test_spread_pure_in_call,
2619 "const result = Math.max(...[1, 2, 3]);"
2620 );
2621
2622 side_effects!(
2624 test_spread_with_side_effect,
2625 "const result = Math.max(...getArray());"
2626 );
2627
2628 no_side_effects!(
2630 test_super_complex_access,
2631 r#"
2632 class Foo extends Bar {
2633 static method() {
2634 return super.parentMethod;
2635 }
2636 }
2637 "#
2638 );
2639
2640 no_side_effects!(
2642 test_getter_definition,
2643 r#"
2644 const obj = {
2645 get foo() {
2646 return this._foo;
2647 }
2648 };
2649 "#
2650 );
2651
2652 no_side_effects!(
2654 test_async_function_declaration,
2655 r#"
2656 async function foo() {
2657 return await something;
2658 }
2659 "#
2660 );
2661
2662 no_side_effects!(
2664 test_generator_declaration,
2665 r#"
2666 function* foo() {
2667 yield 1;
2668 yield 2;
2669 }
2670 "#
2671 );
2672
2673 no_side_effects!(
2675 test_async_generator,
2676 r#"
2677 async function* foo() {
2678 yield await something;
2679 }
2680 "#
2681 );
2682
2683 side_effects!(
2688 test_nullish_coalescing_with_side_effect,
2689 "const x = a ?? sideEffect();"
2690 );
2691
2692 side_effects!(
2694 test_logical_or_with_side_effect,
2695 "const x = a || sideEffect();"
2696 );
2697
2698 side_effects!(
2700 test_logical_and_with_side_effect,
2701 "const x = a && sideEffect();"
2702 );
2703 }
2704
2705 mod common_js_modules_tests {
2706 use super::*;
2707
2708 no_side_effects!(test_common_js_exports, "exports.foo = 'a'");
2711 no_side_effects!(test_common_js_exports_module, "module.exports.foo = 'a'");
2712 no_side_effects!(test_common_js_exports_assignment, "module.exports = {}");
2713 no_side_effects!(
2714 test_common_js_function_exports,
2715 "exports.foo = function () { return 1; }; exports.bar = 2;"
2716 );
2717
2718 module_evaluation_is_side_effect_free!(
2719 test_common_js_reexport,
2720 "module.exports = require('./other');"
2721 );
2722 module_evaluation_is_side_effect_free!(
2723 test_common_js_named_reexports,
2724 "exports.a = require('./a'); exports.b = require('./b');"
2725 );
2726
2727 side_effects!(
2729 test_common_js_export_impure_value,
2730 "exports.foo = sideEffect();"
2731 );
2732 side_effects!(
2734 test_common_js_export_computed_side_effect,
2735 "exports[sideEffect()] = 'a';"
2736 );
2737 side_effects!(test_module_non_export_assignment, "module.foo = 'a';");
2739 side_effects!(
2741 test_shadowed_exports_assignment,
2742 "let exports = {}; exports.foo = 'a';"
2743 );
2744
2745 side_effects!(
2749 test_cjs_export_setter_invoked,
2750 "module.exports = { set foo(v) { sideEffect() } }; module.exports.foo = 1;"
2751 );
2752
2753 no_side_effects!(
2756 test_cjs_export_fresh_then_write,
2757 "module.exports = {}; module.exports.foo = 1;"
2758 );
2759 side_effects!(
2763 test_cjs_export_reexport_then_write,
2764 "module.exports = require('./other'); module.exports.extra = 1;"
2765 );
2766 side_effects!(
2767 test_cjs_export_alias_then_write,
2768 "module.exports = other; module.exports.foo = 1;"
2769 );
2770 side_effects!(
2773 test_cjs_export_literal_capturing_global_then_write,
2774 "module.exports = { g: globalThis }; module.exports.g.x = 1;"
2775 );
2776 side_effects!(
2779 test_cjs_export_reexport_then_write_sequence,
2780 "module.exports = require('./other'), module.exports.extra = 1;"
2781 );
2782 side_effects!(
2786 test_cjs_export_reexport_in_logical_then_write,
2787 "x && (module.exports = require('./other')); module.exports.extra = 1;"
2788 );
2789 side_effects!(
2790 test_cjs_export_setter_attached_in_conditional,
2791 "x ? (module.exports = { set foo(v) { sideEffect() } }) : 0;"
2792 );
2793 no_side_effects!(
2796 test_cjs_export_reassign_in_function_body_is_pure,
2797 "function f() { module.exports = require('./other'); } module.exports.foo = 1;"
2798 );
2799
2800 side_effects!(
2805 test_cjs_export_setter_in_static_block,
2806 "class C { static { module.exports = { set foo(v) { sideEffect() } }; \
2807 module.exports.foo = 1; } }"
2808 );
2809 no_side_effects!(
2815 test_cjs_export_setter_in_constructor_is_pure,
2816 "class C { constructor() { module.exports = { set foo(v) { sideEffect() } }; } } \
2817 module.exports.foo = 1;"
2818 );
2819
2820 side_effects!(
2824 test_cjs_export_setter_in_static_property,
2825 "class C { static x = (module.exports = { set foo(v) { sideEffect() } }); } \
2826 module.exports.foo = 1;"
2827 );
2828
2829 no_side_effects!(
2830 test_cjs_export_define_property,
2831 "Object.defineProperty(exports, '__esModule', { value: true }); exports.foo = 1;"
2832 );
2833 no_side_effects!(
2834 test_cjs_export_define_property_module_exports,
2835 "Object.defineProperty(module.exports, 'foo', { value: 1 });"
2836 );
2837 no_side_effects!(
2838 test_cjs_export_define_property_enumerable,
2839 "Object.defineProperty(exports, 'foo', { value: 1, enumerable: true });"
2840 );
2841 no_side_effects!(
2842 test_cjs_export_define_property_getter,
2843 "Object.defineProperty(exports, 'foo', { get() { return 1; } });"
2844 );
2845 no_side_effects!(
2846 test_cjs_export_define_property_getter_function,
2847 "Object.defineProperty(exports, 'foo', { get: function() { return 1; } });"
2848 );
2849 module_evaluation_is_side_effect_free!(
2851 test_cjs_export_define_property_reexport_barrel,
2852 "Object.defineProperty(exports, '__esModule', { value: true }); var _a = \
2853 require('./a'); Object.defineProperty(exports, 'a', { enumerable: true, get: \
2854 function() { return _a.a; } });"
2855 );
2856 side_effects!(
2857 test_cjs_export_define_property_setter_then_write,
2858 "Object.defineProperty(exports, 'foo', { set(v) { sideEffect() } }); exports.foo = 1;"
2859 );
2860 side_effects!(
2861 test_cjs_export_define_property_getter_then_write,
2862 "Object.defineProperty(exports, 'foo', { get() { return 1; } }); exports.bar = 1;"
2863 );
2864 side_effects!(
2865 test_cjs_export_define_property_spread_descriptor_then_write,
2866 "Object.defineProperty(exports, 'foo', { ...descriptor }); exports.bar = 1;"
2867 );
2868 side_effects!(
2869 test_cjs_export_define_property_descriptor_getter,
2870 "Object.defineProperty(exports, 'foo', { get value() { return sideEffect(); } });"
2871 );
2872 side_effects!(
2873 test_cjs_export_define_property_computed_descriptor_key_then_write,
2874 "Object.defineProperty(exports, 'foo', { [key]: fn }); exports.bar = 1;"
2875 );
2876 side_effects!(
2877 test_cjs_export_define_property_nested_setter_then_write,
2878 "Object.defineProperty(exports, 'foo', { value: { set a(v) { sideEffect() } } }); \
2879 exports.foo.a = 1;"
2880 );
2881 side_effects!(
2882 test_cjs_export_define_property_computed_key,
2883 "Object.defineProperty(exports, someVar, { value: 1 });"
2884 );
2885 side_effects!(
2886 test_define_property_foreign_target,
2887 "Object.defineProperty(globalThis, 'foo', { value: 1 });"
2888 );
2889 side_effects!(
2890 test_cjs_export_define_property_impure_value,
2891 "Object.defineProperty(exports, 'foo', { value: sideEffect() });"
2892 );
2893 side_effects!(
2894 test_cjs_export_define_property_after_reexport,
2895 "module.exports = require('./other'); Object.defineProperty(module.exports, 'foo', { \
2896 value: 1 });"
2897 );
2898 }
2899
2900 mod local_variable_mutation_tests {
2901 use super::*;
2902
2903 no_side_effects!(
2907 test_const_object_build,
2908 "const config = {}; config['a'] = 'a'; config['b'] = 'b'; export default config;"
2909 );
2910 no_side_effects!(test_const_member_assignment, "const o = {}; o.a = 1;");
2911 no_side_effects!(test_const_array_index, "const a = []; a[0] = 1;");
2912 no_side_effects!(test_const_nested_member, "const o = { a: {} }; o.a.b = 1;");
2913
2914 side_effects!(
2917 test_aliased_import_mutation,
2918 "import config from './config'; const c = config; c.enabled = true;"
2919 );
2920 side_effects!(
2922 test_aliased_global_mutation,
2923 "const g = globalThis; g.shared = 1;"
2924 );
2925 side_effects!(
2927 test_imported_binding_mutation,
2928 "import obj from 'x'; obj.foo = 1;"
2929 );
2930 side_effects!(
2932 test_non_safe_assignment_constant_init,
2933 "const o = makeObj(); o.a = 1;"
2934 );
2935 side_effects!(test_let_object_mutation, "let o = {}; o.a = 1;");
2937 side_effects!(test_global_assignment, "globalThis.shared = 1;");
2939 side_effects!(test_undeclared_assignment, "leaked = 1;");
2940 side_effects!(
2942 test_safe_assignment_constant_impure_value,
2943 "const o = {}; o.a = sideEffect();"
2944 );
2945 side_effects!(
2947 test_safe_assignment_constant_computed_key_side_effect,
2948 "const o = {}; o[sideEffect()] = 1;"
2949 );
2950 side_effects!(
2953 test_local_setter_invoked,
2954 "const o = { set x(v) { sideEffect() } }; o.x = 1;"
2955 );
2956 side_effects!(
2957 test_local_nested_setter_invoked,
2958 "const o = {}; o.a = { set y(v) { sideEffect() } }; o.a.y = 1;"
2959 );
2960 side_effects!(
2963 test_local_setter_attached_after_init,
2964 "const o = {}; o.x = { set y(v) { sideEffect() } };"
2965 );
2966 side_effects!(
2969 test_local_setter_via_define_property,
2970 "const o = {}; Object.defineProperty(o, 'b', { set(x) { this.a = x / 2 } }); o.b = 4;"
2971 );
2972 side_effects!(
2976 test_local_setter_attached_in_conditional,
2977 "const o = {}; x && (o.a = { set y(v) { sideEffect() } }); o.a.y = 1;"
2978 );
2979
2980 side_effects!(
2981 test_literal_capturing_global_nested_mutation,
2982 "const box = { g: globalThis }; box.g.x = 1;"
2983 );
2984 side_effects!(
2985 test_literal_capturing_global_direct_mutation,
2986 "const box = { g: globalThis }; box.g = 1;"
2987 );
2988 side_effects!(
2989 test_literal_capturing_global_deeply_nested_mutation,
2990 "const box = { a: { b: globalThis } }; box.a.b.x = 1;"
2991 );
2992 side_effects!(
2993 test_array_literal_capturing_global_nested_mutation,
2994 "const box = [globalThis]; box[0].x = 1;"
2995 );
2996 side_effects!(
2997 test_literal_capturing_window_mutation,
2998 "const box = { w: window }; box.w.x = 1;"
2999 );
3000 side_effects!(
3001 test_literal_capturing_import_mutation,
3002 "import obj from './obj'; const box = { obj }; box.obj.foo = 1;"
3003 );
3004 side_effects!(
3005 test_literal_capturing_require_result_mutation,
3006 "const box = { dep: require('./dep') }; box.dep.x = 1;"
3007 );
3008 side_effects!(
3009 test_literal_spreading_import_mutation,
3010 "import obj from './obj'; const box = { ...obj }; box.foo.bar = 1;"
3011 );
3012 side_effects!(
3013 test_foreign_value_assigned_then_nested_mutation,
3014 "const box = {}; box.g = globalThis; box.g.x = 1;"
3015 );
3016 side_effects!(
3017 test_foreign_value_assigned_then_direct_mutation,
3018 "const box = {}; box.g = globalThis; box.a = 1;"
3019 );
3020 side_effects!(
3021 test_require_result_assigned_then_direct_mutation,
3022 "const box = {}; box.dep = require('./dep'); box.a = 1;"
3023 );
3024 no_side_effects!(
3025 test_deeply_nested_literal_mutation,
3026 "const box = { a: { b: {} } }; box.a.b.c = 1;"
3027 );
3028 no_side_effects!(
3029 test_nested_literal_assigned_then_mutated,
3030 "const box = {}; box.a = { b: {} }; box.a.b.c = 1;"
3031 );
3032 }
3033}