From 243b685c3d4c19f78420f0d4ebc315c2432be215 Mon Sep 17 00:00:00 2001 From: camc314 <18101008+camc314@users.noreply.github.com> Date: Fri, 28 Aug 2026 14:02:19 +0000 Subject: [PATCH] fix(transformer/object-rest): lower multiple declarators correctly (#26147) ## Summary Fix ES2018 object-rest lowering when one variable declaration contains multiple object-rest declarators. ### Before Given: ```js let { a, ...r } = x, { b, ...s } = y; ``` Oxc produced: ```js let { a } = x, { b } = y, s = babelHelpers.objectWithoutProperties(y, ["b"]), {} = null; ``` The `r` binding was lost. Each replacement was recorded using its original declarator index. Replacing the first declarator expanded it into multiple entries, shifting the second declarator. The subsequent replacement still used the old index and overwrote part of the first expansion. ### After Oxc now produces: ```js let { a } = x, r = babelHelpers.objectWithoutProperties(x, ["a"]), { b } = y, s = babelHelpers.objectWithoutProperties(y, ["b"]); ``` Queued replacements are applied from right to left. Replacing a later declarator cannot change the index of an earlier one, so every recorded index remains valid. Regression coverage includes multiple `let`, `const`, and `var` declarators. --- crates/oxc_transformer/src/es2018/object_rest_spread.rs | 2 +- tasks/transform_conformance/snapshots/oxc.snap.md | 2 +- .../test/fixtures/oxc/multiple-variable-declarators/input.js | 3 +++ .../test/fixtures/oxc/multiple-variable-declarators/output.js | 3 +++ .../test/fixtures/oxc/options.json | 3 +++ 5 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-object-rest-spread/test/fixtures/oxc/multiple-variable-declarators/input.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-object-rest-spread/test/fixtures/oxc/multiple-variable-declarators/output.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-object-rest-spread/test/fixtures/oxc/options.json diff --git a/crates/oxc_transformer/src/es2018/object_rest_spread.rs b/crates/oxc_transformer/src/es2018/object_rest_spread.rs index 96cac6d72e..9248828f8f 100644 --- a/crates/oxc_transformer/src/es2018/object_rest_spread.rs +++ b/crates/oxc_transformer/src/es2018/object_rest_spread.rs @@ -820,7 +820,7 @@ impl<'a> ObjectRestSpread<'a> { new_decls.push((i, decls)); } } - for (i, decls) in new_decls { + for (i, decls) in new_decls.into_iter().rev() { decl.declarations.splice(i..=i, decls); } } diff --git a/tasks/transform_conformance/snapshots/oxc.snap.md b/tasks/transform_conformance/snapshots/oxc.snap.md index 8548ef9387..5938d7c302 100644 --- a/tasks/transform_conformance/snapshots/oxc.snap.md +++ b/tasks/transform_conformance/snapshots/oxc.snap.md @@ -1,6 +1,6 @@ commit: 1eac4481 -Passed: 270/398 +Passed: 271/399 # All Passed: * babel-plugin-transform-class-static-block diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-object-rest-spread/test/fixtures/oxc/multiple-variable-declarators/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-object-rest-spread/test/fixtures/oxc/multiple-variable-declarators/input.js new file mode 100644 index 0000000000..28b71f83ee --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-object-rest-spread/test/fixtures/oxc/multiple-variable-declarators/input.js @@ -0,0 +1,3 @@ +let { a, ...r } = x, { b, ...s } = y; +const { c, ...t } = z, { d, ...u } = w; +var { e, ...v } = p, { f, ...q } = o; diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-object-rest-spread/test/fixtures/oxc/multiple-variable-declarators/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-object-rest-spread/test/fixtures/oxc/multiple-variable-declarators/output.js new file mode 100644 index 0000000000..558590df6c --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-object-rest-spread/test/fixtures/oxc/multiple-variable-declarators/output.js @@ -0,0 +1,3 @@ +let { a } = x, r = babelHelpers.objectWithoutProperties(x, ["a"]), { b } = y, s = babelHelpers.objectWithoutProperties(y, ["b"]); +const { c } = z, t = babelHelpers.objectWithoutProperties(z, ["c"]), { d } = w, u = babelHelpers.objectWithoutProperties(w, ["d"]); +var { e } = p, v = babelHelpers.objectWithoutProperties(p, ["e"]), { f } = o, q = babelHelpers.objectWithoutProperties(o, ["f"]); diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-object-rest-spread/test/fixtures/oxc/options.json b/tasks/transform_conformance/tests/babel-plugin-transform-object-rest-spread/test/fixtures/oxc/options.json new file mode 100644 index 0000000000..85af630bdd --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-object-rest-spread/test/fixtures/oxc/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["transform-object-rest-spread"] +}