fix(minifier): preserve default parameter side effects (#24791)

Pure-function summaries currently treat functions with identifier parameters as side-effect-free without checking parameter initializers. This lets dead-code elimination remove calls that must evaluate a default value.

For example:

```js
var foo = (a = side_effect()) => {};
foo();
```

Before this change, the call is removed entirely. After this change, it is preserved so `side_effect()` runs.

This skips the summary when an ordinary parameter has a default initializer and adds regression coverage for both arrow functions and function declarations.

Verified with `cargo test -p oxc_minifier`, `just minsize`, `cargo clippy -p oxc_minifier -- -D warnings`, and `just fmt`. No minsize or allocation snapshot changed.

Implemented with AI assistance (OpenAI Codex and Claude). Claude identified the bug during review; Codex implemented and verified the fix. The contributor remains responsible for reviewing and understanding the change.
This commit is contained in:
Dunqing committed 2026-07-22 09:45:58 +00:00
1 parent 334d8848ff
commit 23b904ff09
2 files changed
+16 -2

No files matched your search

@@ -485,8 +485,14 @@ impl<'a> PeepholeOptimizations {
if r#async || generator {
return;
}
// `function foo({}) {} foo(null)` is runtime type error.
if !params.items.iter().all(|pat| pat.pattern.is_binding_identifier()) {
// Destructuring can throw. Default initializers run for missing or `undefined`
// arguments, and function summaries are call-independent, so reject an initializer
// that may have side effects. TDZ-only throws follow the minifier's documented
// `No TDZ Violation` assumption.
if !params.items.iter().all(|param| {
param.pattern.is_binding_identifier()
&& param.initializer.as_ref().is_none_or(|init| !init.may_have_side_effects(ctx))
}) {
return;
}
if body.statements.iter().any(|stmt| stmt.may_have_side_effects(ctx)) {
@@ -278,6 +278,14 @@ fn remove_empty_function() {
test_options("var foo = () => {}; x = foo(a(), b())", "x = (a(), b(), void 0)", &options);
test_options("var foo = function () {}; foo()", "", &options);
test_options("var foo = (a = 0) => {}; foo()", "", &options);
test_options(
"var foo = (a = side_effect()) => {}; foo()",
"((a = side_effect()) => {})()",
&options,
);
test_same_options("function foo(a = side_effect()) {} foo()", &options);
test_same_options("function foo({}) {} foo()", &options);
test_options("var foo = ({}) => {}; foo()", "(({}) => {})()", &options);
test_options("var foo = function ({}) {}; foo()", "(function ({}) {})()", &options);