Skip to content

fix(transformer/object-rest-spread): declare temp vars for computed keys#22284

Merged
graphite-app[bot] merged 1 commit into
mainfrom
codex/fix-object-rest-computed-temps
May 11, 2026
Merged

fix(transformer/object-rest-spread): declare temp vars for computed keys#22284
graphite-app[bot] merged 1 commit into
mainfrom
codex/fix-object-rest-computed-temps

Conversation

@camc314

@camc314 camc314 commented May 9, 2026

Copy link
Copy Markdown
Contributor

Given some code like

const {
  [({ ...rest }) => {
    let { ...b } = {};
  }]: a,
  [({ ...d } = {})]: c,
} = {};

export {}; // necessary so we are in strict mode

It would previously be transformed to:

const { [(_ref) => {
	let rest = _extends({}, (_objectDestructuringEmpty(_ref), _ref));
	let _ref2 = {}, b = _extends({}, (_objectDestructuringEmpty(_ref2), _ref2));
}]: a, [(_ref3 = {}, {} = _ref3, d = _extends({}, (_objectDestructuringEmpty(_ref3), _ref3)), _ref3)]: c } = {};
export {};

This is a problem because there was no declaration to _ref3

This causes runtime errors.

After this PR, we now emit:

This new code includes var _ref3, fixing the unresolved reference issue.

var _ref3;
const {
  [_ref => {
    let rest = babelHelpers.extends({}, (babelHelpers.objectDestructuringEmpty(_ref), _ref));
    let _ref2 = {},
      b = babelHelpers.extends({}, (babelHelpers.objectDestructuringEmpty(_ref2), _ref2));
  }]: a,
  [(_ref3 = {}, {} = _ref3, d = babelHelpers.extends({}, (babelHelpers.objectDestructuringEmpty(_ref3), _ref3)), _ref3)]: c
} = {};

This aligns with Babel.


This has been fixed by walking up to find the containing statement, and inserting the var declaration before it.

Previously, the var declaration would only be inserted if we were inside ExpressionStatementExpression which was not guaranteed.

Copilot AI review requested due to automatic review settings May 9, 2026 22:34
@github-actions github-actions Bot added the A-transformer Area - Transformer / Transpiler label May 9, 2026
@camc314 camc314 changed the title fix(transformer): declare object rest temps for computed keys fix(transformer/object-rest-spread): declare temp vars for computed keys May 9, 2026
@camc314 camc314 self-assigned this May 9, 2026
@camc314 camc314 marked this pull request as draft May 9, 2026 22:35
@codspeed-hq

codspeed-hq Bot commented May 9, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 44 untouched benchmarks
⏩ 7 skipped benchmarks1


Comparing codex/fix-object-rest-computed-temps (eb02514) with main (a7c3e22)

Open in CodSpeed

Footnotes

  1. 7 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes the ES2018 object rest/spread transformer so that temporary var declarations introduced during destructuring-assignment-with-rest transformations are injected before the containing statement, including when the assignment occurs in computed keys (e.g. computed binding keys and computed class method names).

Changes:

  • Refactors temp var injection to locate the address of the statement containing the transformed assignment (instead of only handling expression statements).
  • Adds new conformance fixtures covering nested object-rest destructuring inside computed keys (binding + class method).
  • Updates conformance snapshots and removes now-unneeded Babel override outputs for previously failing cases.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
crates/oxc_transformer/src/es2018/object_rest_spread.rs Adds helper to inject temp var declarations before the containing statement for more AST contexts (computed keys).
tasks/transform_conformance/tests/babel-plugin-transform-object-rest-spread/test/fixtures/object-rest/nested-computed-class-method/input.js New fixture input covering computed class method name with nested destructuring assignment/rest.
tasks/transform_conformance/tests/babel-plugin-transform-object-rest-spread/test/fixtures/object-rest/nested-computed-class-method/output.js New expected output asserting temp declaration placement for the computed class method case.
tasks/transform_conformance/tests/babel-plugin-transform-object-rest-spread/test/fixtures/object-rest/nested-computed-assignment/input.js New fixture input covering computed binding key with nested destructuring assignment/rest.
tasks/transform_conformance/tests/babel-plugin-transform-object-rest-spread/test/fixtures/object-rest/nested-computed-assignment/output.js New expected output asserting temp declaration placement for the computed binding key case.
tasks/transform_conformance/snapshots/oxc.snap.md Snapshot update reflecting new totals/passes.
tasks/transform_conformance/snapshots/babel.snap.md Snapshot update reflecting reduced mismatches for object-rest-spread.
tasks/transform_conformance/overrides/babel-plugin-transform-object-rest-spread/test/fixtures/object-rest/nested-default-value/output.js Removes override output now that the case matches expected behavior.
tasks/transform_conformance/overrides/babel-plugin-transform-object-rest-spread/test/fixtures/object-rest/nested-computed-key/output.js Removes override output now that the case matches expected behavior.

