fix(transformer/object-rest-spread): declare temp vars for computed keys#22284
Conversation
Merging this PR will not alter performance
Comparing Footnotes
|
There was a problem hiding this comment.
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
varinjection 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. |
ae81d59 to
5153791
Compare
5153791 to
897da54
Compare
|
I've assigned Dunqing and overlookmotel - not sure who should review - whoever gets to it first I guess 🙂 |
Merge activity
|
…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.
89f33c0 to
06896d1
Compare
06896d1 to
eb02514
Compare
…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.
eb02514 to
410e5af
Compare
…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.
410e5af to
4e880de
Compare
### 🚀 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]>
Given some code like
It would previously be transformed to:
This is a problem because there was no declaration to
_ref3This causes runtime errors.
After this PR, we now emit:
This new code includes
var _ref3, fixing the unresolved reference issue.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
vardeclaration would only be inserted if we were insideExpressionStatementExpressionwhich was not guaranteed.