Repository: swc-project/swc (rust)

Issue:
Minify: multiple same temp symbols declarations
### Describe the bug

code like: `const bar = ((v) => v)(1);`
will generate a temp value as function inline parameters like this:

```js
let v;
const bar = (v = 1, 1);
```

However if there are 2 same declarations with same parameter symbols:

```js
let v, v;
const bar = (v = 1, 1), foo = (v = 2, 2);
```

There will be 2 same declarations

### Input code

```typescript
const bar = ((v) => v)(1);
const foo = ((v) => v)(2);

eval(bar);
eval(foo);
```


### Config

```json
{
  "jsc": {
    "parser": {
      "syntax": "ecmascript",
      "jsx": false
    },
    "target": "es2022",
    "externalHelpers": true,
    "transform": {
      "react": {
        "development": true,
        "runtime": "automatic"
      }
    },
    "minify": {
      "compress": {
        "arguments": false,
        "arrows": true,
        "booleans": true,
        "booleans_as_integers": false,
        "collapse_vars": true,
        "comparisons": true,
        "computed_props": true,
        "conditionals": true,
        "dead_code": true,
        "directives": true,
        "drop_console": false,
        "drop_debugger": true,
        "evaluate": true,
        "expression": false,
        "hoist_funs": false,
        "hoist_props": true,
        "hoist_vars": false,
        "if_return": true,
        "join_vars": true,
        "keep_classnames": false,
        "keep_fargs": true,
        "keep_fnames": false,
        "keep_infinity": false,
        "loops": true,
        "negate_iife": true,
        "properties": true,
        "reduce_funcs": false,
        "reduce_vars": false,
        "side_effects": true,
        "switches": true,
        "typeofs": true,
        "unsafe": false,
        "unsafe_arrows": false,
        "unsafe_comps": false,
        "unsafe_Function": false,
        "unsafe_math": false,
        "unsafe_symbols": false,
        "unsafe_methods": false,
        "unsafe_proto": false,
        "unsafe_regexp": false,
        "unsafe_undefined": false,
        "unused": true,
        "const_to_let": false,
        "pristine_globals": true,
        "passes": 99
      },
      "mangle": {
        "toplevel": false,
        "keep_classnames": false,
        "keep_fnames": false,
        "keep_private_props": false,
        "ie8": false,
        "safari10": false
      },
      "inlineSourcesContent": false
    },
    "loose": false
  },
  "minify": false,
  "isModule": true,
  "module": {
    "type": "es6"
  },
}
```


### Playground link (or link to the minimal reproduction)

https://play.swc.rs/?version=1.4.16&code=H4sIAAAAAAAAA0vOzysuUUhKLFKwVdDQKNNUsLVTKNPUMNS05koGS6Xl56NKGQGluFLLEnM0gLqAbDATqArIBACKP7PKTQAAAA%3D%3D&config=H4sIAAAAAAAAA42VvY7bMAyA9z5F4PmGa4ai17VA0aVTH8BQJMpRKouCSOUSHPLupe0kdRI66GbzE0nxVx%2BfVqtmR7b5tvqQT%2FnJphCU679I6JjYHETSgO0N2RIyNy8XuqMBeRMJRtFpIg2b0gGPWrR%2BXa%2FPGg0cGEoy8SfEDIXkAJcKF6ViEnks%2Fdx%2FAWN5JhCRgz1EzD0kvjEwna%2BJQw%2BDa1MZe8PBNmd8urljH1Lwx7kvi30uQHTrTkKpgy%2B6RPoyZwXf6fEWG8QIEs0yaQ21ITF0UxruDVuM0WSCdm%2BKYmW4qSmBUHMxwMrg2lwwqzy5wAGlDgp1YFxr0YGCQgHLYQ%2BamvgStUQSnhLPiB1sateN%2FXWnDXsTq2HFJxzGkshtFatbDMStr0lL4QQXcjDBc3LvNYNvC3At6VFvhyEt1OQPgGQgGqJketDsjie89NOStn%2BqGZKXluWjwiOqUSboJKltCF7J7JAZKBy0ahZw1cKQWatd54wX0kfBQQveS68opuk9sN1qTvmYAb0CpL7Ga101gfY6hQt8GIgn%2BIdEyXqDnU%2FIFtkuUzr2G4xPHPTAW3RPDkgpGJdxkS1xyMu8JgfSGuDUI5VG8LgEZAAY2zju6Xu1LMuFxWTbRdyoeyJLo49VfHu7rNfrs9Cb1I1rYLZGGXMcFvdSd%2F%2FH5DylcuP90OyXgX8Yaviq9aqReQyfX29esXkoIUVJw2%2BsxQJ9R9nYiRvtyZMRJJiRUf7vkbk6bgL9QlfjfCKb%2FiI5v8PDKEyv55dmsnX6C1nhPmStBwAA

