Commit Graph

100 Commits

Author SHA1 Message Date
overlookmotel 66744f0eb9 feat(parser)!: rename panicked to fatal_error in ParserReturn (#26382)
`ParserReturn` contains a property `panicked`.

The name `panicked` is misleading. The parser did _not_ panic. When `true`, it means that the parser encountered a fatal error and is unable to parse the source, because it contains an unrecoverable syntax error.

Rename this property to `fatal_error`, to better describe what it means.

This is a breaking change, but one that's trivial to fix in downstream code - the property doesn't change its meaning or behavior, just its name.

All the changes in this PR in other crates are just reacting to this change and making everywhere use the term "fatal error" instead of "panicked", so the codebase is consistent.
2026-09-07 09:41:52 +00:00
sapphi-red 133bb35dee fix(ecmascript): detect side effects of foo().#bar correctly when PropertyReadSideEffects::None (#24769)
Side effects of`foo().#bar` was not detected correctly when PropertyReadSideEffects::None is used.

Repro: https://stackblitz.com/edit/oxc-project-website-l6tidw4j?file=src%2Fmain.js

refs https://github.com/oxc-project/oxc/pull/24707#discussion_r3614105531
2026-07-21 23:33:45 +00:00
Dunqing 79e6d775fb fix(minifier): preserve RegExp feature detection (#24712)
> **PR stack (3 of 3)**
> 1. #24739
> 2. #24742
> 3. **#24712 (this PR)**
>
> Review #24739 and #24742 first. This PR is based on #24742, so GitHub shows only the RegExp behavior change.

Stacked on #24742, which exposes strict engine-target capabilities to side-effect analysis.

RegExp constructor calls used for engine feature detection were removed by DCE when their pattern was valid according to the current Oxc RegExp parser. A pattern can be valid at build time but still throw a `SyntaxError` on the configured runtime, so removing the call changes the result of surrounding `try`/`catch` checks.

RegExp purity now requires both syntactic validity and support from every configured target. Consumers without concrete targets remain conservative, while an explicit target allows the minifier to remove constructors known to be safe for those engines.

Target-independent flag validation and pattern compatibility checks now live in `oxc_regular_expression`; side-effect analysis only maps target support to unsupported flags and patterns. String patterns, modern flags, named groups, property escapes, lookbehind, modifiers, invalid flags, and lone-surrogate strings are covered. A RegExp literal with replacement flags is removable only when the target supports both those flags and the dedicated `ES2015RegExpConstructorCanAlterFlags` compatibility feature. Explicitly targeting `esnext` opts into current features.

## Example

Input:

```js
export function supportsUnicodeRegExp() {
  try {
    new RegExp("\\p{Ll}", "u");
    return true;
  } catch {
    return false;
  }
}
```

Before:

```js
export function supportsUnicodeRegExp() {
  try {
    return true;
  } catch {
    return false;
  }
}
```

After:

```js
export function supportsUnicodeRegExp() {
  try {
    new RegExp("\\p{Ll}", "u");
    return true;
  } catch {
    return false;
  }
}
```

Verified with all `oxc_regular_expression`, `oxc_compat`, `oxc_ecmascript`, and `oxc_minifier` tests; the no-feature/`side_effects`/`constant_evaluation` `oxc_ecmascript` matrix; affected-crate clippy; formatting; and diff checks.

Fixes rolldown/rolldown#10279.

Implemented with AI assistance (OpenAI Codex and Claude). The contributor remains responsible for reviewing, understanding, and submitting these changes under the repository AI usage policy.
2026-07-21 14:15:24 +00:00
sapphi-red e750a8243a fix(ecmascript): fix false negative for may_have_side_effects on dynamic property access (#24709)
``[][`length${foo()}`]`` was treated as side effect free. But `foo()` can have side effects.
2026-07-20 09:13:59 +00:00
overlookmotel f78e09650e test(minifier): simplify ArenaVec builder args to array (#24625)
Pure refactor. Just shorten code.

Replace pattern of creating a `Vec` and pushing to it repeatedly with just passing an array to the AST builder method.

This is shorter and more performant - though perf doesn't matter, because it's just in a test.
2026-07-17 13:49:40 +00:00
overlookmotel b28051fb12 refactor(all): pass [v] for single-value ArenaVec builder args (#24623)
Pure refactor. Just shorten code.

#24621 made AST builder methods take arrays as params.

Replace `ArenaVec::from_value_in(v, alloc)` with `[v]` where the `ArenaVec` is passed to an AST builder method.

```rs
let block = Statement::new_block_statement(
    SPAN,
    ArenaVec::from_value_in(return_stmt, ctx),
    ctx,
);
```

\->

```rs
let block = Statement::new_block_statement(SPAN, [return_stmt], ctx);
```
2026-07-17 13:49:39 +00:00
overlookmotel c1a074cdc8 refactor(all): pass [] for empty ArenaVec args to AST builder methods (#24622)
Pure refactor. Just shorten code.

#24621 made AST builder methods take arrays as params.

Replace `ArenaVec::new_in(...)` arguments written inline at AST builder method call sites with the empty array literal `[]`.

```rs
let block = Statement::new_block_statement(SPAN, ArenaVec::new_in(ctx), ctx);
```

\->

```rs
let block = Statement::new_block_statement(SPAN, [], ctx);
```
2026-07-17 13:49:39 +00:00
Boshen cef9143aed refactor(transformer,minifier,react_compiler): switch JS-only visitors from Visit to VisitJs (#24532)
Migrate the transform-pipeline `Visit` implementors to `VisitJs` (#24499): `oxc_transformer` (3), `oxc_minifier` (4), `oxc_react_compiler` (1), plus the parser regex example and the coverage driver. Part of #24317; linter half in #24530. Behavior is unchanged.

Together with #24530: `oxlint` −129.4 KiB (−0.94%) stripped, `oxc_transform` napi −16.2 KiB (−0.46%).

## Why each visitor can switch

A visitor is safe on `VisitJs` when its hooks cannot observe anything inside the pruned type grammar — either the hooked node kinds are value-space-only, or the AST it walks cannot contain type nodes at that pipeline stage.

| Visitor | File | Why equivalent |
|---|---|---|
| `PrivateInExpressionDetector` | `crates/oxc_transformer/src/decorator/legacy/mod.rs` | Hooks `visit_private_in_expression` (+`visit_decorators`); `#x in y` is runtime-only and cannot occur in type grammar, even inside decorator expressions carrying casts. |
| `BindingMover` | `crates/oxc_transformer/src/es2017/async_to_generator.rs` | Its formal-parameter overrides visit only pattern + initializer (`BindingPattern` carries no type field — annotations are sibling fields it never touches); runs in the exit phase after TS strip. |
| `UsedInJSXBindingsCollector` | `crates/oxc_transformer/src/jsx/refresh.rs` | Runs on un-stripped TSX, but its hooks (JSX-shaped calls, `visit_jsx_opening_element`, declarations/imports) cannot fire inside type grammar; its hand-written `visit_ts_type_annotation` skip-stub is deleted — `VisitJs` prunes that natively. |
| `Finder` | `crates/oxc_react_compiler/src/react_compiler/entrypoint/program.rs` | Matches functions by `scope_id`; real JS function scopes never live in pure type grammar, and `VisitJs` still walks the places one could hide (enum initializers, namespace bodies, cast inner expressions). |
| `KeepVar` | `crates/oxc_minifier/src/keep_var.rs` | Hooks `visit_statement`/`visit_variable_declaration`; statements and `var` declarations don't exist in type space, and namespace/enum bodies (which can contain them) are still walked by `VisitJs`. |
| `DirectEvalFlagCheck` | `crates/oxc_minifier/src/peephole/mod.rs` | Debug-only; hooks `visit_call_expression` only — call expressions cannot occur in valid type grammar. |
| `LiveDirectEvalCollector` | `crates/oxc_minifier/src/peephole/mod.rs` | Same single call-expression hook; type-space-immune regardless of input. |
| `TestVisitor` | `crates/oxc_minifier/tests/ecmascript/prop_name.rs` | Test helper parsing `SourceType::default()` (plain JS); single non-recursing `visit_object_expression` hook. |
| `RegularExpressionVisitor` | `crates/oxc_parser/examples/regular_expression.rs` | Finds `RegExpLiteral` / `new RegExp(...)`; neither node kind can occur in type grammar. |
| `CheckASTNodes` | `tasks/coverage/src/driver.rs` | Its only active override is `visit_reg_exp_literal` (regex print idempotency); `RegExpLiteral` cannot occur in type space. (The commented-out `visit_span` full-AST check would need `Visit` if ever revived.) |

Left on `Visit`: the minifier's incremental-scoping bookkeeping (`DropDiff`, `OverPruneCheck`, `LiveRefCollector`) — dropping `let x: Foo = …` must unmark the type-space `Foo` reference, which the `peephole::remove_unused_declaration` tests verify (they fail under `VisitJs`; confirmed and reverted during this work). `ChildScopeCollector` also stays: TS type nodes (mapped/conditional/function types) carry real `scope_id`s it must collect.

---

🤖 This PR was written with the assistance of Claude Code (Claude Fable 5).
2026-07-15 08:08:18 +00:00
Dunqing e2744150c1 fix(minifier): don't drop global calls that throw despite pure arguments (#23917)
## What

`is_pure_global_method_call` (and the bare-global path) in `CallExpression::may_have_side_effects` treated a call as side-effect-free whenever its arguments were, ignoring that several of these functions **throw** at runtime. Unused calls that throw were therefore dropped as dead code. terser and esbuild both keep them.

Guard the cases oxc can prove:

| Call | Throws | Before | After |
| --- | --- | --- | --- |
| `String.raw()`, `Symbol.keyFor()`, `Symbol.keyFor(42)` | `TypeError` (missing/invalid required arg) | dropped | kept |
| `URL.canParse()` | `TypeError` (required first arg) | dropped | kept |
| `String.fromCodePoint(-1 / 1.5 / 0x110000)` | `RangeError` (code point outside `[0, 0x10FFFF]`) | dropped | kept |
| `Math.abs(10n)`, `Date.UTC(10n)`, `String.fromCharCode(10n)`, `isNaN(10n)`, … | `TypeError` (`ToNumber(BigInt)`) | dropped | kept |

Safe optimizations are preserved — `URL.canParse("x")`, `String.fromCodePoint(65)`, `String.fromCharCode(65)`, `Math.abs(1)` still drop. `String.raw` / `Symbol.keyFor` are removed from the pure-method list (never provably safe); the rest get argument guards.

### A Symbol argument is left droppable, on purpose

A Symbol argument also throws (`ToString` / `ToNumber` on a Symbol), but oxc has no `ValueType::Symbol` and `to_primitive` never resolves a pure expression to `Symbol`, so a Symbol throw is **never provable**. Guarding it would mean keeping every `Math.abs(x)` / `parseInt(x)` on an undetermined argument — a large minification regression. esbuild makes the same trade-off (it drops `Math.abs(Symbol.iterator)` too), so only the provable cases are guarded.

oxc already guards exactly this for the `Number` / `Symbol` / `BigInt` / `Error` constructors; this brings the method-call path in line. The assumption (including the non-provable-Symbol carve-out) is documented in `crates/oxc_minifier/docs/ASSUMPTIONS.md`.

Covered by `test_throwing_global_calls` in `crates/oxc_minifier/tests/ecmascript/may_have_side_effects.rs`; cross-checked against terser and esbuild; `minsize` unchanged.

---

Stacked on #23916.
2026-07-02 02:02:14 +00:00
Dunqing dcc9a73b12 fix(minifier): treat Object.isExtensible/isFrozen/isSealed as pure (#23916)
## What

`Object.isExtensible`, `Object.isFrozen`, and `Object.isSealed` were grouped with the throwing introspection methods (`Object.keys`, `getOwnPropertyDescriptor`, …) in `CallExpression::may_have_side_effects`, so a call with a missing / `null` / `undefined` argument was conservatively kept.

Unlike those methods, the three `is*` methods never `ToObject` their target — per [spec](https://tc39.es/ecma262/#sec-object.isextensible) a non-object receiver returns a primitive (`false` / `true`) instead of throwing, so the call is side-effect-free and an unused one can be dropped.

```js
// in
Object.isExtensible();
Object.isFrozen(x);
Object.isSealed();
Object.keys();

// out
Object.isFrozen(x), Object.keys();
```

`isExtensible()` / `isSealed()` drop (pure); `isFrozen(x)` stays — an undetermined target could be a `Proxy`, whose `[[IsExtensible]]` / `[[OwnPropertyKeys]]` traps are observable — and `keys()` stays (it throws on the `undefined` receiver).

Covered by new cases in `crates/oxc_minifier/tests/ecmascript/may_have_side_effects.rs`; `minsize` unchanged.

Closes #23779
2026-06-29 13:51:39 +00:00
overlookmotel b7cd88dcd6 refactor(minifier): switch to new AstBuilder (#23832)
Part of #23043. Migrate `oxc_minifier` over to new `AstBuilder`.
2026-06-26 18:17:30 +00:00
overlookmotel 3855f0c0d1 perf(minifier): allocate AST nodes in arena directly (#23710)
Same as #23709.

Small perf optimizations around AST builder calls.

- AST nodes which end up in `Box`es, allocate into the `Box` as early as possible, to increase chance compiler sees the type can be built directly in arena, rather than built on the stack, and then copied from stack into arena.
- Functions return `Box<T>` rather than `T` where the value needs to be boxed anyway. If function is not inlined, this avoids stack allocation + copy.

Also, shorten code where possible by using more specific `AstBuilder` methods.
2026-06-23 01:00:15 +00:00
Dunqing 31bfd9bc75 fix(minifier): keep Object introspection calls on a possible Proxy (#23483)
## Summary

Closes #17157.

`Object.keys`, `getOwnPropertyDescriptor(s)`, `getOwnPropertyNames`, `getOwnPropertySymbols`, `getPrototypeOf`, `hasOwn`, `isExtensible`, `isFrozen`, and `isSealed` introspect their target through internal methods a `Proxy` can trap (`[[OwnPropertyKeys]]`, `[[GetOwnProperty]]`, …), so they have observable side effects when the target is a Proxy. They were treated as unconditionally pure and dropped when unused — so `Object.getOwnPropertyDescriptor(proxy, "foo")` / `Object.keys(proxy)` were silently removed, skipping the trap.

They are now treated as side-effect-free only when the target is provably not a Proxy (a determined, non-null value type — i.e. a literal), unless the bundler opts out of property-read side effects. A `null`/`undefined`/missing target is also kept (it throws a `TypeError`).

- `Object.create(proto)` is pure only with an object/`null` prototype and no `properties` argument (read via `[[OwnPropertyKeys]]`/`[[Get]]`).
- `Object.values`/`entries` are no longer treated as pure at all — they invoke `[[Get]]` and run getters even on a plain object.
- `Object.is` stays unconditionally pure.

## Credit

Adapted from #21056 by @gthb. Their `proxy_sensitive_arg_index` approach is preserved; this version adds corrections for the getter-invoking methods (`values`/`entries`, and `create` with properties) and the throw-on-`null`/`undefined` cases that the original treated as droppable.

Verified by `cargo test -p oxc_minifier` / `-p oxc_ecmascript`; `minsize` unchanged.

cloese https://github.com/oxc-project/oxc/pull/21056
2026-06-17 08:07:58 +00:00
Boshen 777f02ae10 refactor(diagnostics)!: return a Diagnostics type instead of Vec<OxcDiagnostic> (#23217)
## Summary

Introduces `oxc_diagnostics::Diagnostics` — a `Vec<OxcDiagnostic>` newtype that knows the difference between errors and warnings — and makes it the standard diagnostics return value across oxc.

- **New type**: `Diagnostics` with `has_errors()`, `has_warnings()`, `errors()`, `warnings()`, plus `Vec`-like ergonomics (Deref, `IntoIterator`, `Extend`, `From`/`Into<Vec>`) so most call sites are unchanged.
- **Return types**: `ParserReturn`, `SemanticBuilderReturn`, `IsolatedDeclarationsReturn`, the React Compiler `TransformResult`/`LintResult`, and `TransformerReturn` now expose `diagnostics: Diagnostics` instead of `errors: Vec<OxcDiagnostic>` (the React Compiler's separate `errors`/`warnings` are merged into one severity-tagged list).
- **Behaviour**: a React Compiler **error** now stops the transform (no output), while **warnings** flow through and the transform continues. `compile()` aborts codegen only when `diagnostics.has_errors()` — warnings alone no longer abort (previously any diagnostic did).

## Breaking change

Consumers reading `*.errors` on these return values must read `*.diagnostics`. React Compiler consumers that read `.errors`/`.warnings` separately should use `diagnostics.has_errors()` / `diagnostics.errors()` / `diagnostics.warnings()`.

Parser/semantic/isolated-declarations fatality is otherwise unchanged. Builds on the React Compiler transform feature (#23201).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-10 14:46:52 +00:00
Dunqing d7a359a0cc fix(ecmascript): treat update expressions as unconditionally side-effectful (#21456)
## Summary

`obj.prop++` performs an implicit `GetValue` + `ToNumeric` + `PutValue`. The `ToNumeric` coercion alone can invoke `valueOf`/`Symbol.toPrimitive` on the old value, and the read/write may trigger getters, setters, or Proxy traps.

Previously `UpdateExpression::may_have_side_effects` only checked `property_write_side_effects()` and treated the update as free when disabled — letting the minifier drop `counter.value++` even when `counter` is externally observable.

## Fix

Return `true` unconditionally for `UpdateExpression`. Terser, esbuild, Rollup, and SWC all hardcode `++`/`--` as unconditionally side-effectful — this matches their behavior, and mirrors how compound assignments like `a.b += 1` are already handled one impl above (`AssignmentExpression`).

## Impact

Under rolldown's tree-shake config (`propertyWriteSideEffects: false`), these are now correctly preserved instead of being dropped:

```js
counter.value++;
(class { static { ++counter.count; } });
class A { [counter.another++] = 123; }
```

## Reported

rolldown/rolldown#9094 ([comment](https://github.com/rolldown/rolldown/pull/9094#discussion_r3083594834))
2026-04-15 14:12:57 +00:00
sapphi-red 2338e28f59 fix(ecmascript): treat this as potentially having side effects (#21297)
fixes https://github.com/oxc-project/oxc/issues/21296 by treating `this` as potentially having side effects.
We can reduce the case that is bailed out by checking whether `this` is written inside `constructor` or not in the future.
2026-04-11 14:25:36 +00:00
overlookmotel c9cd809e50 refactor(str)!: rename Atom to Str (#20835)
Pure refactor.

Rename `Atom` to `Str`. The name "Atom" is a leftover from when we used to intern strings (years ago), and is misleading now.

Just call it `Str`. This follows the naming convention we have for arena-allocated versions of other types e.g. `Box`, `Vec`, `HashMap` - which we name the same as the native equivalents but with a lifetime (`Box<'a>` not `ArenaBox<'a>`, so `Str<'a>` not `ArenaStr<'a>`).

Rename all variables that contained "atom", and methods which relate to `Str`s to match the new type name.

Rename `as_atom` methods to `as_arena_str`. The longer name is required here to disambiguate from `as_str` methods which return a `&str`.

Some variables called "atom" still exist in `oxc_regular_expression` crate - that's a different meaning of "atom" - the meaning defined in regexp spec.

The diff of this PR is large but it's 100% renaming. No substantive or unrelated changes whatsoever.
2026-03-30 02:40:33 +00:00
Boshen 0dc620d4bf chore(deps): upgrade javascript-globals to 1.5.0 (#20707)
## Summary

- Upgrade `javascript-globals` from 1.4.1 to 1.5.0
- Use direct exports (`GLOBALS_BUILTIN`, `GLOBALS_ES2026`) instead of indexing into `GLOBALS["builtin"]` / `GLOBALS["es2026"]`, avoiding repeated hashmap lookups

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-03-24 15:52:27 +00:00
Dunqing 4ae3f3fbc9 fix(ecmascript): apply coercion-is-pure assumption to constructor side-effect detection (#20420)
## Summary

Address review feedback from #20383 and #20395. Apply the "Coercion Methods Are Pure" assumption (`ASSUMPTIONS.md`) — `.toString()`/`.valueOf()`/`[Symbol.toPrimitive]()` are assumed side-effect-free — while still checking for spec-mandated throws that are NOT covered by that assumption.

### Changes

**`new String/Number(arg)`** — `ToString(Symbol)` / `ToNumber(Symbol)` throws TypeError. Check via `new_expr_first_arg_may_be_symbol`. BigInt is fine for both (`ToString(BigInt)` works, Number converts BigInt→Number).

**`new Date/ArrayBuffer(arg)`** — `ToNumber(Symbol)` AND `ToNumber(BigInt)` throw TypeError. Check via `new_expr_first_arg_may_be_symbol_or_bigint`.

**Error constructors** — `new Error(Symbol())` / `Error(Symbol())` throw (`ToString(Symbol)`). Removed from `is_unconditionally_pure_constructor` and `is_pure_callable_constructor`. Added `is_error_constructor()` helper with Symbol check.

**TypedArray constructors** — use `value_type()` instead of `to_primitive()`: object args call `@@iterator` (NOT a coercion method), BigInt args cause `ToNumber` to throw. Only known-safe primitive value types (Number/String/Boolean/Null/Undefined) accepted.

**`String(x)` call** — unconditionally pure. `String()` as a function has special Symbol handling per spec (`String(Symbol())` → `"Symbol()"` without throwing). Added to `is_pure_callable_constructor`.

**`Number(x)` / `Symbol(x)` calls** — keep special handling (`ToNumeric(Symbol)` / `ToString(Symbol)` throw).

**`is_pure_collection_constructor`** — now verifies `undefined` is actually global (not shadowed). Removed invalid issue link (`XXXX`).

**Also fixes:** typo `"BitUint64Array"` → `"BigUint64Array"` in `is_known_global_constructor`.

### Cross-tool comparison

| Expression | ES Spec | esbuild | Terser | Rollup | SWC | Oxc (this PR) |
|---|---|---|---|---|---|---|
| `new String(x)` | impure (Symbol) | impure | impure | pure | impure | **impure** (Symbol check) |
| `new Number(x)` | impure (Symbol) | impure | impure | pure | impure | **impure** (Symbol check) |
| `new Date(x)` | impure (Symbol/BigInt) | impure | impure | pure | impure | **impure** (Symbol+BigInt) |
| `new Date(0n)` | impure (BigInt) | impure | impure | pure | impure | **impure** (BigInt check) |
| `new ArrayBuffer(x)` | impure (Symbol/BigInt) | impure | impure | pure | impure | **impure** (Symbol+BigInt) |
| `new Error(x)` | impure (Symbol) | impure | impure | pure | impure | **impure** (Symbol check) |
| `new Uint8Array(x)` | impure (@@iterator) | impure | impure | pure | impure | **impure** (value_type) |
| `new Uint8Array({})` | impure (@@iterator) | impure | impure | pure | impure | **impure** (value_type) |
| `String(x)` call | safe (Symbol handled) | impure | impure | pure | impure | **pure** (coercion assumption) |
| `Error(x)` call | impure (Symbol) | impure | impure | pure | impure | **impure** (Symbol check) |

Oxc sits between esbuild (most conservative) and Rollup (most aggressive): uses the coercion assumption to skip ToPrimitive checks on objects, but catches spec-mandated throws (`ToString(Symbol)`, `ToNumber(Symbol)`, `ToNumber(BigInt)`, `@@iterator` on objects).

## Test plan

- [x] All 473 `oxc_minifier` tests pass (39 ignored)
- [x] All 13 `oxc_ecmascript` tests pass
- [x] Updated test expectations with new cases (`new Date(0n)`, `new ArrayBuffer(0n)`, `new Error(x)`, `Error(x)`)
- [x] Cross-tool research verified against ES Spec, esbuild, Terser, Rollup, SWC
2026-03-17 15:44:46 +00:00
Dunqing 878eace78e refactor(ecmascript): consolidate side-effect known globals into separate module (#20401)
## Summary

- Extract all global lookup tables (~430 lines) into a new `known_globals.rs` module, keeping `expressions.rs` focused on `MayHaveSideEffects` trait implementations
- Extract `is_pure_math_method` to deduplicate 36 Math method names shared between read-safety (`is_known_global_property`) and call-safety (`is_pure_global_method_call`) checks
- Extract inline call-safe match into a named `is_pure_global_method_call` function with clear doc comments
- Rename `is_pure_call` → `is_pure_callable_constructor`, removing 4 dead entries (`String`, `Number`, `BigInt`, `Symbol`) already handled by earlier special-case logic
- Redefine `is_unconditionally_pure_constructor` in terms of `is_pure_callable_constructor` to keep error-type lists in sync
- **Fix**: use `is_typed_array_constructor` for the TypedArray `.of()` arm, adding previously missing `BigInt64Array` and `BigUint64Array`

## Test plan

- [x] Added tests for `BigInt64Array.of()` and `BigUint64Array.of()`
- [x] All existing `side_effects` tests pass
- [x] `cargo clippy` clean

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-03-17 15:44:46 +00:00
Dunqing efeba2802e fix(ecmascript): add argument validation for NewExpression side-effect detection (#20395)
## Summary

`is_pure_constructor` treated many global constructors as unconditionally side-effect-free regardless of arguments. Cross-tool research (ES spec, esbuild, Terser, Rollup, SWC) confirmed this was the most permissive of all bundlers/minifiers and introduced real bugs.

### Problem

These constructors perform observable operations on object arguments but were treated as pure:

| Constructor | Side effect | ES Spec operation |
|---|---|---|
| `new String(obj)` | Calls `toString()`/`valueOf()` | `ToString` -> `ToPrimitive` |
| `new Number(obj)` | Calls `valueOf()`/`toString()`, throws on Symbol | `ToNumeric` -> `ToPrimitive` |
| `new Date(obj)` | Calls `[Symbol.toPrimitive]`/`valueOf()`/`toString()` | `ToPrimitive` |
| `new TypedArray(obj)` | Calls `[Symbol.iterator]` or `.length` getter | `GetMethod(@@iterator)` |
| `new ArrayBuffer(obj)` | Calls `valueOf()`/`toString()` | `ToIndex` -> `ToNumber` |

### Cross-tool comparison

| Constructor | esbuild | Terser | Rollup | SWC | Oxc (before) |
|---|---|---|---|---|---|
| `new String(obj)` | impure | impure (`unsafe` only) | pure | pure | **pure** |
| `new Number(obj)` | impure | impure (`unsafe` only) | pure | pure | **pure** |
| `new Date(obj)` | impure | impure (`unsafe` only) | pure | impure | **pure** |
| `new TypedArray(obj)` | impure | impure | pure | impure | **pure** |

### Solution

- Split `is_pure_constructor` into `is_unconditionally_pure_constructor` (Object, Boolean, Error types) and `is_typed_array_constructor`
- Add per-constructor argument validation in `NewExpression::may_have_side_effects` using the same `ToPrimitive`-based pattern already used by `CallExpression`
- Three shared helpers: `new_expr_with_to_string_check`, `new_expr_with_to_number_check`, `new_expr_with_to_primitive_check`

### Also fixes

- Typo `"BitUint64Array"` -> `"BigUint64Array"` in `is_known_global_constructor`
- `is_pure_collection_constructor` now verifies `undefined` is actually global (not shadowed)

## Test plan

- Added 52 new test cases covering primitive args (safe), unknown/object args (side-effectful), and edge cases (`{toString(){}}`, `{valueOf(){}}`)
- All existing tests continue to pass
- Verified no regressions in Rolldown (1639/1639 tests pass with this change patched in)
2026-03-17 14:53:24 +00:00
Dunqing edb8677e26 fix(ecmascript): treat collection constructor with variable arg as side-effectful (#20383)
## Summary

`new Map(x)`, `new Set(x)`, `new WeakMap(x)`, `new WeakSet(x)` were incorrectly considered side-effect-free when the argument is a variable reference. These constructors iterate their argument via `Symbol.iterator`, which can execute arbitrary user code (custom iterators, proxies, etc.).

**Before:** `is_pure_constructor` unconditionally included `Map`/`Set`/`WeakMap`/`WeakSet`, so `new Map(variable)` was treated as pure.

**After:** Collection constructors are only pure with provably safe arguments:
- No arguments: `new Set()`
- `null` or `undefined`: `new Set(null)`
- Array literals: `new Set([1, 2, 3])`, `new Map([[k, v]])`
- Variable references or other expressions: **NOT pure**

This matches the behavior of **esbuild** and **Rollup**:
- esbuild: only marks pure with no args, null, undefined, or array literals ([test cases](https://github.com/evanw/esbuild/blob/main/internal/js_parser/js_parser_test.go))
- Rollup: uses `PURE_WITH_ARRAY` — only array literals are trusted ([knownGlobals.ts](https://github.com/rollup/rollup/blob/master/src/ast/nodes/shared/knownGlobals.ts))

**Found via:** Rolldown's `tree-shake-iterable` test failing after delegating `NewExpression` side-effect analysis to Oxc.

## Test plan

- [x] Added tests for all cases: null, undefined, array literals, variable refs, Map with non-array entries
- [x] All existing `oxc_minifier` tests pass (473 passed)
- [x] Verified against Rolldown's `tree-shake-iterable` and `manual-pure-functions` test suites

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-03-16 08:49:52 +00:00
Boshen e62524d618 fix(minifier): treat object spread of getters as having side effects (#20380)
## Summary

- Object spread (`...{ get prop() {} }`) invokes getters at runtime, so
it has side effects
- The previous code only checked key/value AST nodes and missed the
getter invocation
- Setters are **not** invoked by spread, so they remain side-effect-free
- Fixes the Rolldown test `rollup@function@object-spread-side-effect`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-03-14 23:04:28 +08:00
Boshen 5c97b14bbc fix(minifier): recognize object spread of object literals as side-effect-free (#20299)
## Summary

- Add `ObjectExpression` handling in `ObjectPropertyKind::SpreadProperty`'s `may_have_side_effects` — spreading an object literal is side-effect-free when all its properties are
- Fix `remove_unused_object_expr` to check side effects for all-spread objects instead of unconditionally bailing

Closes rolldown/rolldown#8582
2026-03-13 04:56:43 +00:00
Dunqing ade14d4510 fix(ecmascript): enhance side-effect detection for classes, TypedArrays, computed members, and spread (#20213)
## Summary

Part of https://github.com/oxc-project/oxc/issues/19673

Several side-effect detection improvements:

- **Computed member expressions**: When `property_read_side_effects` is `None`, non-literal keys (e.g. `obj[identRef]`) now check the key expression and object for their own side effects instead of always returning `true`
- **ObjectExpression spread**: When `property_read_side_effects` is `None`, `{...expr}` delegates to the argument's own side effects
- **Class MethodDefinition**: Now checks for parameter decorators (TypeScript `@foo` on params)
- **Class AccessorProperty**: Now checks accessor property values for side effects
- **TypedArray constructors**: Added `Int8Array`, `Uint8Array`, `Uint8ClampedArray`, `Int16Array`, `Uint16Array`, `Int32Array`, `Uint32Array`, `Float32Array`, `Float64Array`, `BigInt64Array`, `BigUint64Array` to pure constructors (`DataView` excluded as it requires an `ArrayBuffer` argument)

## Test plan

- [x] Added tests for computed member non-literal keys with `property_read_side_effects`
- [x] Added tests for ObjectExpression spread with `property_read_side_effects`
- [x] Added tests for class method parameter decorators
- [x] Added tests for class accessor property values
- [x] Added tests for TypedArray constructors
- [x] All existing tests pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-03-12 09:22:57 +00:00
Dunqing e7163b6720 feat(ecmascript): add known-globals to side-effect-free property reads (#20212)
## Summary

close: #13067

Part of https://github.com/oxc-project/oxc/issues/19673

Depends on #20217.

Port known-globals lists from Rolldown to Oxc's side-effect detection:

- **Global identifiers** (Math, Array, console, DOM classes, etc.) are now considered side-effect-free to access via `is_known_global_identifier`, matching Rolldown's `GLOBAL_IDENT` set
- **Property reads** like `Math.PI`, `console.log`, `Object.keys` are considered side-effect-free via `is_known_global_property`
- **3-level chains** like `Object.prototype.hasOwnProperty` are supported via `is_known_global_property_deep`

## Test plan

- [x] Added tests for known global identifiers (Math, Array, Object, console, etc.)
- [x] Added tests for known global property reads (Math.PI, Object.keys, etc.)
- [x] Added tests for 3-level chains (Object.prototype.hasOwnProperty, etc.)
- [x] Existing minifier test expectations updated for improved side-effect detection
- [x] All existing tests pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-03-12 09:22:57 +00:00
Dunqing 139ab685f9 feat(ecmascript): add property_write_side_effects to MayHaveSideEffectsContext (#20217)
Part of https://github.com/oxc-project/oxc/issues/19673

## Summary

- Add `property_write_side_effects()` to `MayHaveSideEffectsContext` trait with default `true` (conservative)
- Implement `MayHaveSideEffects` for `AssignmentExpression` and `UpdateExpression` (previously fell through to `_ => true`)
- When `property_write_side_effects` is `false`, member expression writes are side-effect-free
- Compound assignments (`+=`) and updates (`++`) still respect `property_read_side_effects` for their implicit reads
- Reference: https://rolldown.rs/reference/InputOptions.treeshake#propertywritesideeffects

## Test plan

- [x] Added `test_property_write_side_effects_support` covering all combinations of write/read side-effect settings
- [x] Existing tests pass unchanged
2026-03-12 07:25:05 +00:00
Boshen a35063ea7b fix(minifier): preserve side effects for meta property url reads (#19668)
## Summary
- remove the `MetaProperty.url` side-effect-free special-case from property read analysis
- treat `import.meta.url` reads as side-effectful in side-effect tests
- add regression coverage for both `import.meta.url` and `new.target.url` getter side effects

Closes #19466
Closes #19467

## Testing
- `just fmt`
- `cargo test -p oxc_minifier test_property_access`
- `cargo test -p oxc_minifier test_esm_minification_edge_cases`
- `cargo test -p oxc_minifier test_meta_property_url_getter_side_effects`

## AI Disclosure
This PR was prepared with AI assistance (OpenAI Codex) and reviewed by the contributor.
2026-02-24 08:30:56 +00:00
Boshen 4538098d4b refactor(ecmascript): align side-effect analysis with spec coercions (#19308)
## Summary
- align side-effect detection for conversion-heavy global calls with spec behavior
- add explicit handling for `PrivateInExpression` throw behavior on non-object RHS
- treat `import.meta.url` property reads as side-effect free
- expand minifier may-have-side-effects tests for String/Number/Boolean/BigInt/Symbol coercion cases

## Testing
- cargo test -p oxc_minifier may_have_side_effects -- --nocapture
- cargo test -p oxc_ecmascript

## AI Usage
- Drafted with AI assistance (Codex) and reviewed/tested by the author.
2026-02-12 15:25:46 +00:00
copilot-swe-agent 110c300bfc fix(oxc_ecmascript): +[false] and +[true] should evaluate to NaN (#19174)
## Fix minifier incorrectly handling `+[false]` and `+[true]`

- [x] Explore repository structure and locate the bug
- [x] Identify the root cause in `to_number` implementation for arrays
- [x] Add test case for the bug in minifier fold_constants tests
- [x] Add test case in to_number unit tests
- [x] Fix the bug in `to_number.rs` to properly convert arrays to strings first
- [x] Run tests to verify the fix - all tests passing
- [x] Manual verification with minifier example - outputs NaN correctly
- [x] Run code review - no issues found
- [x] Run security check - no vulnerabilities found
- [x] Comprehensive testing of all array conversion cases - all correct
- [x] Apply cargo fmt to fix formatting
- [x] Address PR feedback: hoist use statements, simplify comments and code
- [x] Address bot feedback: optimize count, remove redundant import, add test coverage

**Summary:**
Fixed a bug where `+[false]` and `+[true]` were incorrectly minified to `0` and `1` instead of `NaN`. The issue was in the `to_number` implementation for arrays in `oxc_ecmascript`, which was bypassing the proper ECMAScript conversion chain (array → string → number). Now correctly converts arrays to strings first via `to_js_string`, then converts the string to a number.

<!-- START COPILOT ORIGINAL PROMPT -->

<details>

<summary>Original prompt</summary>

>
> ----
>
> *This section details on the original issue you should resolve*
>
> <issue_title>minifier: `+[false]` and `+[true]` are incorrectly minified</issue_title>
> <issue_description><!--
> ⚠️ IMPORTANT ⚠️
> Please use the appropriate template for each!
>
> - If you are reporting a bug for Oxlint or the Oxc VS Code extension:
>   - https://github.com/oxc-project/oxc/issues/new?template=linter_bug_report.yaml
> - If you are reporting a bug for Oxfmt, especially difference with Prettier:
>   - https://github.com/oxc-project/oxc/issues/new?template=formatter_diff_report.yaml
>
> Other than that, feel free to enter whatever information you like.
>
> Please include as much detail as possible, such as version information.
> Also, if you have steps to reproduce the issue, that would be very helpful.
> -->
> I was messing around with [jsfuck](https://jsfuck.com/) and found out that oxc_minifier doesn't minify `+[false]` or `+[true]` correctly. It is supposed to evalutate to `NaN`, but Oxc just grabs the boolean and uses that. This makes the 'minified' jsfuck code invalid (not that I'm using jsfuck on a day to day basis)
>
> Original:
> ```ts
> const a = +[false]
> const b = +[true]
> ```
>
> Oxc minified:
> ```ts
> const a = 0
> const b = 1;
> ```
>
> Expected:
> ```ts
> const a = NaN
> const b = NaN;
> ```
>
> [Playground](https://playground.oxc.rs/?code=const+a+%3D+%2B%5Bfalse%5D%0Aconst+b+%3D+%2B%5Btrue%5D) (enable minify syntax)
>
> </issue_description>
>
> <agent_instructions>probably there a bug in the constant evaluator in oxc_ecmascript that handles `UnaryExpression`</agent_instructions>
>
> ## Comments on the Issue (you are @copilot in this section)
>
> <comments>
> </comments>
>

</details>

<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes oxc-project/oxc#19084

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
2026-02-09 12:39:22 +00:00
Boshen 38e4b535f7 fix(minifier): validate RegExp patterns before marking as pure (#18125)
## Summary

Invalid RegExp patterns like `RegExp("[")` or invalid flags like `RegExp("a", "xyz")` throw `SyntaxError` at runtime, but the minifier was incorrectly removing them.

This change uses `oxc_regular_expression` to validate patterns at compile time:
- Valid patterns are marked as pure and can be removed when unused
- Invalid patterns or non-literal arguments are kept (preserving runtime errors)

### Behavior

| Input | Valid? | Removed? |
|-------|--------|----------|
| `RegExp()` | ✅ | ✅ |
| `RegExp('a', 'g')` | ✅ | ✅ |
| `RegExp(/foo/)` | ✅ | ✅ |
| `RegExp('[')` | ❌ | ❌ (throws SyntaxError) |
| `RegExp('a', 'xyz')` | ❌ | ❌ (throws SyntaxError) |
| `RegExp(variable)` | ? | ❌ (can't determine) |

Fixes #18050

## Test plan

- [x] Updated existing tests to reflect correct behavior
- [x] Added tests for invalid patterns and flags
- [x] `cargo test -p oxc_minifier` passes
- [x] `cargo test -p oxc_ecmascript` passes

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-01-18 12:24:39 +08:00
camc314 1044116c75 fix(ecmascript): mark new Symbol as non side-effect free (#17568)
![Screenshot 2026-01-01 at 22.03.21.png](https://app.graphite.com/user-attachments/assets/bf89d00f-28c7-4625-aad4-51a843de47ab.png)

`new Symbol` throws an error
2026-01-04 09:40:37 +00:00
sapphi-red c90f0535c2 feat(minfier): support . separated values for compress.treeshake.manualPureFunctions (#16529)
close #16100
2025-12-05 09:58:02 +00:00
sapphi-red f97283b5df feat(ecmascript): support more cases for IsLiteralValue with include_functions (#13425)
Improved IsLiteralValue with `include_functions`.
2025-08-30 11:16:40 +00:00
sapphi-red 95d43113e3 feat(ecmascript): check side effects inside static blocks (#13404)
Now that we have `MayHaveSideEffects` for Statements, we can check the static block.
2025-08-30 04:15:21 +00:00
sapphi-red c557854fe3 feat(ecmascript): support MayHaveSideEffects for Statements (#13402)
Added MayHaveSideEffects for Statements.
2025-08-30 04:15:20 +00:00
sapphi-red ac2067b69a feat(ecmascript): examine and improve is_literal_value (#13363)
Rewrote `IsLiteralValue` completely to ensure it satisfies the condition written in the description. Also added a bunch of tests for it.
2025-08-29 02:55:34 +00:00
sapphi-red e4598661b1 refactor(ecmascript)!: remove PropertyReadSideEffects::OnlyMemberPropertyAccess (#13348)
In #10129, I introduced `PropertyReadSideEffects`. In that, I added `PropertyReadSideEffects::OnlyMemberPropertyAccess`. This was because I thought this was the default behavior of Rollup at that time.

But after re-reading the Rollup's code and checking the behavior in REPL while reviewing https://github.com/rolldown/rolldown/pull/5945, I noticed that I was wrong. Since this was added solely for Rolldown to add support for `treeshake.propertyReadSideEffects` and it's not useful for that usecase, I removed the value.

cc @IWANABETHATGUY
2025-08-28 12:15:33 +00:00
sapphi-red 5d898e803b fix(ecmascript): treat shadowed global calls correctly (#13292)
`var Object; Object.create()` was treated as side effect free, but this should be treated as side effectful.
2025-08-25 14:52:51 +00:00
sapphi-red 8e34656140 feat(ecmascript): implement MayHaveSideEffects for AssignmentTarget (#13279)
Implemented `MayHaveSideEffects` for `AssignmentTarget` to use it by #13280.
2025-08-24 08:42:53 +00:00
Boshen fbe6663518 feat(minifier): mark more known global methods as side-effect free (#13086)
closes #13067
2025-08-16 03:42:22 +00:00
sapphi-red 36386e49b8 feat(ecmascript): treat [...arguments] as side effect free (#13116)
Treat `[...arguments]` as side effect free.
2025-08-16 02:46:41 +00:00
Boshen fe4589b09f feat(minifier): mark more global constructors as side-effect free (#13082) 2025-08-14 16:54:27 +08:00
Copilot 54d17508fd feat(ecmascript): handle typeof guarded global access as side effect free (#12981) 2025-08-12 09:28:57 +08:00
Boshen 8a5c9b971e refactor(minifier,ecmascript): clean up is_global_reference (#12953) 2025-08-10 15:16:53 +08:00
Boshen 0c5bffc052 refactor(ecmascript): change IsGlobalReference to GlobalContext (#12952) 2025-08-10 06:57:13 +00:00
Boshen fe9c8e14b1 fix(minifier): do not remove non-plain empty functions (#12573)
Do not remove empty function calls if the function is async or generator or args can cause type error.

From test262:

```js
function fn({}) {}

assert.throws(TypeError, function() {
  fn(null);
});
```
2025-07-28 12:54:43 +00:00
Boshen 5a46641e94 refactor(ecmascript): move get_constant_value_for_reference_id to IsGlobalReference trait (#11810) 2025-06-19 13:30:39 +00:00
Boshen 7a05e711d5 feat(minifier)!: add Treeshake options (#11786)
closes #9527
2025-06-18 06:42:03 +00:00
overlookmotel f3eaefbf1b feat(ast)!: add value field to BigIntLiteral (#11564)
Add `value` field to `BigIntLiteral`, same as the other literal types.

Once upon a time, we stored the value as a `BigInt` which normalized the representation (removing `_`s, and converting e.g. `0xFF` to `255`). But we had to remove it because `BigInt` owns a heap allocation, and this caused a memory leak.

https://github.com/oxc-project/oxc/commit/50fe0fef73027f10740e68a65a78216933b5d8a0

This PR brings back the `value` field, but avoids memory leak by storing the normalized value as a string (`Atom`).

This has effects in various places in the codebase:

* Brings our JS-side AST into line with ESTree (fixing the test cases broken by #11563).
* Fixes edge cases in a couple of lint rules.
* Codegen now prints bigints in normalized form e.g. `0xFFn` is printed as `255n`.
* Changes behavior of 1 function in `oxc_ecmascript`.

I've made comments below on the parts I'm not so sure about. Review would be very welcome!
2025-06-09 02:28:46 +00:00