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 let is_pure_annotated = self.is_pure_annotated(call.span);
1022 if is_pure_annotated || self.is_known_pure_builtin(&call.callee) {
1023 call.callee.visit_with(self);
1029
1030 self.with_will_invoke_fn_exprs(!is_pure_annotated, |this| {
1034 call.args.visit_children_with(this);
1035 });
1036 } else if self.is_require_or_import(&call.callee) {
1037 self.has_imports = true;
1038 call.args.visit_children_with(self);
1041 } else if self.define_property_target_is_pure(call) {
1042 call.args.visit_children_with(self);
1043 } else {
1044 self.mark_side_effect();
1046 }
1047 }
1048 Expr::New(new) => {
1049 let is_pure_annotated = self.is_pure_annotated(new.span);
1051 if is_pure_annotated || self.is_known_pure_constructor(&new.callee) {
1052 new.callee.visit_with(self);
1054 self.with_will_invoke_fn_exprs(!is_pure_annotated, |this| {
1055 new.args.visit_children_with(this);
1056 });
1057 } else {
1058 self.mark_side_effect();
1060 }
1061 }
1062 Expr::Assign(assign) => {
1063 if assign.op == AssignOp::Assign && self.assign_target_is_pure(&assign.left) {
1072 assign.left.visit_with(self);
1075 assign.right.visit_with(self);
1076 } else {
1077 self.mark_side_effect();
1078 }
1079 }
1080 Expr::Update(_) => {
1081 self.mark_side_effect();
1084 }
1085 Expr::Await(e) => {
1086 e.arg.visit_with(self);
1087 }
1088 Expr::Yield(e) => {
1089 e.arg.visit_with(self);
1090 }
1091 Expr::TaggedTpl(tagged_tpl)
1092 if self.is_known_pure_builtin_function(&tagged_tpl.tag) => {
1095 for arg in &tagged_tpl.tpl.exprs {
1096 arg.visit_with(self);
1097 }
1098 }
1099 Expr::OptChain(opt_chain) => {
1100 opt_chain.base.visit_with(self);
1103 }
1104 Expr::Seq(seq) => {
1105 seq.exprs.visit_children_with(self);
1107 }
1108 Expr::SuperProp(super_prop) => {
1109 super_prop.prop.visit_with(self);
1112 }
1113 Expr::MetaProp(_) => {
1114 }
1117 Expr::JSXMember(_) | Expr::JSXNamespacedName(_) | Expr::JSXEmpty(_) => {
1118 }
1120 Expr::JSXElement(_) | Expr::JSXFragment(_) => {
1121 self.mark_side_effect();
1126 }
1127 Expr::PrivateName(_) => {
1128 }
1130
1131 _ => {
1133 self.mark_side_effect();
1134 }
1135 }
1136 }
1137
1138 fn visit_opt_chain_base(&mut self, base: &OptChainBase) {
1139 check_side_effects!(self);
1140
1141 match base {
1142 OptChainBase::Member(member) => {
1143 member.visit_with(self);
1144 }
1145 OptChainBase::Call(_opt_call) => {
1146 self.mark_side_effect();
1150 }
1151 }
1152 }
1153
1154 fn visit_prop_or_spread(&mut self, prop: &PropOrSpread) {
1155 check_side_effects!(self);
1156
1157 match prop {
1158 PropOrSpread::Spread(spread) => {
1159 spread.expr.visit_with(self);
1160 }
1161 PropOrSpread::Prop(prop) => {
1162 prop.visit_with(self);
1163 }
1164 }
1165 }
1166
1167 fn visit_prop(&mut self, prop: &Prop) {
1168 check_side_effects!(self);
1169
1170 match prop {
1171 Prop::KeyValue(kv) => {
1172 kv.key.visit_with(self);
1173 kv.value.visit_with(self);
1174 }
1175 Prop::Getter(getter) => {
1176 getter.key.visit_with(self);
1177 }
1179 Prop::Setter(setter) => {
1180 setter.key.visit_with(self);
1181 }
1183 Prop::Method(method) => {
1184 method.key.visit_with(self);
1185 }
1187 Prop::Shorthand(_) => {
1188 }
1190 Prop::Assign(_) => {
1191 }
1194 }
1195 }
1196
1197 fn visit_prop_name(&mut self, prop_name: &PropName) {
1198 check_side_effects!(self);
1199
1200 match prop_name {
1201 PropName::Computed(computed) => {
1202 computed.expr.visit_with(self);
1204 }
1205 _ => {
1206 }
1208 }
1209 }
1210
1211 fn visit_class(&mut self, class: &Class) {
1212 check_side_effects!(self);
1213
1214 for decorator in &class.decorators {
1216 decorator.visit_with(self);
1217 }
1218
1219 if let Some(super_class) = &class.super_class {
1221 super_class.visit_with(self);
1222 }
1223
1224 for member in &class.body {
1226 member.visit_with(self);
1227 }
1228 }
1229
1230 fn visit_class_member(&mut self, member: &ClassMember) {
1231 check_side_effects!(self);
1232
1233 match member {
1234 ClassMember::StaticBlock(block) => {
1236 for stmt in &block.body.stmts {
1239 stmt.visit_with(self);
1240 }
1241 }
1242 ClassMember::ClassProp(class_prop) if class_prop.is_static => {
1244 for decorator in &class_prop.decorators {
1246 decorator.visit_with(self);
1247 }
1248 class_prop.key.visit_with(self);
1250 if let Some(value) = &class_prop.value {
1252 value.visit_with(self);
1253 }
1254 }
1255 ClassMember::Method(method) => {
1257 for decorator in &method.function.decorators {
1259 decorator.visit_with(self);
1260 }
1261 method.key.visit_with(self);
1262 }
1264 ClassMember::Constructor(constructor) => {
1265 constructor.key.visit_with(self);
1266 }
1268 ClassMember::PrivateMethod(private_method) => {
1269 for decorator in &private_method.function.decorators {
1271 decorator.visit_with(self);
1272 }
1273 private_method.key.visit_with(self);
1274 }
1276 ClassMember::ClassProp(class_prop) => {
1277 for decorator in &class_prop.decorators {
1279 decorator.visit_with(self);
1280 }
1281 class_prop.key.visit_with(self);
1283 }
1285 ClassMember::PrivateProp(private_prop) => {
1286 for decorator in &private_prop.decorators {
1288 decorator.visit_with(self);
1289 }
1290 private_prop.key.visit_with(self);
1291 }
1293 ClassMember::AutoAccessor(auto_accessor) if auto_accessor.is_static => {
1294 for decorator in &auto_accessor.decorators {
1296 decorator.visit_with(self);
1297 }
1298 auto_accessor.key.visit_with(self);
1300 if let Some(value) = &auto_accessor.value {
1301 value.visit_with(self);
1302 }
1303 }
1304 ClassMember::AutoAccessor(auto_accessor) => {
1305 for decorator in &auto_accessor.decorators {
1307 decorator.visit_with(self);
1308 }
1309 auto_accessor.key.visit_with(self);
1311 }
1312 ClassMember::Empty(_) => {
1313 }
1315 ClassMember::TsIndexSignature(_) => {
1316 }
1318 }
1319 }
1320
1321 fn visit_decorator(&mut self, _decorator: &Decorator) {
1322 if self.has_side_effects {
1323 return;
1324 }
1325
1326 self.mark_side_effect();
1330 }
1331
1332 fn visit_pat(&mut self, pat: &Pat) {
1333 check_side_effects!(self);
1334
1335 match pat {
1336 Pat::Object(object_pat) => {
1338 for prop in &object_pat.props {
1339 match prop {
1340 ObjectPatProp::KeyValue(kv) => {
1341 kv.key.visit_with(self);
1343 kv.value.visit_with(self);
1345 }
1346 ObjectPatProp::Assign(assign) => {
1347 if let Some(value) = &assign.value {
1349 value.visit_with(self);
1350 }
1351 }
1352 ObjectPatProp::Rest(rest) => {
1353 rest.arg.visit_with(self);
1355 }
1356 }
1357 }
1358 }
1359 Pat::Array(array_pat) => {
1361 for elem in array_pat.elems.iter().flatten() {
1362 elem.visit_with(self);
1363 }
1364 }
1365 Pat::Assign(assign_pat) => {
1367 assign_pat.right.visit_with(self);
1369 assign_pat.left.visit_with(self);
1371 }
1372 _ => {}
1374 }
1375 }
1376}
1377
1378#[cfg(test)]
1379mod tests {
1380 use swc_core::{
1381 common::{FileName, GLOBALS, Mark, SourceMap, comments::SingleThreadedComments, sync::Lrc},
1382 ecma::{
1383 ast::EsVersion,
1384 parser::{EsSyntax, Syntax, parse_file_as_program},
1385 transforms::base::resolver,
1386 visit::VisitMutWith,
1387 },
1388 };
1389
1390 use super::*;
1391
1392 fn parse_and_check_for_side_effects(code: &str, expected: ModuleSideEffects) {
1394 GLOBALS.set(&Default::default(), || {
1395 let cm = Lrc::new(SourceMap::default());
1396 let fm = cm.new_source_file(Lrc::new(FileName::Anon), code.to_string());
1397
1398 let comments = SingleThreadedComments::default();
1399 let mut errors = vec![];
1400
1401 let mut program = parse_file_as_program(
1402 &fm,
1403 Syntax::Es(EsSyntax {
1404 jsx: true,
1405 decorators: true,
1406 ..Default::default()
1407 }),
1408 EsVersion::latest(),
1409 Some(&comments),
1410 &mut errors,
1411 )
1412 .expect("Failed to parse");
1413
1414 let unresolved_mark = Mark::new();
1416 let top_level_mark = Mark::new();
1417 program.visit_mut_with(&mut resolver(unresolved_mark, top_level_mark, false));
1418
1419 let actual =
1420 compute_module_evaluation_side_effects(&program, &comments, unresolved_mark);
1421
1422 let msg = match expected {
1423 ModuleSideEffects::ModuleEvaluationIsSideEffectFree => {
1424 "Expected code to have no local side effects"
1425 }
1426 ModuleSideEffects::SideEffectFree => "Expected code to be side effect free",
1427 ModuleSideEffects::SideEffectful => "Expected code to have side effects",
1428 };
1429 assert_eq!(actual, expected, "{}:\n{}", msg, code);
1430 })
1431 }
1432
1433 macro_rules! assert_side_effects {
1435 ($name:ident, $code:expr, $expected:expr) => {
1436 #[test]
1437 fn $name() {
1438 parse_and_check_for_side_effects($code, $expected);
1439 }
1440 };
1441 }
1442
1443 macro_rules! side_effects {
1444 ($name:ident, $code:expr) => {
1445 assert_side_effects!($name, $code, ModuleSideEffects::SideEffectful);
1446 };
1447 }
1448
1449 macro_rules! no_side_effects {
1450 ($name:ident, $code:expr) => {
1451 assert_side_effects!($name, $code, ModuleSideEffects::SideEffectFree);
1452 };
1453 }
1454 macro_rules! module_evaluation_is_side_effect_free {
1455 ($name:ident, $code:expr) => {
1456 assert_side_effects!(
1457 $name,
1458 $code,
1459 ModuleSideEffects::ModuleEvaluationIsSideEffectFree
1460 );
1461 };
1462 }
1463
1464 mod constructor_callee_tests {
1465 use super::*;
1466
1467 side_effects!(test_call_callee, "/*#__PURE__*/ new (getConstructor())();");
1468 side_effects!(
1469 test_nested_call_callee,
1470 "/*#__PURE__*/ new (getContainer().Constructor)();"
1471 );
1472 side_effects!(
1473 test_computed_callee,
1474 "/*#__PURE__*/ new (constructors[getKey()])();"
1475 );
1476 side_effects!(
1477 test_conditional_callee,
1478 "/*#__PURE__*/ new (flag ? getConstructor() : Constructor)();"
1479 );
1480 side_effects!(
1481 test_callee_and_arguments,
1482 "/*#__PURE__*/ new (getConstructor())(effect());"
1483 );
1484 side_effects!(test_arguments, "/*#__PURE__*/ new Constructor(effect());");
1485 no_side_effects!(test_identifier_callee, "/*#__PURE__*/ new Constructor();");
1486 no_side_effects!(
1487 test_member_callee,
1488 "/*#__PURE__*/ new constructors.Constructor();"
1489 );
1490 no_side_effects!(
1491 test_computed_pure_callee,
1492 "/*#__PURE__*/ new constructors[key]();"
1493 );
1494 no_side_effects!(
1495 test_uninvoked_constructor_body,
1496 "/*#__PURE__*/ new (function () { effect(); })();"
1497 );
1498 no_side_effects!(test_known_constructor, "new Map();");
1499 no_side_effects!(
1500 test_pure_callee_call,
1501 "/*#__PURE__*/ new (/*#__PURE__*/ getConstructor())();"
1502 );
1503 }
1504
1505 mod basic_tests {
1506 use super::*;
1507
1508 no_side_effects!(test_empty_program, "");
1509
1510 no_side_effects!(test_simple_const_declaration, "const x = 5;");
1511
1512 no_side_effects!(test_simple_let_declaration, "let y = 'string';");
1513
1514 no_side_effects!(test_array_literal, "const arr = [1, 2, 3];");
1515
1516 no_side_effects!(test_object_literal, "const obj = { a: 1, b: 2 };");
1517
1518 no_side_effects!(test_function_declaration, "function foo() { return 1; }");
1519
1520 no_side_effects!(
1521 test_function_expression,
1522 "const foo = function() { return 1; };"
1523 );
1524
1525 no_side_effects!(test_arrow_function, "const foo = () => 1;");
1526 }
1527
1528 mod side_effects_tests {
1529 use super::*;
1530
1531 side_effects!(test_console_log, "console.log('hello');");
1532
1533 side_effects!(test_function_call, "foo();");
1534
1535 side_effects!(test_method_call, "obj.method();");
1536
1537 side_effects!(test_assignment, "x = 5;");
1538
1539 side_effects!(test_member_assignment, "obj.prop = 5;");
1540
1541 side_effects!(test_constructor_call, "new SideEffect();");
1542
1543 side_effects!(test_update_expression, "x++;");
1544 }
1545
1546 mod pure_expressions_tests {
1547 use super::*;
1548
1549 no_side_effects!(test_binary_expression, "const x = 1 + 2;");
1550
1551 no_side_effects!(test_unary_expression, "const x = -5;");
1552
1553 no_side_effects!(test_conditional_expression, "const x = true ? 1 : 2;");
1554
1555 no_side_effects!(test_template_literal, "const x = `hello ${world}`;");
1556
1557 no_side_effects!(test_nested_object, "const obj = { a: { b: { c: 1 } } };");
1558
1559 no_side_effects!(test_nested_array, "const arr = [[1, 2], [3, 4]];");
1560 }
1561
1562 mod import_export_tests {
1563 use super::*;
1564
1565 module_evaluation_is_side_effect_free!(test_import_statement, "import x from 'y';");
1566 module_evaluation_is_side_effect_free!(test_require_statement, "const x = require('y');");
1567
1568 no_side_effects!(test_export_statement, "export default 5;");
1569
1570 no_side_effects!(test_export_const, "export const x = 5;");
1571
1572 side_effects!(
1573 test_export_const_with_side_effect,
1574 "export const x = foo();"
1575 );
1576 }
1577
1578 mod mixed_cases_tests {
1579 use super::*;
1580
1581 side_effects!(test_call_in_initializer, "const x = foo();");
1582
1583 side_effects!(test_call_in_array, "const arr = [1, foo(), 3];");
1584
1585 side_effects!(test_call_in_object, "const obj = { a: foo() };");
1586
1587 no_side_effects!(
1588 test_multiple_declarations_pure,
1589 "const x = 1;\nconst y = 2;\nconst z = 3;"
1590 );
1591
1592 side_effects!(
1593 test_multiple_declarations_with_side_effect,
1594 "const x = 1;\nfoo();\nconst z = 3;"
1595 );
1596
1597 no_side_effects!(test_class_declaration, "class Foo {}");
1598
1599 no_side_effects!(
1600 test_class_with_methods,
1601 "class Foo { method() { return 1; } }"
1602 );
1603 }
1604
1605 mod pure_annotations_tests {
1606 use super::*;
1607
1608 no_side_effects!(test_pure_annotation_function_call, "/*#__PURE__*/ foo();");
1609
1610 no_side_effects!(test_pure_annotation_with_at, "/*@__PURE__*/ foo();");
1611
1612 no_side_effects!(test_pure_annotation_constructor, "/*#__PURE__*/ new Foo();");
1613
1614 no_side_effects!(
1615 test_pure_annotation_in_variable,
1616 "const x = /*#__PURE__*/ foo();"
1617 );
1618
1619 no_side_effects!(
1620 test_pure_annotation_with_pure_args,
1621 "/*#__PURE__*/ foo(1, 2, 3, function(){ console.log(1); });"
1622 );
1623
1624 no_side_effects!(
1625 test_pure_annotation_with_arrow_callback,
1626 "/*#__PURE__*/ foo(() => console.log(1));"
1627 );
1628
1629 no_side_effects!(
1630 test_pure_annotation_builtin_with_callback,
1631 "/*#__PURE__*/ [1].map(() => console.log(1));"
1632 );
1633
1634 no_side_effects!(
1635 test_pure_annotation_constructor_with_callback,
1636 "/*#__PURE__*/ new Promise(() => console.log(1));"
1637 );
1638
1639 side_effects!(
1641 test_pure_annotation_with_impure_args,
1642 "/*#__PURE__*/ foo(bar());"
1643 );
1644
1645 side_effects!(test_without_pure_annotation, "foo();");
1647
1648 no_side_effects!(
1649 test_pure_nested_in_object,
1650 "const obj = { x: /*#__PURE__*/ foo() };"
1651 );
1652
1653 no_side_effects!(test_pure_in_array, "const arr = [/*#__PURE__*/ foo()];");
1654
1655 no_side_effects!(
1656 test_multiple_pure_calls,
1657 "const x = /*#__PURE__*/ foo();\nconst y = /*#__PURE__*/ bar();"
1658 );
1659
1660 side_effects!(
1661 test_mixed_pure_and_impure,
1662 "const x = /*#__PURE__*/ foo();\nbar();\nconst z = /*#__PURE__*/ baz();"
1663 );
1664 }
1665
1666 mod known_pure_builtins_tests {
1667 use super::*;
1668
1669 no_side_effects!(test_math_abs, "const x = Math.abs(-5);");
1670
1671 no_side_effects!(test_math_floor, "const x = Math.floor(3.14);");
1672
1673 no_side_effects!(test_math_max, "const x = Math.max(1, 2, 3);");
1674
1675 no_side_effects!(test_object_keys, "const keys = Object.keys(obj);");
1676
1677 no_side_effects!(test_object_values, "const values = Object.values(obj);");
1678
1679 no_side_effects!(test_object_entries, "const entries = Object.entries(obj);");
1680
1681 no_side_effects!(test_array_is_array, "const result = Array.isArray([]);");
1682
1683 no_side_effects!(
1684 test_string_from_char_code,
1685 "const char = String.fromCharCode(65);"
1686 );
1687
1688 no_side_effects!(test_number_is_nan, "const result = Number.isNaN(x);");
1689
1690 no_side_effects!(
1691 test_multiple_math_calls,
1692 "const x = Math.abs(-5);\nconst y = Math.floor(3.14);\nconst z = Math.max(x, y);"
1693 );
1694
1695 side_effects!(
1697 test_pure_builtin_with_impure_arg,
1698 "const x = Math.abs(foo());"
1699 );
1700
1701 no_side_effects!(
1702 test_pure_builtin_in_expression,
1703 "const x = Math.abs(-5) + Math.floor(3.14);"
1704 );
1705
1706 side_effects!(
1707 test_mixed_builtin_and_impure,
1708 "const x = Math.abs(-5);\nfoo();\nconst z = Object.keys({});"
1709 );
1710
1711 side_effects!(test_unknown_math_property, "const x = Math.random();");
1713
1714 side_effects!(test_object_assign, "Object.assign(target, source);");
1716
1717 no_side_effects!(test_array_from, "const arr = Array.from(iterable);");
1718
1719 no_side_effects!(test_global_is_nan, "const result = isNaN(value);");
1720
1721 no_side_effects!(test_global_is_finite, "const result = isFinite(value);");
1722
1723 no_side_effects!(test_global_parse_int, "const num = parseInt('42', 10);");
1724
1725 no_side_effects!(test_global_parse_float, "const num = parseFloat('3.14');");
1726
1727 no_side_effects!(
1728 test_global_decode_uri,
1729 "const decoded = decodeURI(encoded);"
1730 );
1731
1732 no_side_effects!(
1733 test_global_decode_uri_component,
1734 "const decoded = decodeURIComponent(encoded);"
1735 );
1736
1737 no_side_effects!(
1739 test_global_string_constructor_as_function,
1740 "const str = String(123);"
1741 );
1742
1743 no_side_effects!(
1745 test_global_number_constructor_as_function,
1746 "const num = Number('123');"
1747 );
1748
1749 no_side_effects!(
1751 test_global_boolean_constructor_as_function,
1752 "const bool = Boolean(value);"
1753 );
1754
1755 no_side_effects!(
1757 test_global_symbol_constructor_as_function,
1758 "const sym = Symbol('description');"
1759 );
1760
1761 no_side_effects!(test_symbol_for, "const sym = Symbol.for('description');");
1763
1764 no_side_effects!(
1766 test_symbol_key_for,
1767 "const description = Symbol.keyFor(sym);"
1768 );
1769
1770 side_effects!(
1772 test_global_pure_with_impure_arg,
1773 "const result = isNaN(foo());"
1774 );
1775
1776 side_effects!(
1778 test_shadowed_global_is_nan,
1779 r#"
1780 const isNaN = () => sideEffect();
1781 const result = isNaN(value);
1782 "#
1783 );
1784 }
1785
1786 mod edge_cases_tests {
1787 use super::*;
1788
1789 no_side_effects!(test_computed_property, "const obj = { [key]: value };");
1790
1791 side_effects!(
1792 test_computed_property_with_call,
1793 "const obj = { [foo()]: value };"
1794 );
1795
1796 no_side_effects!(test_spread_in_array, "const arr = [...other];");
1797
1798 no_side_effects!(test_spread_in_object, "const obj = { ...other };");
1799
1800 no_side_effects!(test_destructuring_assignment, "const { a, b } = obj;");
1801
1802 no_side_effects!(test_array_destructuring, "const [a, b] = arr;");
1803
1804 no_side_effects!(test_nested_ternary, "const x = a ? (b ? 1 : 2) : 3;");
1805
1806 no_side_effects!(test_logical_and, "const x = a && b;");
1807
1808 no_side_effects!(test_logical_or, "const x = a || b;");
1809
1810 no_side_effects!(test_nullish_coalescing, "const x = a ?? b;");
1811
1812 no_side_effects!(test_typeof_operator, "const x = typeof y;");
1813
1814 no_side_effects!(test_void_operator, "const x = void 0;");
1815
1816 side_effects!(test_delete_expression, "delete obj.prop;");
1818
1819 no_side_effects!(test_sequence_expression_pure, "const x = (1, 2, 3);");
1820
1821 side_effects!(test_sequence_expression_impure, "const x = (foo(), 2, 3);");
1822
1823 no_side_effects!(test_arrow_with_block, "const foo = () => { return 1; };");
1824
1825 no_side_effects!(
1826 test_class_with_constructor,
1827 "class Foo { constructor() { this.x = 1; } }"
1828 );
1829
1830 no_side_effects!(test_class_extends, "class Foo extends Bar {}");
1831
1832 no_side_effects!(test_async_function, "async function foo() { return 1; }");
1833
1834 no_side_effects!(test_generator_function, "function* foo() { yield 1; }");
1835
1836 side_effects!(test_tagged_template, "const x = tag`hello`;");
1838
1839 no_side_effects!(
1841 test_tagged_template_string_raw,
1842 "const x = String.raw`hello ${world}`;"
1843 );
1844
1845 no_side_effects!(test_regex_literal, "const re = /pattern/g;");
1846
1847 no_side_effects!(test_bigint_literal, "const big = 123n;");
1848
1849 no_side_effects!(test_optional_chaining_pure, "const x = obj?.prop;");
1850
1851 side_effects!(test_optional_chaining_call, "const x = obj?.method();");
1853
1854 no_side_effects!(
1855 test_multiple_exports_pure,
1856 "export const a = 1;\nexport const b = 2;\nexport const c = 3;"
1857 );
1858
1859 no_side_effects!(test_export_function, "export function foo() { return 1; }");
1860
1861 no_side_effects!(test_export_class, "export class Foo {}");
1862
1863 module_evaluation_is_side_effect_free!(test_reexport, "export { foo } from 'bar';");
1864
1865 module_evaluation_is_side_effect_free!(
1867 test_dynamic_import,
1868 "const mod = import('./module');"
1869 );
1870
1871 module_evaluation_is_side_effect_free!(
1872 test_dynamic_import_with_await,
1873 "const mod = await import('./module');"
1874 );
1875
1876 no_side_effects!(test_export_default_expression, "export default 1 + 2;");
1877
1878 side_effects!(
1879 test_export_default_expression_with_side_effect,
1880 "export default foo();"
1881 );
1882
1883 no_side_effects!(
1884 test_export_default_function,
1885 "export default function() { return 1; }"
1886 );
1887
1888 no_side_effects!(test_export_default_class, "export default class Foo {}");
1889
1890 no_side_effects!(
1891 test_export_named_with_pure_builtin,
1892 "export const result = Math.abs(-5);"
1893 );
1894
1895 side_effects!(
1896 test_multiple_exports_mixed,
1897 "export const a = 1;\nexport const b = foo();\nexport const c = 3;"
1898 );
1899 }
1900
1901 mod pure_constructors_tests {
1902 use super::*;
1903
1904 no_side_effects!(test_new_set, "const s = new Set();");
1905
1906 no_side_effects!(test_new_map, "const m = new Map();");
1907
1908 no_side_effects!(test_new_weakset, "const ws = new WeakSet();");
1909
1910 no_side_effects!(test_new_weakmap, "const wm = new WeakMap();");
1911
1912 no_side_effects!(test_new_regexp, "const re = new RegExp('pattern');");
1913
1914 no_side_effects!(test_new_date, "const d = new Date();");
1915
1916 no_side_effects!(test_new_error, "const e = new Error('message');");
1917
1918 no_side_effects!(test_new_promise, "const p = new Promise(() => {});");
1919 side_effects!(
1920 test_new_promise_effectful,
1921 "const p = new Promise(() => {console.log('hello')});"
1922 );
1923
1924 no_side_effects!(test_new_array, "const arr = new Array(10);");
1925
1926 no_side_effects!(test_new_object, "const obj = new Object();");
1927
1928 no_side_effects!(test_new_typed_array, "const arr = new Uint8Array(10);");
1929
1930 no_side_effects!(test_new_url, "const url = new URL('https://example.com');");
1931
1932 no_side_effects!(
1933 test_new_url_search_params,
1934 "const params = new URLSearchParams();"
1935 );
1936
1937 side_effects!(
1939 test_pure_constructor_with_impure_args,
1940 "const s = new Set([foo()]);"
1941 );
1942
1943 no_side_effects!(
1944 test_multiple_pure_constructors,
1945 "const s = new Set();\nconst m = new Map();\nconst re = new RegExp('test');"
1946 );
1947
1948 side_effects!(
1950 test_unknown_constructor,
1951 "const custom = new CustomClass();"
1952 );
1953
1954 side_effects!(
1955 test_mixed_constructors,
1956 "const s = new Set();\nconst custom = new CustomClass();\nconst m = new Map();"
1957 );
1958 }
1959
1960 mod shadowing_detection_tests {
1961 use super::*;
1962
1963 side_effects!(
1965 test_shadowed_math,
1966 r#"
1967 const Math = { abs: () => console.log('side effect') };
1968 const result = Math.abs(-5);
1969 "#
1970 );
1971
1972 side_effects!(
1974 test_shadowed_object,
1975 r#"
1976 const Object = { keys: () => sideEffect() };
1977 const result = Object.keys({});
1978 "#
1979 );
1980
1981 side_effects!(
1983 test_shadowed_array_constructor,
1984 r#"
1985 const Array = class { constructor() { sideEffect(); } };
1986 const arr = new Array();
1987 "#
1988 );
1989
1990 side_effects!(
1992 test_shadowed_set_constructor,
1993 r#"
1994 const Set = class { constructor() { sideEffect(); } };
1995 const s = new Set();
1996 "#
1997 );
1998
1999 side_effects!(
2001 test_shadowed_map_constructor,
2002 r#"
2003 {
2004 const Map = class { constructor() { sideEffect(); } };
2005 const m = new Map();
2006 }
2007 "#
2008 );
2009
2010 no_side_effects!(
2012 test_global_math_not_shadowed,
2013 r#"
2014 const result = Math.abs(-5);
2015 "#
2016 );
2017
2018 no_side_effects!(
2020 test_global_object_not_shadowed,
2021 r#"
2022 const keys = Object.keys({ a: 1, b: 2 });
2023 "#
2024 );
2025
2026 no_side_effects!(
2028 test_global_array_constructor_not_shadowed,
2029 r#"
2030 const arr = new Array(1, 2, 3);
2031 "#
2032 );
2033
2034 side_effects!(
2036 test_shadowed_by_import,
2037 r#"
2038 import { Math } from './custom-math';
2039 const result = Math.abs(-5);
2040 "#
2041 );
2042
2043 side_effects!(
2045 test_nested_scope_shadowing,
2046 r#"
2047 {
2048 const Math = { floor: () => sideEffect() };
2049 const result = Math.floor(4.5);
2050 }
2051 "#
2052 );
2053
2054 no_side_effects!(
2058 test_parameter_shadowing,
2059 r#"
2060 function test(RegExp) {
2061 return new RegExp('test');
2062 }
2063 "#
2064 );
2065
2066 side_effects!(
2068 test_shadowing_with_var,
2069 r#"
2070 var Number = { isNaN: () => sideEffect() };
2071 const check = Number.isNaN(123);
2072 "#
2073 );
2074
2075 no_side_effects!(
2077 test_global_regexp_not_shadowed,
2078 r#"
2079 const re = new RegExp('[a-z]+');
2080 "#
2081 );
2082 }
2083
2084 mod literal_receiver_methods_tests {
2085 use super::*;
2086
2087 no_side_effects!(
2089 test_string_literal_to_lower_case,
2090 r#"const result = "HELLO".toLowerCase();"#
2091 );
2092
2093 no_side_effects!(
2094 test_string_literal_to_upper_case,
2095 r#"const result = "hello".toUpperCase();"#
2096 );
2097
2098 no_side_effects!(
2099 test_string_literal_slice,
2100 r#"const result = "hello world".slice(0, 5);"#
2101 );
2102
2103 no_side_effects!(
2104 test_string_literal_split,
2105 r#"const result = "a,b,c".split(',');"#
2106 );
2107
2108 no_side_effects!(
2109 test_string_literal_trim,
2110 r#"const result = " hello ".trim();"#
2111 );
2112
2113 no_side_effects!(
2114 test_string_literal_replace,
2115 r#"const result = "hello".replace('h', 'H');"#
2116 );
2117
2118 no_side_effects!(
2119 test_string_literal_includes,
2120 r#"const result = "hello world".includes('world');"#
2121 );
2122
2123 no_side_effects!(
2125 test_array_literal_map,
2126 r#"const result = [1, 2, 3].map(x => x * 2);"#
2127 );
2128 side_effects!(
2129 test_array_literal_map_with_effectful_callback,
2130 r#"const result = [1, 2, 3].map(x => {globalThis.something.push(x)});"#
2131 );
2132
2133 no_side_effects!(
2135 test_number_literal_to_fixed,
2136 r#"const result = (3.14159).toFixed(2);"#
2137 );
2138
2139 no_side_effects!(
2140 test_number_literal_to_string,
2141 r#"const result = (42).toString();"#
2142 );
2143
2144 no_side_effects!(
2145 test_number_literal_to_exponential,
2146 r#"const result = (123.456).toExponential(2);"#
2147 );
2148
2149 no_side_effects!(
2151 test_boolean_literal_to_string,
2152 r#"const result = true.toString();"#
2153 );
2154
2155 no_side_effects!(
2156 test_boolean_literal_value_of,
2157 r#"const result = false.valueOf();"#
2158 );
2159
2160 no_side_effects!(
2162 test_regexp_literal_to_string,
2163 r#"const result = /[a-z]+/.toString();"#
2164 );
2165
2166 no_side_effects!(
2169 test_regexp_literal_test,
2170 r#"const result = /[a-z]+/g.test("hello");"#
2171 );
2172
2173 no_side_effects!(
2174 test_regexp_literal_exec,
2175 r#"const result = /(\d+)/g.exec("test123");"#
2176 );
2177
2178 side_effects!(
2181 test_array_literal_with_impure_elements,
2182 r#"const result = [foo(), 2, 3].map(x => x * 2);"#
2183 );
2184
2185 no_side_effects!(
2189 test_array_literal_map_with_callback,
2190 r#"const result = [1, 2, 3].map(x => x * 2);"#
2191 );
2192 }
2193
2194 mod class_expression_side_effects_tests {
2195 use super::*;
2196
2197 no_side_effects!(test_class_no_extends_no_static, "class Foo {}");
2199
2200 no_side_effects!(test_class_pure_extends, "class Foo extends Bar {}");
2202
2203 side_effects!(
2205 test_class_extends_with_call,
2206 "class Foo extends someMixinFunction() {}"
2207 );
2208
2209 side_effects!(
2211 test_class_extends_with_complex_expr,
2212 "class Foo extends (Bar || Baz()) {}"
2213 );
2214
2215 side_effects!(
2217 test_class_static_property_with_call,
2218 r#"
2219 class Foo {
2220 static foo = someFunction();
2221 }
2222 "#
2223 );
2224
2225 no_side_effects!(
2227 test_class_static_property_pure,
2228 r#"
2229 class Foo {
2230 static foo = 42;
2231 }
2232 "#
2233 );
2234
2235 no_side_effects!(
2237 test_class_static_property_array_literal,
2238 r#"
2239 class Foo {
2240 static foo = [1, 2, 3];
2241 }
2242 "#
2243 );
2244
2245 side_effects!(
2247 test_class_static_block,
2248 r#"
2249 class Foo {
2250 static {
2251 console.log("hello");
2252 }
2253 }
2254 "#
2255 );
2256
2257 no_side_effects!(
2258 test_class_static_block_empty,
2259 r#"
2260 class Foo {
2261 static {}
2262 }
2263 "#
2264 );
2265
2266 no_side_effects!(
2268 test_class_instance_property_with_call,
2269 r#"
2270 class Foo {
2271 foo = someFunction();
2272 }
2273 "#
2274 );
2275
2276 no_side_effects!(
2278 test_class_constructor_with_side_effects,
2279 r#"
2280 class Foo {
2281 constructor() {
2282 console.log("constructor");
2283 }
2284 }
2285 "#
2286 );
2287
2288 no_side_effects!(
2290 test_class_method,
2291 r#"
2292 class Foo {
2293 method() {
2294 console.log("method");
2295 }
2296 }
2297 "#
2298 );
2299
2300 side_effects!(
2302 test_class_expr_extends_with_call,
2303 "const Foo = class extends getMixin() {};"
2304 );
2305
2306 side_effects!(
2308 test_class_expr_static_with_call,
2309 r#"
2310 const Foo = class {
2311 static prop = initValue();
2312 };
2313 "#
2314 );
2315
2316 no_side_effects!(
2318 test_class_expr_static_pure,
2319 r#"
2320 const Foo = class {
2321 static prop = "hello";
2322 };
2323 "#
2324 );
2325
2326 side_effects!(
2328 test_export_class_with_side_effects,
2329 r#"
2330 export class Foo extends getMixin() {
2331 static prop = init();
2332 }
2333 "#
2334 );
2335
2336 side_effects!(
2338 test_export_default_class_with_side_effects,
2339 r#"
2340 export default class Foo {
2341 static { console.log("init"); }
2342 }
2343 "#
2344 );
2345
2346 no_side_effects!(
2348 test_export_class_no_side_effects,
2349 r#"
2350 export class Foo {
2351 method() {
2352 console.log("method");
2353 }
2354 }
2355 "#
2356 );
2357
2358 side_effects!(
2360 test_class_mixed_static_properties,
2361 r#"
2362 class Foo {
2363 static a = 1;
2364 static b = impureCall();
2365 static c = 3;
2366 }
2367 "#
2368 );
2369
2370 no_side_effects!(
2372 test_class_static_property_pure_builtin,
2373 r#"
2374 class Foo {
2375 static value = Math.abs(-5);
2376 }
2377 "#
2378 );
2379
2380 side_effects!(
2382 test_class_computed_property_with_call,
2383 r#"
2384 class Foo {
2385 [computeName()]() {
2386 return 42;
2387 }
2388 }
2389 "#
2390 );
2391
2392 no_side_effects!(
2394 test_class_computed_property_pure,
2395 r#"
2396 class Foo {
2397 ['method']() {
2398 return 42;
2399 }
2400 }
2401 "#
2402 );
2403 }
2404
2405 mod complex_variable_declarations_tests {
2406 use super::*;
2407
2408 no_side_effects!(test_destructure_simple, "const { foo } = obj;");
2410
2411 side_effects!(
2413 test_destructure_default_with_call,
2414 "const { foo = someFunction() } = obj;"
2415 );
2416
2417 no_side_effects!(test_destructure_default_pure, "const { foo = 42 } = obj;");
2419
2420 no_side_effects!(
2422 test_destructure_default_array_literal,
2423 "const { foo = ['hello'] } = obj;"
2424 );
2425
2426 no_side_effects!(
2428 test_destructure_default_object_literal,
2429 "const { foo = { bar: 'baz' } } = obj;"
2430 );
2431
2432 side_effects!(
2434 test_destructure_nested_with_call,
2435 "const { a: { b = sideEffect() } } = obj;"
2436 );
2437
2438 side_effects!(
2440 test_array_destructure_default_with_call,
2441 "const [a, b = getDefault()] = arr;"
2442 );
2443
2444 no_side_effects!(
2446 test_array_destructure_default_pure,
2447 "const [a, b = 10] = arr;"
2448 );
2449
2450 side_effects!(
2452 test_multiple_destructure_mixed,
2453 "const { foo = 1, bar = compute() } = obj;"
2454 );
2455
2456 no_side_effects!(test_destructure_rest_pure, "const { foo, ...rest } = obj;");
2458
2459 side_effects!(
2461 test_destructure_complex_with_side_effect,
2462 r#"
2463 const {
2464 a,
2465 b: { c = sideEffect() },
2466 d = [1, 2, 3]
2467 } = obj;
2468 "#
2469 );
2470
2471 no_side_effects!(
2473 test_destructure_complex_pure,
2474 r#"
2475 const {
2476 a,
2477 b: { c = 5 },
2478 d = [1, 2, 3]
2479 } = obj;
2480 "#
2481 );
2482
2483 side_effects!(
2485 test_export_destructure_with_side_effect,
2486 "export const { foo = init() } = obj;"
2487 );
2488
2489 no_side_effects!(
2491 test_export_destructure_pure,
2492 "export const { foo = 42 } = obj;"
2493 );
2494
2495 no_side_effects!(
2497 test_destructure_default_pure_builtin,
2498 "const { foo = Math.abs(-5) } = obj;"
2499 );
2500
2501 no_side_effects!(
2503 test_destructure_default_pure_annotation,
2504 "const { foo = /*#__PURE__*/ compute() } = obj;"
2505 );
2506 }
2507
2508 mod decorator_side_effects_tests {
2509 use super::*;
2510
2511 side_effects!(
2513 test_class_decorator,
2514 r#"
2515 @decorator
2516 class Foo {}
2517 "#
2518 );
2519
2520 side_effects!(
2522 test_method_decorator,
2523 r#"
2524 class Foo {
2525 @decorator
2526 method() {}
2527 }
2528 "#
2529 );
2530
2531 side_effects!(
2533 test_property_decorator,
2534 r#"
2535 class Foo {
2536 @decorator
2537 prop = 1;
2538 }
2539 "#
2540 );
2541
2542 side_effects!(
2544 test_multiple_decorators,
2545 r#"
2546 @decorator1
2547 @decorator2
2548 class Foo {
2549 @propDecorator
2550 prop = 1;
2551
2552 @methodDecorator
2553 method() {}
2554 }
2555 "#
2556 );
2557
2558 side_effects!(
2560 test_decorator_with_args,
2561 r#"
2562 @decorator(config())
2563 class Foo {}
2564 "#
2565 );
2566 }
2567
2568 mod additional_edge_cases_tests {
2569 use super::*;
2570
2571 no_side_effects!(
2573 test_super_property_pure,
2574 r#"
2575 class Foo extends Bar {
2576 method() {
2577 return super.parentMethod;
2578 }
2579 }
2580 "#
2581 );
2582
2583 no_side_effects!(
2585 test_super_call_in_method,
2586 r#"
2587 class Foo extends Bar {
2588 method() {
2589 return super.parentMethod();
2590 }
2591 }
2592 "#
2593 );
2594
2595 no_side_effects!(test_import_meta, "const url = import.meta.url;");
2597
2598 no_side_effects!(
2600 test_new_target,
2601 r#"
2602 function Foo() {
2603 console.log(new.target);
2604 }
2605 "#
2606 );
2607
2608 side_effects!(test_jsx_element, "const el = <div>Hello</div>;");
2610
2611 side_effects!(test_jsx_fragment, "const el = <>Hello</>;");
2613
2614 no_side_effects!(
2616 test_private_field_access,
2617 r#"
2618 class Foo {
2619 #privateField = 42;
2620 method() {
2621 return this.#privateField;
2622 }
2623 }
2624 "#
2625 );
2626
2627 no_side_effects!(
2629 test_super_computed_property_pure,
2630 r#"
2631 class Foo extends Bar {
2632 method() {
2633 return super['prop'];
2634 }
2635 }
2636 "#
2637 );
2638
2639 no_side_effects!(
2641 test_static_block_pure_content,
2642 r#"
2643 class Foo {
2644 static {
2645 const x = 1;
2646 const y = 2;
2647 }
2648 }
2649 "#
2650 );
2651
2652 side_effects!(
2654 test_static_block_with_side_effect_inside,
2655 r#"
2656 class Foo {
2657 static {
2658 sideEffect();
2659 }
2660 }
2661 "#
2662 );
2663
2664 no_side_effects!(
2666 test_this_expression,
2667 r#"
2668 class Foo {
2669 method() {
2670 return this;
2671 }
2672 }
2673 "#
2674 );
2675
2676 no_side_effects!(
2678 test_spread_pure_in_call,
2679 "const result = Math.max(...[1, 2, 3]);"
2680 );
2681
2682 side_effects!(
2684 test_spread_with_side_effect,
2685 "const result = Math.max(...getArray());"
2686 );
2687
2688 no_side_effects!(
2690 test_super_complex_access,
2691 r#"
2692 class Foo extends Bar {
2693 static method() {
2694 return super.parentMethod;
2695 }
2696 }
2697 "#
2698 );
2699
2700 no_side_effects!(
2702 test_getter_definition,
2703 r#"
2704 const obj = {
2705 get foo() {
2706 return this._foo;
2707 }
2708 };
2709 "#
2710 );
2711
2712 no_side_effects!(
2714 test_async_function_declaration,
2715 r#"
2716 async function foo() {
2717 return await something;
2718 }
2719 "#
2720 );
2721
2722 no_side_effects!(
2724 test_generator_declaration,
2725 r#"
2726 function* foo() {
2727 yield 1;
2728 yield 2;
2729 }
2730 "#
2731 );
2732
2733 no_side_effects!(
2735 test_async_generator,
2736 r#"
2737 async function* foo() {
2738 yield await something;
2739 }
2740 "#
2741 );
2742
2743 side_effects!(
2748 test_nullish_coalescing_with_side_effect,
2749 "const x = a ?? sideEffect();"
2750 );
2751
2752 side_effects!(
2754 test_logical_or_with_side_effect,
2755 "const x = a || sideEffect();"
2756 );
2757
2758 side_effects!(
2760 test_logical_and_with_side_effect,
2761 "const x = a && sideEffect();"
2762 );
2763 }
2764
2765 mod common_js_modules_tests {
2766 use super::*;
2767
2768 no_side_effects!(test_common_js_exports, "exports.foo = 'a'");
2771 no_side_effects!(test_common_js_exports_module, "module.exports.foo = 'a'");
2772 no_side_effects!(test_common_js_exports_assignment, "module.exports = {}");
2773 no_side_effects!(
2774 test_common_js_function_exports,
2775 "exports.foo = function () { return 1; }; exports.bar = 2;"
2776 );
2777
2778 module_evaluation_is_side_effect_free!(
2779 test_common_js_reexport,
2780 "module.exports = require('./other');"
2781 );
2782 module_evaluation_is_side_effect_free!(
2783 test_common_js_named_reexports,
2784 "exports.a = require('./a'); exports.b = require('./b');"
2785 );
2786
2787 side_effects!(
2789 test_common_js_export_impure_value,
2790 "exports.foo = sideEffect();"
2791 );
2792 side_effects!(
2794 test_common_js_export_computed_side_effect,
2795 "exports[sideEffect()] = 'a';"
2796 );
2797 side_effects!(test_module_non_export_assignment, "module.foo = 'a';");
2799 side_effects!(
2801 test_shadowed_exports_assignment,
2802 "let exports = {}; exports.foo = 'a';"
2803 );
2804
2805 side_effects!(
2809 test_cjs_export_setter_invoked,
2810 "module.exports = { set foo(v) { sideEffect() } }; module.exports.foo = 1;"
2811 );
2812
2813 no_side_effects!(
2816 test_cjs_export_fresh_then_write,
2817 "module.exports = {}; module.exports.foo = 1;"
2818 );
2819 side_effects!(
2823 test_cjs_export_reexport_then_write,
2824 "module.exports = require('./other'); module.exports.extra = 1;"
2825 );
2826 side_effects!(
2827 test_cjs_export_alias_then_write,
2828 "module.exports = other; module.exports.foo = 1;"
2829 );
2830 side_effects!(
2833 test_cjs_export_literal_capturing_global_then_write,
2834 "module.exports = { g: globalThis }; module.exports.g.x = 1;"
2835 );
2836 side_effects!(
2839 test_cjs_export_reexport_then_write_sequence,
2840 "module.exports = require('./other'), module.exports.extra = 1;"
2841 );
2842 side_effects!(
2846 test_cjs_export_reexport_in_logical_then_write,
2847 "x && (module.exports = require('./other')); module.exports.extra = 1;"
2848 );
2849 side_effects!(
2850 test_cjs_export_setter_attached_in_conditional,
2851 "x ? (module.exports = { set foo(v) { sideEffect() } }) : 0;"
2852 );
2853 no_side_effects!(
2856 test_cjs_export_reassign_in_function_body_is_pure,
2857 "function f() { module.exports = require('./other'); } module.exports.foo = 1;"
2858 );
2859
2860 side_effects!(
2865 test_cjs_export_setter_in_static_block,
2866 "class C { static { module.exports = { set foo(v) { sideEffect() } }; \
2867 module.exports.foo = 1; } }"
2868 );
2869 no_side_effects!(
2875 test_cjs_export_setter_in_constructor_is_pure,
2876 "class C { constructor() { module.exports = { set foo(v) { sideEffect() } }; } } \
2877 module.exports.foo = 1;"
2878 );
2879
2880 side_effects!(
2884 test_cjs_export_setter_in_static_property,
2885 "class C { static x = (module.exports = { set foo(v) { sideEffect() } }); } \
2886 module.exports.foo = 1;"
2887 );
2888
2889 no_side_effects!(
2890 test_cjs_export_define_property,
2891 "Object.defineProperty(exports, '__esModule', { value: true }); exports.foo = 1;"
2892 );
2893 no_side_effects!(
2894 test_cjs_export_define_property_module_exports,
2895 "Object.defineProperty(module.exports, 'foo', { value: 1 });"
2896 );
2897 no_side_effects!(
2898 test_cjs_export_define_property_enumerable,
2899 "Object.defineProperty(exports, 'foo', { value: 1, enumerable: true });"
2900 );
2901 no_side_effects!(
2902 test_cjs_export_define_property_getter,
2903 "Object.defineProperty(exports, 'foo', { get() { return 1; } });"
2904 );
2905 no_side_effects!(
2906 test_cjs_export_define_property_getter_function,
2907 "Object.defineProperty(exports, 'foo', { get: function() { return 1; } });"
2908 );
2909 module_evaluation_is_side_effect_free!(
2911 test_cjs_export_define_property_reexport_barrel,
2912 "Object.defineProperty(exports, '__esModule', { value: true }); var _a = \
2913 require('./a'); Object.defineProperty(exports, 'a', { enumerable: true, get: \
2914 function() { return _a.a; } });"
2915 );
2916 side_effects!(
2917 test_cjs_export_define_property_setter_then_write,
2918 "Object.defineProperty(exports, 'foo', { set(v) { sideEffect() } }); exports.foo = 1;"
2919 );
2920 side_effects!(
2921 test_cjs_export_define_property_getter_then_write,
2922 "Object.defineProperty(exports, 'foo', { get() { return 1; } }); exports.bar = 1;"
2923 );
2924 side_effects!(
2925 test_cjs_export_define_property_spread_descriptor_then_write,
2926 "Object.defineProperty(exports, 'foo', { ...descriptor }); exports.bar = 1;"
2927 );
2928 side_effects!(
2929 test_cjs_export_define_property_descriptor_getter,
2930 "Object.defineProperty(exports, 'foo', { get value() { return sideEffect(); } });"
2931 );
2932 side_effects!(
2933 test_cjs_export_define_property_computed_descriptor_key_then_write,
2934 "Object.defineProperty(exports, 'foo', { [key]: fn }); exports.bar = 1;"
2935 );
2936 side_effects!(
2937 test_cjs_export_define_property_nested_setter_then_write,
2938 "Object.defineProperty(exports, 'foo', { value: { set a(v) { sideEffect() } } }); \
2939 exports.foo.a = 1;"
2940 );
2941 side_effects!(
2942 test_cjs_export_define_property_computed_key,
2943 "Object.defineProperty(exports, someVar, { value: 1 });"
2944 );
2945 side_effects!(
2946 test_define_property_foreign_target,
2947 "Object.defineProperty(globalThis, 'foo', { value: 1 });"
2948 );
2949 side_effects!(
2950 test_cjs_export_define_property_impure_value,
2951 "Object.defineProperty(exports, 'foo', { value: sideEffect() });"
2952 );
2953 side_effects!(
2954 test_cjs_export_define_property_after_reexport,
2955 "module.exports = require('./other'); Object.defineProperty(module.exports, 'foo', { \
2956 value: 1 });"
2957 );
2958 }
2959
2960 mod local_variable_mutation_tests {
2961 use super::*;
2962
2963 no_side_effects!(
2967 test_const_object_build,
2968 "const config = {}; config['a'] = 'a'; config['b'] = 'b'; export default config;"
2969 );
2970 no_side_effects!(test_const_member_assignment, "const o = {}; o.a = 1;");
2971 no_side_effects!(test_const_array_index, "const a = []; a[0] = 1;");
2972 no_side_effects!(test_const_nested_member, "const o = { a: {} }; o.a.b = 1;");
2973
2974 side_effects!(
2977 test_aliased_import_mutation,
2978 "import config from './config'; const c = config; c.enabled = true;"
2979 );
2980 side_effects!(
2982 test_aliased_global_mutation,
2983 "const g = globalThis; g.shared = 1;"
2984 );
2985 side_effects!(
2987 test_imported_binding_mutation,
2988 "import obj from 'x'; obj.foo = 1;"
2989 );
2990 side_effects!(
2992 test_non_safe_assignment_constant_init,
2993 "const o = makeObj(); o.a = 1;"
2994 );
2995 side_effects!(test_let_object_mutation, "let o = {}; o.a = 1;");
2997 side_effects!(test_global_assignment, "globalThis.shared = 1;");
2999 side_effects!(test_undeclared_assignment, "leaked = 1;");
3000 side_effects!(
3002 test_safe_assignment_constant_impure_value,
3003 "const o = {}; o.a = sideEffect();"
3004 );
3005 side_effects!(
3007 test_safe_assignment_constant_computed_key_side_effect,
3008 "const o = {}; o[sideEffect()] = 1;"
3009 );
3010 side_effects!(
3013 test_local_setter_invoked,
3014 "const o = { set x(v) { sideEffect() } }; o.x = 1;"
3015 );
3016 side_effects!(
3017 test_local_nested_setter_invoked,
3018 "const o = {}; o.a = { set y(v) { sideEffect() } }; o.a.y = 1;"
3019 );
3020 side_effects!(
3023 test_local_setter_attached_after_init,
3024 "const o = {}; o.x = { set y(v) { sideEffect() } };"
3025 );
3026 side_effects!(
3029 test_local_setter_via_define_property,
3030 "const o = {}; Object.defineProperty(o, 'b', { set(x) { this.a = x / 2 } }); o.b = 4;"
3031 );
3032 side_effects!(
3036 test_local_setter_attached_in_conditional,
3037 "const o = {}; x && (o.a = { set y(v) { sideEffect() } }); o.a.y = 1;"
3038 );
3039
3040 side_effects!(
3041 test_literal_capturing_global_nested_mutation,
3042 "const box = { g: globalThis }; box.g.x = 1;"
3043 );
3044 side_effects!(
3045 test_literal_capturing_global_direct_mutation,
3046 "const box = { g: globalThis }; box.g = 1;"
3047 );
3048 side_effects!(
3049 test_literal_capturing_global_deeply_nested_mutation,
3050 "const box = { a: { b: globalThis } }; box.a.b.x = 1;"
3051 );
3052 side_effects!(
3053 test_array_literal_capturing_global_nested_mutation,
3054 "const box = [globalThis]; box[0].x = 1;"
3055 );
3056 side_effects!(
3057 test_literal_capturing_window_mutation,
3058 "const box = { w: window }; box.w.x = 1;"
3059 );
3060 side_effects!(
3061 test_literal_capturing_import_mutation,
3062 "import obj from './obj'; const box = { obj }; box.obj.foo = 1;"
3063 );
3064 side_effects!(
3065 test_literal_capturing_require_result_mutation,
3066 "const box = { dep: require('./dep') }; box.dep.x = 1;"
3067 );
3068 side_effects!(
3069 test_literal_spreading_import_mutation,
3070 "import obj from './obj'; const box = { ...obj }; box.foo.bar = 1;"
3071 );
3072 side_effects!(
3073 test_foreign_value_assigned_then_nested_mutation,
3074 "const box = {}; box.g = globalThis; box.g.x = 1;"
3075 );
3076 side_effects!(
3077 test_foreign_value_assigned_then_direct_mutation,
3078 "const box = {}; box.g = globalThis; box.a = 1;"
3079 );
3080 side_effects!(
3081 test_require_result_assigned_then_direct_mutation,
3082 "const box = {}; box.dep = require('./dep'); box.a = 1;"
3083 );
3084 no_side_effects!(
3085 test_deeply_nested_literal_mutation,
3086 "const box = { a: { b: {} } }; box.a.b.c = 1;"
3087 );
3088 no_side_effects!(
3089 test_nested_literal_assigned_then_mutated,
3090 "const box = {}; box.a = { b: {} }; box.a.b.c = 1;"
3091 );
3092 }
3093}