@camc314 camc314 force-pushed the codex/fix-object-rest-computed-temps branch from ae81d59 to 5153791 Compare May 10, 2026 11:04
@camc314 camc314 removed their assignment May 10, 2026
@camc314 camc314 force-pushed the codex/fix-object-rest-computed-temps branch from 5153791 to 897da54 Compare May 10, 2026 11:19
@camc314 camc314 requested a review from Copilot May 10, 2026 11:23
@camc314 camc314 marked this pull request as ready for review May 10, 2026 11:24
@camc314

camc314 commented May 10, 2026

Copy link
Copy Markdown
Contributor Author

I've assigned Dunqing and overlookmotel - not sure who should review - whoever gets to it first I guess 🙂

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 11 changed files in this pull request and generated no new comments.

@Dunqing Dunqing left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work!

@Dunqing Dunqing added the 0-merge Merge with Graphite Merge Queue label May 11, 2026

Dunqing commented May 11, 2026

Copy link
Copy Markdown
Member

Merge activity

  • May 11, 8:49 AM UTC: The merge label '0-merge' was detected. This PR will be added to the Graphite merge queue once it meets the requirements.
  • May 11, 8:49 AM UTC: Dunqing added this pull request to the Graphite merge queue.
  • May 11, 8:58 AM UTC: The Graphite merge queue couldn't merge this PR because it was not satisfying all requirements (Failed CI: 'Conformance').
  • May 11, 9:04 AM UTC: The merge label '0-merge' was detected. This PR will be added to the Graphite merge queue once it meets the requirements.
  • May 11, 9:07 AM UTC: camc314 added this pull request to the Graphite merge queue.
  • May 11, 9:18 AM UTC: Merged by the Graphite merge queue.