### SWC Info output

_No response_

### Expected behavior

The temporary parameters need to be deconflicted.

### Actual behavior

_No response_

### Version

1.4.16

### Additional context

_No response_

Pull request description:
fix(es/minifier): Do not add vars if `eval` exists
**Related issue:**

 - Closes #8886

Patch:
diff --git a/crates/swc_ecma_minifier/src/compress/optimize/iife.rs b/crates/swc_ecma_minifier/src/compress/optimize/iife.rs
index 200278abf2..dbd2323040 100644
--- a/crates/swc_ecma_minifier/src/compress/optimize/iife.rs
+++ b/crates/swc_ecma_minifier/src/compress/optimize/iife.rs
@@ -159,8 +159,14 @@ impl Optimizer<'_> {
 
         fn clean_params(callee: &mut Expr) {
             match callee {
-                Expr::Arrow(callee) => callee.params.retain(|p| !p.is_invalid()),
-                Expr::Fn(callee) => callee.function.params.retain(|p| !p.pat.is_invalid()),
+                Expr::Arrow(callee) => {
+                    // Drop invalid nodes
+                    callee.params.retain(|p| !p.is_invalid())
+                }
+                Expr::Fn(callee) => {
+                    // Drop invalid nodes
+                    callee.function.params.retain(|p| !p.pat.is_invalid())
+                }
                 _ => {}
             }
         }
@@ -301,9 +307,9 @@ impl Optimizer<'_> {
                     unreachable!("find_body and find_params should match")
                 }
             }
-        }
 
-        clean_params(callee);
+            clean_params(callee);
+        }
     }
 
     /// If a parameter is not used, we can ignore return value of the
