Repository: swc-project/swc (rust)

Issue:
Combination of `evaluate` and `reduce_vars` causes `undefined` to appear in template literals
### Describe the bug

With both `jsc.minify.compression.evaluate` and `jsc.minify.compression.reduce_vars` enabled, variables that are passed directly to a template literal are replaced with a `undefined` in the compiled code.

* Disabling one of them causes the code to be compiled correctly.
* Doing anything with the variable inside the template literal causes the code to be compiled correctly, just raw variables are miss-compiled.

In the provided input code, the first interpolation is compiled correctly, while the second is not. Removing the `+ 1` part causes the first to also be miss-compiled.

### Input code

```typescript
export const foo = a => `${a + 1}-${a}`;
```


### Config

```json
{
  "jsc": {
    "minify": {
      "compress": {
        "arguments": false,
        "arrows": false,
        "booleans": false,
        "booleans_as_integers": false,
        "collapse_vars": false,
        "comparisons": false,
        "computed_props": false,
        "conditionals": false,
        "dead_code": false,
        "directives": false,
        "drop_console": false,
        "drop_debugger": false,
        "evaluate": true,
        "expression": false,
        "hoist_funs": false,
        "hoist_props": false,
        "hoist_vars": false,
        "if_return": false,
        "join_vars": false,
        "keep_classnames": false,
        "keep_fargs": false,
        "keep_fnames": false,
        "keep_infinity": false,
        "loops": false,
        "negate_iife": false,
        "properties": false,
        "reduce_funcs": false,
        "reduce_vars": true,
        "side_effects": false,
        "switches": false,
        "typeofs": false,
        "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": false
      },
      "mangle": false
    },
    "parser": {
      "syntax": "typescript",
      "tsx": false
    },
    "target": "es2018"
  },
  "minify": false
}
```


### Playground link

https://play.swc.rs/?version=1.2.144&code=H4sIAAAAAAAAA0utKMgvKlFIzs8rLlFIy89XsFVIVLC1U0hQqU5U0FYwrNUFMmoTrAGZLvGuKAAAAA%3D%3D&config=H4sIAAAAAAAAA22TTXKDMAyF78I6i7arTg%2FQa3gcWyZK%2FTeWnMBkeveKkAED3aHPb%2BTnJ%2FHormS6r0cXMKIbpy%2BTQi5ANH3r0tcAkaVw2hOchJR0X8tzSh50PAKlSWFk6KGshyZ5rzOBuukNDVkXpBS3rDJYlUvKLY4WGVOUcoEWtFUmWVgJFjCMN2hE0kdEkcTeFlo4115sLhRu2lfNIuNSp3p45iG3LpJLQmLlauN4Rlu7M9s8Fp0qwLWsva4J41bzAyBevSaKOsCOO5nJHv0jw%2Bhkojwu1KfWWYReXqgQ3ZrG5B0KY9OqgK0GpoeaA5wtzxERWlDgnKS%2B6uiObC5NNx4zJLfWEp9u7p9LtduwF50W4gC%2FxRe3c3nxoPmyZzSGc%2FKHFgH4kuwBSxac9rDINg95T2u0IFmDbQ4qLeXvqQs69svWSS3bTtO%2BPToaI%2Buh%2B3pGQ6Zg5k5yomEVs8wbWCRAH2%2Fvn93U7%2FWvzpI%2FFzDzicMDAAA%3D

### Expected behavior

That variables passed directly to a template literal are not replaced with `undefined`.

### Actual behavior

Any pattern `${<identifier>}` inside a template literal is replaced with `undefined` in the compiled code.

### Version

1.2.144

### Additional context

_No response_

Pull request:
fix(es/minifier): Skip fuction parameter used for template literals w…
…hile evaluate and reduce_vars

close:#3710

<!-- Note: CI script will automatically rebase your PR so please do not rebase unless required -->

<!-- Thanks for submitting a pull request! Please provide enough information so that others can review your pull request. -->

**Description:**

<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->

**BREAKING CHANGE:**

<!--
If this PR introduces a breaking change, it must contain a notice for it to be included in the CHANGELOG. Add description or remove entirely if not breaking.

You may need to update `MIGRATION.md` for the breaking changes.
-->

**Related issue (if exists):**


---


 - Closes #3710