graphite-app Bot pushed a commit that referenced this pull request May 11, 2026
…eys (#22284)

Given some code like
```js
const {
  [({ ...rest }) => {
    let { ...b } = {};
  }]: a,
  [({ ...d } = {})]: c,
} = {};

export {}; // necessary so we are in strict mode
```
It would previously be transformed to:
```js
const { [(_ref) => {
	let rest = _extends({}, (_objectDestructuringEmpty(_ref), _ref));
	let _ref2 = {}, b = _extends({}, (_objectDestructuringEmpty(_ref2), _ref2));
}]: a, [(_ref3 = {}, {} = _ref3, d = _extends({}, (_objectDestructuringEmpty(_ref3), _ref3)), _ref3)]: c } = {};
export {};
```

This is a problem because there was no declaration to `_ref3`

This causes runtime errors.

After this PR, we now emit:

This new code includes `var _ref3`, fixing the unresolved reference issue.

```js
var _ref3;
const {
  [_ref => {
    let rest = babelHelpers.extends({}, (babelHelpers.objectDestructuringEmpty(_ref), _ref));
    let _ref2 = {},
      b = babelHelpers.extends({}, (babelHelpers.objectDestructuringEmpty(_ref2), _ref2));
  }]: a,
  [(_ref3 = {}, {} = _ref3, d = babelHelpers.extends({}, (babelHelpers.objectDestructuringEmpty(_ref3), _ref3)), _ref3)]: c
} = {};

```

This aligns with Babel.

----

This has been fixed by walking up to find the containing statement, and inserting the var declaration before it.

Previously, the `var` declaration would only be inserted if we were inside `ExpressionStatementExpression` which was not guaranteed.
@graphite-app graphite-app Bot force-pushed the codex/fix-object-rest-computed-temps branch from 89f33c0 to 06896d1 Compare May 11, 2026 08:53
@graphite-app graphite-app Bot removed the 0-merge Merge with Graphite Merge Queue label May 11, 2026
@camc314 camc314 force-pushed the codex/fix-object-rest-computed-temps branch from 06896d1 to eb02514 Compare May 11, 2026 09:04
@camc314 camc314 added the 0-merge Merge with Graphite Merge Queue label May 11, 2026
graphite-app Bot pushed a commit that referenced this pull request May 11, 2026
…eys (#22284)

Given some code like
```js
const {
  [({ ...rest }) => {
    let { ...b } = {};
  }]: a,
  [({ ...d } = {})]: c,
} = {};

export {}; // necessary so we are in strict mode
```
It would previously be transformed to:
```js
const { [(_ref) => {
	let rest = _extends({}, (_objectDestructuringEmpty(_ref), _ref));
	let _ref2 = {}, b = _extends({}, (_objectDestructuringEmpty(_ref2), _ref2));
}]: a, [(_ref3 = {}, {} = _ref3, d = _extends({}, (_objectDestructuringEmpty(_ref3), _ref3)), _ref3)]: c } = {};
export {};
```

This is a problem because there was no declaration to `_ref3`

This causes runtime errors.

After this PR, we now emit:

This new code includes `var _ref3`, fixing the unresolved reference issue.

```js
var _ref3;
const {
  [_ref => {
    let rest = babelHelpers.extends({}, (babelHelpers.objectDestructuringEmpty(_ref), _ref));
    let _ref2 = {},
      b = babelHelpers.extends({}, (babelHelpers.objectDestructuringEmpty(_ref2), _ref2));
  }]: a,
  [(_ref3 = {}, {} = _ref3, d = babelHelpers.extends({}, (babelHelpers.objectDestructuringEmpty(_ref3), _ref3)), _ref3)]: c
} = {};

```

This aligns with Babel.

----

This has been fixed by walking up to find the containing statement, and inserting the var declaration before it.

Previously, the `var` declaration would only be inserted if we were inside `ExpressionStatementExpression` which was not guaranteed.
@graphite-app graphite-app Bot force-pushed the codex/fix-object-rest-computed-temps branch from eb02514 to 410e5af Compare May 11, 2026 09:10
…eys (#22284)

Given some code like
```js
const {
  [({ ...rest }) => {
    let { ...b } = {};
  }]: a,
  [({ ...d } = {})]: c,
} = {};

export {}; // necessary so we are in strict mode
```
It would previously be transformed to:
```js
const { [(_ref) => {
	let rest = _extends({}, (_objectDestructuringEmpty(_ref), _ref));
	let _ref2 = {}, b = _extends({}, (_objectDestructuringEmpty(_ref2), _ref2));
}]: a, [(_ref3 = {}, {} = _ref3, d = _extends({}, (_objectDestructuringEmpty(_ref3), _ref3)), _ref3)]: c } = {};
export {};
```

This is a problem because there was no declaration to `_ref3`

This causes runtime errors.

After this PR, we now emit:

This new code includes `var _ref3`, fixing the unresolved reference issue.

```js
var _ref3;
const {
  [_ref => {
    let rest = babelHelpers.extends({}, (babelHelpers.objectDestructuringEmpty(_ref), _ref));
    let _ref2 = {},
      b = babelHelpers.extends({}, (babelHelpers.objectDestructuringEmpty(_ref2), _ref2));
  }]: a,
  [(_ref3 = {}, {} = _ref3, d = babelHelpers.extends({}, (babelHelpers.objectDestructuringEmpty(_ref3), _ref3)), _ref3)]: c
} = {};

```

This aligns with Babel.

----

This has been fixed by walking up to find the containing statement, and inserting the var declaration before it.

Previously, the `var` declaration would only be inserted if we were inside `ExpressionStatementExpression` which was not guaranteed.
@graphite-app graphite-app Bot force-pushed the codex/fix-object-rest-computed-temps branch from 410e5af to 4e880de Compare May 11, 2026 09:14
@graphite-app graphite-app Bot merged commit 4e880de into main May 11, 2026
28 checks passed
@graphite-app graphite-app Bot removed the 0-merge Merge with Graphite Merge Queue label May 11, 2026
@graphite-app graphite-app Bot deleted the codex/fix-object-rest-computed-temps branch May 11, 2026 09:18
camc314 added a commit that referenced this pull request May 11, 2026
### 🚀 Features

- 66c9b01 transformer/typescript: Debug_assert that `enum_eval` ran in
semantic (#22252) (Dunqing)
- ffe6475 minifier: Fold `Array` constructor with safe spreads (#22215)
(camc314)

### 🐛 Bug Fixes

- d3d0b18 traverse: Handle `ChainElement::TSNonNullExpression` in
`GatherNodeParts` (#22247) (leaysgur)
- 4e880de transformer/object-rest-spread: Declare temp vars for computed
keys (#22284) (camc314)
- a7c3e22 semantic: Clear member write target for computed keys (#22302)
(camc314)
- 6a8852d codegen: Emit newline after legal-comment orphan flush
(#22304) (Dunqing)
- 5da9fda transformer/explicit-resource-management: Preserve class names
(#22306) (Dunqing)
- b5d970f transformer/explicit-resource-management: Preserve class names
(#22290) (camc314)
- bc54fd4 minifier: Keep function / class names if direct eval is
present in the scope (#22241) (sapphi-red)
- 7a810c0 minifier: Refresh direct eval flags after DCE (#21787)
(Dunqing)
- dd88726 transformer/legacy-decorator: Preserve accessor type
annotation for emitDecoratorMetadata (#21966) (Dunqing)
- 29a3cd7 codegen: Swap mapping/indent order for top-level decls
(#22206) (Dunqing)
- 73b4f40 minifier: Preserve catch binding with direct eval (#22221)
(camc314)
- 0e13d17 minifier: Preserve optional chain base side effects (#22219)
(camc314)
- 0c7c01c transformer/typescript: Inline optional-chain enum member
access (#21834) (Dunqing)
- a6aff7e codegen: Emit block/array/object end mapping at close char
(#22200) (Dunqing)
- a099b03 codegen: Emit call end mapping at `)` position, not past it
(#22199) (Dunqing)
- 5753774 minifier: Cap if-return ternary collapse for firefox (#21841)
(Gurupungav Narayanan)
- 2493bdd codegen: Correct sourcemap end mappings for closing delimiters
(#22001) (Mark Dalgleish)
- 3b385e2 minifier: Bail optimizing `Array` with unknown arg count
(#22188) (camc314)
- 9fa2122 parser: Parse array computed class keys (#22159) (camc314)

### 📚 Documentation

- a4a6892 napi/parser: Correct code comment (#22278) (overlookmotel)
- 9305373 oxc: Update README (#22178) (camc314)

Co-authored-by: Cameron <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-transformer Area - Transformer / Transpiler

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants