Commit 679f6a9
refactor(treeshake): migrate SideEffectDetector to Oxc's MayHaveSideEffects trait (#8624)
## Summary
Closes: #7918
- Delegate expression/statement side-effect analysis to Oxc's
`MayHaveSideEffects` trait instead of reimplementing it
- `SideEffectDetector` now implements `MayHaveSideEffectsContext` and
`GlobalContext` directly, keeping only bundler-specific logic
- Remove `utils.rs` (593 lines) and `global_reference.rs` (828 lines) —
logic now lives in Oxc
- Remove `BundlerSideEffectCtx` bridge type, merging its fields and
trait impls into `SideEffectDetector`
- Remove `phf` dependency from `rolldown_utils`
- Fix pre-existing bugs:
- Destructuring assignment targets (`[] = rhs`, `{} = rhs`) were
incorrectly treated as side-effect-free — now delegated to Oxc which
correctly identifies `GetIterator`/`RequireObjectCoercible` as
observable
- Compound assignment operators (`+=`, `-=`, `||=`, etc.) were missing a
side-effect check — now delegated to Oxc which correctly treats them as
always side-effectful
Net: **-1555 lines**
### Why some checks still can't be delegated to Oxc
Oxc's `MayHaveSideEffects` is a pure JS semantics analysis — it has no
concept of bundler-level concerns. The remaining Rolldown-specific
overrides are:
| Override | Why Oxc can't handle it |
|---|---|
| **`SideEffectDetail` metadata flags** (`GlobalVarAccess`,
`PureAnnotation`, `PureCjs`) | These are bundler metadata bits
propagated through the tree for downstream decisions (e.g. cross-module
optimization, CJS interop). Oxc returns a plain `bool`. |
| **`import.meta.*` is side-effect-free** | Oxc treats `import.meta` as
a `MetaProperty` with no special semantics. The bundler knows
`import.meta.url` etc. are safe reads because it controls their
resolution. |
| **CJS `exports.foo = ...` pattern** | Oxc sees a property write on an
unresolved global (`exports`) — side-effectful. The bundler knows this
is a CJS export assignment and marks it `PureCjs`. Must be checked
**before** delegating to Oxc. |
| **Cross-module pure call tracking**
(`side_effect_free_call_expr_addr`) | The bundler's link stage
identifies calls to functions proven pure across modules. Oxc only sees
a single file at a time. |
| **Module declarations** (`import`/`export`) | Oxc would treat these as
side-effectful statements. The bundler knows imports/re-exports are
declarative and handles them in the link stage. |
| **`using` declarations** | Rolldown has custom logic for `using`
declarators (only `null`, `undefined`, or `void expr` initializers are
side-effect-free). This is stricter than generic analysis because
`using` invokes `Symbol.dispose`. |
| **Variable declarations with destructuring** | Rolldown has custom
rules: object destructuring depends on `property_read_side_effects`,
array destructuring checks for nested patterns. These interact with
bundler options that Oxc doesn't model at the statement level. |
| **Pure-annotated call args** | For `/*@__PURE__*/` calls, Rolldown
must re-check args through its own detector (not Oxc's) because
bundler-specific overrides (e.g. `import.meta.*`) apply to args too. |
## Test plan
- [x] All existing side-effect detection tests pass (33/33)
Related: oxc-project/oxc#19673
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>1 parent 557d377 commit 679f6a9
File tree
6 files changed
+360
-2197
lines changed- crates
- rolldown_utils
- src
- rolldown/src/ast_scanner/side_effect_detector
6 files changed
+360
-2197
lines changedSome generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
0 commit comments