Patch:
diff --git a/crates/swc_ecma_minifier/src/compress/optimize/strings.rs b/crates/swc_ecma_minifier/src/compress/optimize/strings.rs
index 3e262756d4..556d241b3b 100644
--- a/crates/swc_ecma_minifier/src/compress/optimize/strings.rs
+++ b/crates/swc_ecma_minifier/src/compress/optimize/strings.rs
@@ -128,7 +128,7 @@ where
                     .data
                     .as_ref()
                     .and_then(|data| data.vars.get(&i.to_id()))
-                    .map(|v| v.assign_count == 0)
+                    .map(|v| v.assign_count == 0 && !v.declared_as_fn_param)
                     .unwrap_or(false)
                 {
                     self.changed = true;


Test patch:
diff --git a/crates/swc_ecma_minifier/tests/compress/fixture/issues/3710/analysis-snapshot.rust-debug b/crates/swc_ecma_minifier/tests/compress/fixture/issues/3710/analysis-snapshot.rust-debug
new file mode 100644
index 0000000000..9d1825f77c
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/compress/fixture/issues/3710/analysis-snapshot.rust-debug
@@ -0,0 +1,82 @@
+TestSnapshot {
+    vars: [
+        (
+            (
+                Atom('a' type=inline),
+                #2,
+            ),
+            VarUsageInfo {
+                inline_prevented: false,
+                ref_count: 2,
+                cond_init: false,
+                declared: true,
+                declared_count: 1,
+                declared_as_fn_param: true,
+                declared_as_fn_expr: false,
+                assign_count: 0,
+                mutation_by_call_count: 0,
+                usage_count: 2,
+                reassigned_with_assignment: false,
+                reassigned_with_var_decl: false,
+                mutated: false,
+                has_property_access: false,
+                has_property_mutation: false,
+                accessed_props: {},
+                exported: false,
+                used_above_decl: false,
+                is_fn_local: true,
+                used_by_nested_fn: true,
+                executed_multiple_time: false,
+                used_in_cond: false,
+                var_kind: Some(
+                    "const",
+                ),
+                var_initialized: false,
+                declared_as_catch_param: false,
+                no_side_effect_for_member_access: false,
+                used_as_callee: false,
+                used_as_arg: false,
+                infects: [],
+            },
+        ),
+        (
+            (
+                Atom('foo' type=inline),
+                #1,
+            ),
+            VarUsageInfo {
+                inline_prevented: true,
+                ref_count: 0,
+                cond_init: false,
+                declared: true,
+                declared_count: 1,
+                declared_as_fn_param: false,
+                declared_as_fn_expr: false,
+                assign_count: 0,
+                mutation_by_call_count: 0,
+                usage_count: 0,
+                reassigned_with_assignment: false,
+                reassigned_with_var_decl: false,
+                mutated: false,
+                has_property_access: false,
+                has_property_mutation: false,
+                accessed_props: {},
+                exported: false,
+                used_above_decl: false,
+                is_fn_local: true,
+                used_by_nested_fn: false,
+                executed_multiple_time: false,
+                used_in_cond: false,
+                var_kind: Some(
+                    "const",
+                ),
+                var_initialized: true,
+                declared_as_catch_param: false,
+                no_side_effect_for_member_access: true,
+                used_as_callee: false,
+                used_as_arg: false,
+                infects: [],
+            },
+        ),
+    ],
+}
diff --git a/crates/swc_ecma_minifier/tests/compress/fixture/issues/3710/config.json b/crates/swc_ecma_minifier/tests/compress/fixture/issues/3710/config.json
new file mode 100644
index 0000000000..226c7c3835
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/compress/fixture/issues/3710/config.json
@@ -0,0 +1,4 @@
+{
+    "evaluate": true,
+    "reduce_vars": true
+}
diff --git a/crates/swc_ecma_minifier/tests/compress/fixture/issues/3710/input.js b/crates/swc_ecma_minifier/tests/compress/fixture/issues/3710/input.js
new file mode 100644
index 0000000000..7e38c70c27
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/compress/fixture/issues/3710/input.js
@@ -0,0 +1,1 @@
+export const foo = (a) => `${a + 1}-${a}`;
diff --git a/crates/swc_ecma_minifier/tests/compress/fixture/issues/3710/output.js b/crates/swc_ecma_minifier/tests/compress/fixture/issues/3710/output.js
new file mode 100644
index 0000000000..274e2e5e96
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/compress/fixture/issues/3710/output.js
@@ -0,0 +1,2 @@
+export const foo = (a)=>`${a + 1}-${a}`
+;