diff --git a/packages/babel-plugin-transform-block-scoping/src/index.ts b/packages/babel-plugin-transform-block-scoping/src/index.ts index 0892eb01cf29..1294e03213bf 100644 --- a/packages/babel-plugin-transform-block-scoping/src/index.ts +++ b/packages/babel-plugin-transform-block-scoping/src/index.ts @@ -71,14 +71,15 @@ export default declare((api, opts: Options) => { if (headPath && isBlockScoped(headPath.node)) { const names = Object.keys(headPath.getBindingIdentifiers()); + const headScope = headPath.scope; - for (const name of names) { + for (let name of names) { if (bodyScope?.hasOwnBinding(name)) continue; // shadowed - let binding = headPath.scope.getOwnBinding(name); + let binding = headScope.getOwnBinding(name); if (!binding) { - headPath.scope.crawl(); - binding = headPath.scope.getOwnBinding(name); + headScope.crawl(); + binding = headScope.getOwnBinding(name); } const { usages, capturedInClosure, hasConstantViolations } = getUsageInBody(binding, path); @@ -86,7 +87,16 @@ export default declare((api, opts: Options) => { if (capturedInClosure) { markNeedsBodyWrap(); captured.push(name); + } else if (headScope.parent.hasBinding(name)) { + // If the binding is not captured, there is no need + // of adding it to the closure param. However, rename + // it if it shadows an outer binding, because the + // closure will be moved to an outer level. + const newName = headScope.generateUid(name); + headPath.scope.rename(name, newName); + name = newName; } + if (isForStatement && hasConstantViolations) { updatedBindingsUsages.set(name, usages); } diff --git a/packages/babel-plugin-transform-block-scoping/src/loop.ts b/packages/babel-plugin-transform-block-scoping/src/loop.ts index 05c5908e631d..6c0fa83aad28 100644 --- a/packages/babel-plugin-transform-block-scoping/src/loop.ts +++ b/packages/babel-plugin-transform-block-scoping/src/loop.ts @@ -177,16 +177,9 @@ export function wrapLoopBody( const callArgs = []; const closureParams = []; const updater = []; - for (const name of captured) { + for (const [name, updatedUsage] of updatedBindingsUsages) { callArgs.push(t.identifier(name)); - const updatedUsage = updatedBindingsUsages.get(name); - if (!updatedUsage) { - // Not updated, re-use the same name - closureParams.push(t.identifier(name)); - continue; - } - const innerName = loopPath.scope.generateUid(name); closureParams.push(t.identifier(innerName)); updater.push( @@ -194,6 +187,11 @@ export function wrapLoopBody( ); for (const path of updatedUsage) path.replaceWith(t.identifier(innerName)); } + for (const name of captured) { + if (updatedBindingsUsages.has(name)) continue; // already injected + callArgs.push(t.identifier(name)); + closureParams.push(t.identifier(name)); + } const id = loopPath.scope.generateUid("loop"); const fn = t.functionExpression( diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/general/for-variable-update-different-captured/exec.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/for-variable-update-different-captured/exec.js new file mode 100644 index 000000000000..6fd7f2c7156c --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/for-variable-update-different-captured/exec.js @@ -0,0 +1,9 @@ +let res = []; + +for (let i = 1; i < 6; i++) { + let y = i; + res.push((() => y)()); + i++; +} + +expect(res).toEqual([1, 3, 5]); diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/general/for-variable-update-different-captured/input.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/for-variable-update-different-captured/input.js new file mode 100644 index 000000000000..e02bfa7cd33e --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/for-variable-update-different-captured/input.js @@ -0,0 +1,5 @@ +for (let i = 4; i < 6; i++) { + let y = i; + () => y; + i++; +} diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/general/for-variable-update-different-captured/output.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/for-variable-update-different-captured/output.js new file mode 100644 index 000000000000..7edc0e9cbbbb --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/for-variable-update-different-captured/output.js @@ -0,0 +1,11 @@ +var _loop = function (_i) { + var y = _i; + (function () { + return y; + }); + _i++; + i = _i; +}; +for (var i = 4; i < 6; i++) { + _loop(i); +} diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-and-capture/exec.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-and-capture/exec.js new file mode 100644 index 000000000000..aff1bf017d4d --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-and-capture/exec.js @@ -0,0 +1,8 @@ +let res = []; + +let i = 0; +for (let i = 4; i < 6; i++) { + res.push((() => i)()); +} + +expect(res).toEqual([4, 5]); diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-and-capture/input.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-and-capture/input.js new file mode 100644 index 000000000000..cbfba47266f9 --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-and-capture/input.js @@ -0,0 +1,4 @@ +let i; +for (let i = 4; i < 6; i++) { + () => i; +} diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-and-capture/output.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-and-capture/output.js new file mode 100644 index 000000000000..9fe0a401a1b6 --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-and-capture/output.js @@ -0,0 +1,9 @@ +var i; +var _loop = function (i) { + (function () { + return i; + }); +}; +for (var _i = 4; _i < 6; _i++) { + _loop(_i); +} diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-and-update/exec.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-and-update/exec.js new file mode 100644 index 000000000000..fda22ae07862 --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-and-update/exec.js @@ -0,0 +1,10 @@ +let res = []; + +let i = 0; +for (let i = 1; i < 6; i++) { + let y = i; + res.push((() => y)()); + i++; +} + +expect(res).toEqual([1, 3, 5]); diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-and-update/input.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-and-update/input.js new file mode 100644 index 000000000000..965744ecaba4 --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-and-update/input.js @@ -0,0 +1,6 @@ +let i; +for (let i = 4; i < 6; i++) { + let y = i; + () => y; + i++; +} diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-and-update/output.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-and-update/output.js new file mode 100644 index 000000000000..66b3e15eb316 --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-and-update/output.js @@ -0,0 +1,12 @@ +var i; +var _loop = function (_i2) { + var y = _i2; + (function () { + return y; + }); + _i2++; + _i = _i2; +}; +for (var _i = 4; _i < 6; _i++) { + _loop(_i); +} diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-original-sibling-scope/exec.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-original-sibling-scope/exec.js new file mode 100644 index 000000000000..f35516b12388 --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-original-sibling-scope/exec.js @@ -0,0 +1,13 @@ +let res = []; + +for (let i = 1; i < 3; i++) { + let x = i; + res.push((() => x)()); +} + +for (let i = 4; i < 6; i++) { + let y = i; + res.push((() => y)()); +} + +expect(res).toEqual([1, 2, 4, 5]); diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-original-sibling-scope/input.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-original-sibling-scope/input.js new file mode 100644 index 000000000000..a5d938fa8903 --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-original-sibling-scope/input.js @@ -0,0 +1,9 @@ +for (let i = 1; i < 3; i++) { + let x = i; + () => x; +} + +for (let i = 4; i < 6; i++) { + let y = i; + () => y; +} diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-original-sibling-scope/output.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-original-sibling-scope/output.js new file mode 100644 index 000000000000..5ed597a408c8 --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow-original-sibling-scope/output.js @@ -0,0 +1,18 @@ +var _loop = function () { + var x = i; + (function () { + return x; + }); +}; +for (var i = 1; i < 3; i++) { + _loop(); +} +var _loop2 = function () { + var y = _i; + (function () { + return y; + }); +}; +for (var _i = 4; _i < 6; _i++) { + _loop2(); +} diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow/exec.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow/exec.js new file mode 100644 index 000000000000..90731683e080 --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow/exec.js @@ -0,0 +1,9 @@ +let res = []; + +let i = 0; +for (let i = 4; i < 6; i++) { + let y = i; + res.push((() => y)()); +} + +expect(res).toEqual([4, 5]); diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow/input.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow/input.js new file mode 100644 index 000000000000..bbaa49e82075 --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow/input.js @@ -0,0 +1,5 @@ +let i; +for (let i = 4; i < 6; i++) { + let y = i; + () => y; +} diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow/output.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow/output.js new file mode 100644 index 000000000000..2c825317fef6 --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/general/issue-15308-for-variable-shadow/output.js @@ -0,0 +1,10 @@ +var i; +var _loop = function () { + var y = _i; + (function () { + return y; + }); +}; +for (var _i = 4; _i < 6; _i++) { + _loop(); +}