@@ -519,6 +525,10 @@ impl Optimizer<'_> {
                         }
                     }
                     BlockStmtOrExpr::Expr(body) => {
+                        if !self.can_extract_param(&param_ids) {
+                            return;
+                        }
+
                         if let Expr::Lit(Lit::Num(..)) = &**body {
                             if self.ctx.in_obj_of_non_computed_member {
                                 return;
@@ -528,15 +538,9 @@ impl Optimizer<'_> {
                         self.changed = true;
                         report_change!("inline: Inlining a function call (arrow)");
 
-                        let vars = param_ids
-                            .iter()
-                            .map(|name| VarDeclarator {
-                                span: DUMMY_SP,
-                                name: name.clone().into(),
-                                init: Default::default(),
-                                definite: Default::default(),
-                            })
-                            .collect::<Vec<_>>();
+                        let mut exprs = vec![Box::new(make_number(DUMMY_SP, 0.0))];
+
+                        let vars = self.inline_fn_param(&param_ids, &mut call.args, &mut exprs);
 
                         if !vars.is_empty() {
                             self.prepend_stmts.push(
@@ -550,18 +554,6 @@ impl Optimizer<'_> {
                             )
                         }
 
-                        let mut exprs = vec![Box::new(make_number(DUMMY_SP, 0.0))];
-                        for (idx, param) in param_ids.iter().enumerate() {
-                            if let Some(arg) = call.args.get_mut(idx) {
-                                exprs.push(Box::new(Expr::Assign(AssignExpr {
-                                    span: DUMMY_SP,
-                                    op: op!("="),
-                                    left: param.clone().into(),
-                                    right: arg.expr.take(),
-                                })));
-                            }
-                        }
-
                         if call.args.len() > f.params.len() {
                             for arg in &mut call.args[f.params.len()..] {
                                 exprs.push(arg.expr.take());
@@ -697,6 +689,22 @@ impl Optimizer<'_> {
         }
     }
 
+    fn can_extract_param(&self, param_ids: &[Ident]) -> bool {
+        // Don't create top-level variables.
+        if !param_ids.is_empty() && !self.may_add_ident() {
+            for pid in param_ids {
+                if let Some(usage) = self.data.vars.get(&pid.to_id()) {
+                    if usage.ref_count > 1 || usage.assign_count > 0 || usage.inline_prevented {
+                        log_abort!("iife: [x] Cannot inline because of usage of `{}`", pid);
+                        return false;
+                    }
+                }
+            }
+        }
+
+        true
+    }
+
     fn can_inline_fn_like(&self, param_ids: &[Ident], body: &BlockStmt) -> bool {
         trace_op!("can_inline_fn_like");
 
@@ -712,16 +720,8 @@ impl Optimizer<'_> {
             }
         }
 
-        // Don't create top-level variables.
-        if !param_ids.is_empty() && !self.may_add_ident() {
-            for pid in param_ids {
-                if let Some(usage) = self.data.vars.get(&pid.to_id()) {
-                    if usage.ref_count > 1 || usage.assign_count > 0 || usage.inline_prevented {
-                        log_abort!("iife: [x] Cannot inline because of usage of `{}`", pid);
-                        return false;
-                    }
-                }
-            }
+        if !self.can_extract_param(param_ids) {
+            return false;
         }
 
         // Abort on eval.
@@ -835,27 +835,13 @@ impl Optimizer<'_> {
         true
     }
 
-    fn inline_fn_like(
+    fn inline_fn_param(
         &mut self,
         params: &[Ident],
-        body: &mut BlockStmt,
         args: &mut [ExprOrSpread],
-    ) -> Option<Expr> {
-        if !self.can_inline_fn_like(params, &*body) {
-            return None;
-        }
-
-        if args.iter().any(|arg| arg.spread.is_some()) {
-            return None;
-        }
-
-        if self.vars.inline_with_multi_replacer(body) {
-            self.changed = true;
-        }
-
+        exprs: &mut Vec<Box<Expr>>,
+    ) -> Vec<VarDeclarator> {
         let mut vars = Vec::new();
-        let mut exprs = Vec::new();
-        let param_len = params.len();
 
         for (idx, param) in params.iter().enumerate() {
             let arg = args.get_mut(idx).map(|arg| arg.expr.take());
@@ -903,6 +889,31 @@ impl Optimizer<'_> {
             });
         }
 
+        vars
+    }
+
+    fn inline_fn_like(
+        &mut self,
+        params: &[Ident],
+        body: &mut BlockStmt,
+        args: &mut [ExprOrSpread],
+    ) -> Option<Expr> {
+        if !self.can_inline_fn_like(params, &*body) {
+            return None;
+        }
+
+        if args.iter().any(|arg| arg.spread.is_some()) {
+            return None;
+        }
+
+        if self.vars.inline_with_multi_replacer(body) {
+            self.changed = true;
+        }
+
+        let param_len = params.len();
+        let mut exprs = Vec::new();
+        let vars = self.inline_fn_param(params, args, &mut exprs);
+
         if args.len() > param_len {
             for arg in &mut args[param_len..] {
                 exprs.push(arg.expr.take());
diff --git a/crates/swc_ecma_minifier/src/compress/optimize/mod.rs b/crates/swc_ecma_minifier/src/compress/optimize/mod.rs
index 062c16944f..265cf9fdb4 100644
--- a/crates/swc_ecma_minifier/src/compress/optimize/mod.rs
+++ b/crates/swc_ecma_minifier/src/compress/optimize/mod.rs
@@ -342,6 +342,14 @@ impl Optimizer<'_> {
     }
 
     fn may_add_ident(&self) -> bool {
+        if self.ctx.in_top_level() && self.data.top.has_eval_call {
+            return false;
+        }
+
+        if self.data.scopes.get(&self.ctx.scope).unwrap().has_eval_call {
+            return false;
+        }
+
         if !self.ctx.in_top_level() {
             return true;
         }


Test patch:
diff --git a/crates/swc/tests/fixture/issues-8xxx/8886/input/.swcrc b/crates/swc/tests/fixture/issues-8xxx/8886/input/.swcrc
new file mode 100644
index 0000000000..f534e23e5b
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8886/input/.swcrc
@@ -0,0 +1,80 @@
+{
+    "jsc": {
+        "parser": {
+            "syntax": "ecmascript",
+            "jsx": false
+        },
+        "target": "es2022",
+        "externalHelpers": true,
+        "transform": {
+            "react": {
+                "development": true,
+                "runtime": "automatic"
+            }
+        },
+        "minify": {
+            "compress": {
+                "arguments": false,
+                "arrows": true,
+                "booleans": true,
+                "booleans_as_integers": false,
+                "collapse_vars": true,
+                "comparisons": true,
+                "computed_props": true,
+                "conditionals": true,
+                "dead_code": true,
+                "directives": true,
+                "drop_console": false,
+                "drop_debugger": true,
+                "evaluate": true,
+                "expression": false,
+                "hoist_funs": false,
+                "hoist_props": true,
+                "hoist_vars": false,
+                "if_return": true,
+                "join_vars": true,
+                "keep_classnames": false,
+                "keep_fargs": true,
+                "keep_fnames": false,
+                "keep_infinity": false,
+                "loops": true,
+                "negate_iife": true,
+                "properties": true,
+                "reduce_funcs": false,
+                "reduce_vars": false,
+                "side_effects": true,
+                "switches": true,
+                "typeofs": true,
+                "unsafe": false,
+                "unsafe_arrows": false,
+                "unsafe_comps": false,
+                "unsafe_Function": false,
+                "unsafe_math": false,
+                "unsafe_symbols": false,
+                "unsafe_methods": false,
+                "unsafe_proto": false,
+                "unsafe_regexp": false,
+                "unsafe_undefined": false,
+                "unused": true,
+                "const_to_let": false,
+                "pristine_globals": true,
+                "passes": 99
+            },
+            "mangle": {
+                "toplevel": false,
+                "keep_classnames": false,
+                "keep_fnames": false,
+                "keep_private_props": false,
+                "ie8": false,
+                "safari10": false
+            },
+            "inlineSourcesContent": false
+        },
+        "loose": false
+    },
+    "minify": false,
+    "isModule": true,
+    "module": {
+        "type": "es6"
+    },
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-8xxx/8886/input/1.js b/crates/swc/tests/fixture/issues-8xxx/8886/input/1.js
new file mode 100644
index 0000000000..ff6142705a
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8886/input/1.js
@@ -0,0 +1,5 @@
+const bar = ((v) => v)(1);
+const foo = ((v) => v)(2);
+
+eval(bar);
+eval(foo);
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-8xxx/8886/output/1.js b/crates/swc/tests/fixture/issues-8xxx/8886/output/1.js
new file mode 100644
index 0000000000..c226397f85
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8886/output/1.js
@@ -0,0 +1,2 @@
+const bar = 1, foo = 2;
+eval(bar), eval(foo);
diff --git a/crates/swc_ecma_minifier/tests/exec.rs b/crates/swc_ecma_minifier/tests/exec.rs
index c7d625194b..5abd0fbffb 100644
--- a/crates/swc_ecma_minifier/tests/exec.rs
+++ b/crates/swc_ecma_minifier/tests/exec.rs
@@ -11212,3 +11212,14 @@ fn issue_8864_1() {
       ",
     )
 }
+
+#[test]
+fn issue_8886() {
+    run_default_exec_test(
+        "const bar = ((v) => v)(1);
+const foo = ((v) => v)(2);
+
+console.log(eval(bar));
+console.log(eval(foo));",
+    );
+}
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/8886/config.json b/crates/swc_ecma_minifier/tests/fixture/issues/8886/config.json
new file mode 100644
index 0000000000..3d2ef31f42
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/8886/config.json
@@ -0,0 +1,48 @@
+{
+    "defaults": true,
+    "arguments": false,
+    "arrows": true,
+    "booleans": true,
+    "booleans_as_integers": false,
+    "collapse_vars": true,
+    "comparisons": true,
+    "computed_props": true,
+    "conditionals": true,
+    "dead_code": true,
+    "directives": true,
+    "drop_console": false,
+    "drop_debugger": true,
+    "evaluate": true,
+    "expression": false,
+    "hoist_funs": false,
+    "hoist_props": true,
+    "hoist_vars": false,
+    "if_return": true,
+    "join_vars": true,
+    "keep_classnames": false,
+    "keep_fargs": true,
+    "keep_fnames": false,
+    "keep_infinity": false,
+    "loops": true,
+    "negate_iife": true,
+    "properties": true,
+    "reduce_funcs": false,
+    "reduce_vars": false,
+    "side_effects": true,
+    "switches": true,
+    "typeofs": true,
+    "unsafe": false,
+    "unsafe_arrows": false,
+    "unsafe_comps": false,
+    "unsafe_Function": false,
+    "unsafe_math": false,
+    "unsafe_symbols": false,
+    "unsafe_methods": false,
+    "unsafe_proto": false,
+    "unsafe_regexp": false,
+    "unsafe_undefined": false,
+    "unused": true,
+    "const_to_let": false,
+    "pristine_globals": true,
+    "passes": 99
+}
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/8886/input.js b/crates/swc_ecma_minifier/tests/fixture/issues/8886/input.js
new file mode 100644
index 0000000000..ff6142705a
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/8886/input.js
@@ -0,0 +1,5 @@
+const bar = ((v) => v)(1);
+const foo = ((v) => v)(2);
+
+eval(bar);
+eval(foo);
\ No newline at end of file
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/8886/mangle.json b/crates/swc_ecma_minifier/tests/fixture/issues/8886/mangle.json
new file mode 100644
index 0000000000..6840950aa2
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/8886/mangle.json
@@ -0,0 +1,8 @@
+{
+    "toplevel": false,
+    "keep_classnames": false,
+    "keep_fnames": false,
+    "keep_private_props": false,
+    "ie8": false,
+    "safari10": false
+}
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/8886/output.js b/crates/swc_ecma_minifier/tests/fixture/issues/8886/output.js
new file mode 100644
index 0000000000..c226397f85
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/8886/output.js
@@ -0,0 +1,2 @@
+const bar = 1, foo = 2;
+eval(bar), eval(foo);
diff --git a/crates/swc_ecma_minifier/tests/terser/compress/collapse_vars/issue_2298/output.js b/crates/swc_ecma_minifier/tests/terser/compress/collapse_vars/issue_2298/output.js
index 340b4181b6..5492f28eeb 100644
--- a/crates/swc_ecma_minifier/tests/terser/compress/collapse_vars/issue_2298/output.js
+++ b/crates/swc_ecma_minifier/tests/terser/compress/collapse_vars/issue_2298/output.js
@@ -1,14 +1,14 @@
-!(function () {
-    (function () {
+!function() {
+    (function() {
         var a = undefined;
         var undefined = a++;
         try {
-            !(function (b) {
+            !function(b) {
                 (void 0)[1] = "foo";
-            })();
+            }();
             console.log("FAIL");
         } catch (e) {
             console.log("PASS");
         }
     })();
-})();
+}();
diff --git a/crates/swc_ecma_minifier/tests/terser/compress/hoist_props/issue_3071_3/output.js b/crates/swc_ecma_minifier/tests/terser/compress/hoist_props/issue_3071_3/output.js
index b79b8656a7..b703b040b8 100644
--- a/crates/swc_ecma_minifier/tests/terser/compress/hoist_props/issue_3071_3/output.js
+++ b/crates/swc_ecma_minifier/tests/terser/compress/hoist_props/issue_3071_3/output.js
@@ -1,8 +1,10 @@
 var c = 0;
-(function (a, b) {
+(function(a, b) {
     (function f(o) {
         var n = 2;
-        while (--b + (o = { p: c++ }) && --n > 0);
+        while(--b + (o = {
+            p: c++
+        }) && --n > 0);
     })();
 })();
 console.log(c);
diff --git a/crates/swc_ecma_minifier/tests/terser/compress/identity/inline_identity_async/output.js b/crates/swc_ecma_minifier/tests/terser/compress/identity/inline_identity_async/output.js
index eecfe7f87d..b64f5376c1 100644
--- a/crates/swc_ecma_minifier/tests/terser/compress/identity/inline_identity_async/output.js
+++ b/crates/swc_ecma_minifier/tests/terser/compress/identity/inline_identity_async/output.js
@@ -1,2 +1,2 @@
-(async () => await 1)();
-(async (x) => await console.log(2))();
+(async ()=>await 1)();
+(async (x)=>await console.log(2))();
diff --git a/crates/swc_ecma_minifier/tests/terser/compress/properties/lhs_prop_2/output.js b/crates/swc_ecma_minifier/tests/terser/compress/properties/lhs_prop_2/output.js
index bd37d9a1c8..2de1031a21 100644
--- a/crates/swc_ecma_minifier/tests/terser/compress/properties/lhs_prop_2/output.js
+++ b/crates/swc_ecma_minifier/tests/terser/compress/properties/lhs_prop_2/output.js
@@ -1,4 +1,6 @@
-[1][0] = 42;
+[
+    1
+][0] = 42;
 "abc".b = "g";
 "def"[2] = "g";
 "ghi"[""] = "g";
diff --git a/crates/swc_ecma_minifier/tests/terser/compress/reduce_vars/duplicate_lambda_defun_name_2/output.js b/crates/swc_ecma_minifier/tests/terser/compress/reduce_vars/duplicate_lambda_defun_name_2/output.js
index 590e51e72d..dac0cc51f9 100644
--- a/crates/swc_ecma_minifier/tests/terser/compress/reduce_vars/duplicate_lambda_defun_name_2/output.js
+++ b/crates/swc_ecma_minifier/tests/terser/compress/reduce_vars/duplicate_lambda_defun_name_2/output.js
@@ -1,5 +1,3 @@
-console.log(
-    (function (a) {
-        return function () {}.length;
-    })()
-);
+console.log(function(a) {
+    return (function() {}).length;
+}());
diff --git a/crates/swc_ecma_minifier/tests/terser/compress/reduce_vars/escape_yield/output.js b/crates/swc_ecma_minifier/tests/terser/compress/reduce_vars/escape_yield/output.js
index fe0bec559e..f11b4f0330 100644
--- a/crates/swc_ecma_minifier/tests/terser/compress/reduce_vars/escape_yield/output.js
+++ b/crates/swc_ecma_minifier/tests/terser/compress/reduce_vars/escape_yield/output.js
@@ -1,8 +1,8 @@
 function foo() {}
-var gen = (function* (s) {
-    for (;;) yield foo;
-})();
-(function () {
+var gen = function*(s) {
+    for(;;)yield foo;
+}();
+(function() {
     var thing = gen.next().value;
     if (thing !== (thing = gen.next().value)) console.log("FAIL");
     else console.log("PASS");
diff --git a/crates/swc_ecma_minifier/tests/terser/compress/reduce_vars/issue_3140_5/output.js b/crates/swc_ecma_minifier/tests/terser/compress/reduce_vars/issue_3140_5/output.js
index 3993861856..11482b2d01 100644
--- a/crates/swc_ecma_minifier/tests/terser/compress/reduce_vars/issue_3140_5/output.js
+++ b/crates/swc_ecma_minifier/tests/terser/compress/reduce_vars/issue_3140_5/output.js
@@ -1,13 +1,12 @@
-var n = 1,
-    c = 0;
-(function (a) {
-    var b = (function () {
+var n = 1, c = 0;
+(function(a) {
+    var b = function() {
         this;
         n-- && h();
-    })();
+    }();
     function h() {
         b && c++;
     }
-    h((b = 1));
+    h(b = 1);
 })();
 console.log(c);