From 9015fda3c106abda6928cf348797a38f09d4b3e0 Mon Sep 17 00:00:00 2001 From: Daryl Tan <3646725+openorclose@users.noreply.github.com> Date: Sun, 1 Mar 2020 23:26:22 +0800 Subject: [PATCH] Fix scope of function body when var redeclares param (#11158) * Fix scope of function body when var redeclares param * Fix empty var declarations * Apply suggestions --- .../babel-helper-call-delegate/src/index.js | 11 ++++++-- .../src/params.js | 28 ++++++++++++++++++- .../parameters/fn-decl-same-as-param/input.js | 5 ++++ .../fn-decl-same-as-param/output.js | 6 ++++ .../var-no-init-same-as-param/input.js | 6 ++++ .../var-no-init-same-as-param/output.js | 8 ++++++ .../input.js | 5 ++++ .../output.js | 11 ++++++++ .../var-same-as-array-pattern-param/input.js | 5 ++++ .../var-same-as-array-pattern-param/output.js | 9 ++++++ .../var-same-as-object-pattern-param/input.js | 5 ++++ .../output.js | 11 ++++++++ .../var-same-as-param-closure/exec.js | 8 ++++++ .../parameters/var-same-as-param/input.js | 3 ++ .../parameters/var-same-as-param/output.js | 6 ++++ .../input.js | 6 ++++ .../output.js | 11 ++++++++ 17 files changed, 140 insertions(+), 4 deletions(-) create mode 100644 packages/babel-plugin-transform-parameters/test/fixtures/parameters/fn-decl-same-as-param/input.js create mode 100644 packages/babel-plugin-transform-parameters/test/fixtures/parameters/fn-decl-same-as-param/output.js create mode 100644 packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-no-init-same-as-param/input.js create mode 100644 packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-no-init-same-as-param/output.js create mode 100644 packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-no-init-then-var-with-init-same-as-param/input.js create mode 100644 packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-no-init-then-var-with-init-same-as-param/output.js create mode 100644 packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-array-pattern-param/input.js create mode 100644 packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-array-pattern-param/output.js create mode 100644 packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-object-pattern-param/input.js create mode 100644 packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-object-pattern-param/output.js create mode 100644 packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-param-closure/exec.js create mode 100644 packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-param/input.js create mode 100644 packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-param/output.js create mode 100644 packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-with-init-then-var-no-init-same-as-param/input.js create mode 100644 packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-with-init-then-var-no-init-same-as-param/output.js diff --git a/packages/babel-helper-call-delegate/src/index.js b/packages/babel-helper-call-delegate/src/index.js index d4bf041d01..7843584620 100644 --- a/packages/babel-helper-call-delegate/src/index.js +++ b/packages/babel-helper-call-delegate/src/index.js @@ -18,7 +18,11 @@ const visitor = { }, }; -export default function(path: NodePath, scope = path.scope) { +export default function( + path: NodePath, + scope = path.scope, + shouldHoistVariables = true, +) { const { node } = path; const container = t.functionExpression( null, @@ -31,8 +35,9 @@ export default function(path: NodePath, scope = path.scope) { let callee = container; let args = []; - // todo: only hoist if necessary - hoistVariables(path, id => scope.push({ id })); + if (shouldHoistVariables) { + hoistVariables(path, id => scope.push({ id })); + } const state = { foundThis: false, diff --git a/packages/babel-plugin-transform-parameters/src/params.js b/packages/babel-plugin-transform-parameters/src/params.js index 4bf346b5be..fe6d09b6bb 100644 --- a/packages/babel-plugin-transform-parameters/src/params.js +++ b/packages/babel-plugin-transform-parameters/src/params.js @@ -64,6 +64,31 @@ export default function convertFunctionParams(path, loose) { for (let i = 0; i < params.length; i++) { const param = params[i]; + for (const name of Object.keys(param.getBindingIdentifiers())) { + const constantViolations = scope.bindings[name]?.constantViolations; + if (constantViolations) { + for (const redeclarator of constantViolations) { + const node = redeclarator.node; + // If a constant violation is a var or a function declaration, + // we first check to see if it's a var without an init. + // If so, we remove that declarator. + // Otherwise, we have to wrap it in an IIFE. + switch (node.type) { + case "VariableDeclarator": + if (node.init === null) { + redeclarator.remove(); + } else { + state.iife = true; + } + break; + case "FunctionDeclaration": + state.iife = true; + break; + } + } + } + } + const paramIsAssignmentPattern = param.isAssignmentPattern(); if (paramIsAssignmentPattern && (loose || node.kind === "set")) { const left = param.get("left"); @@ -146,7 +171,8 @@ export default function convertFunctionParams(path, loose) { path.ensureBlock(); if (state.iife) { - body.push(callDelegate(path, scope)); + // we don't want to hoist the inner declarations up + body.push(callDelegate(path, scope, false)); path.set("body", t.blockStatement(body)); } else { path.get("body").unshiftContainer("body", body); diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/fn-decl-same-as-param/input.js b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/fn-decl-same-as-param/input.js new file mode 100644 index 0000000000..cf2e4b6322 --- /dev/null +++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/fn-decl-same-as-param/input.js @@ -0,0 +1,5 @@ +function foo(a = 2) { + function a() { + + } +} diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/fn-decl-same-as-param/output.js b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/fn-decl-same-as-param/output.js new file mode 100644 index 0000000000..9eb251b8c7 --- /dev/null +++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/fn-decl-same-as-param/output.js @@ -0,0 +1,6 @@ +function foo() { + var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2; + return function () { + function a() {} + }(); +} diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-no-init-same-as-param/input.js b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-no-init-same-as-param/input.js new file mode 100644 index 0000000000..fae31de560 --- /dev/null +++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-no-init-same-as-param/input.js @@ -0,0 +1,6 @@ +function f(a = 2, b = 3) { + var a, b = 4; + var a; + var b; + return a + b; +} diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-no-init-same-as-param/output.js b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-no-init-same-as-param/output.js new file mode 100644 index 0000000000..8af53459ae --- /dev/null +++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-no-init-same-as-param/output.js @@ -0,0 +1,8 @@ +function f() { + var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2; + var b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3; + return function () { + var b = 4; + return a + b; + }(); +} diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-no-init-then-var-with-init-same-as-param/input.js b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-no-init-then-var-with-init-same-as-param/input.js new file mode 100644 index 0000000000..9838300d2b --- /dev/null +++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-no-init-then-var-with-init-same-as-param/input.js @@ -0,0 +1,5 @@ +function f(a = 2, b = 3) { + var a; + var { a } = { a: 4 }; + return a + b; +} diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-no-init-then-var-with-init-same-as-param/output.js b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-no-init-then-var-with-init-same-as-param/output.js new file mode 100644 index 0000000000..ea3a7ea661 --- /dev/null +++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-no-init-then-var-with-init-same-as-param/output.js @@ -0,0 +1,11 @@ +function f() { + var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2; + var b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3; + return function () { + var _a = { + a: 4 + }, + a = _a.a; + return a + b; + }(); +} diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-array-pattern-param/input.js b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-array-pattern-param/input.js new file mode 100644 index 0000000000..b90b5d3411 --- /dev/null +++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-array-pattern-param/input.js @@ -0,0 +1,5 @@ +function foo({a, b}) { + var a = 3; + var c = 2; + var d = a + b + c; +} diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-array-pattern-param/output.js b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-array-pattern-param/output.js new file mode 100644 index 0000000000..8e720020e6 --- /dev/null +++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-array-pattern-param/output.js @@ -0,0 +1,9 @@ +function foo(_ref) { + var a = _ref.a, + b = _ref.b; + return function () { + var a = 3; + var c = 2; + var d = a + b + c; + }(); +} diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-object-pattern-param/input.js b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-object-pattern-param/input.js new file mode 100644 index 0000000000..54bb95b0a3 --- /dev/null +++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-object-pattern-param/input.js @@ -0,0 +1,5 @@ +function foo([a, b]) { + var a = 3; + var c = 2; + var d = a + b + c; +} diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-object-pattern-param/output.js b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-object-pattern-param/output.js new file mode 100644 index 0000000000..bd8ba98ca3 --- /dev/null +++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-object-pattern-param/output.js @@ -0,0 +1,11 @@ +function foo(_ref) { + var _ref2 = babelHelpers.slicedToArray(_ref, 2), + a = _ref2[0], + b = _ref2[1]; + + return function () { + var a = 3; + var c = 2; + var d = a + b + c; + }(); +} diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-param-closure/exec.js b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-param-closure/exec.js new file mode 100644 index 0000000000..1f35e3dd45 --- /dev/null +++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-param-closure/exec.js @@ -0,0 +1,8 @@ +var x = 1 +function foo(x, y = function () { x = 2 }) { + var x = 3 + y() + expect(x).toBe(3); +} +foo() +expect(x).toBe(1); diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-param/input.js b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-param/input.js new file mode 100644 index 0000000000..2f3ac79e04 --- /dev/null +++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-param/input.js @@ -0,0 +1,3 @@ +function foo(a = 2) { + var a = 1; +} diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-param/output.js b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-param/output.js new file mode 100644 index 0000000000..8e7feda6d5 --- /dev/null +++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-same-as-param/output.js @@ -0,0 +1,6 @@ +function foo() { + var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2; + return function () { + var a = 1; + }(); +} diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-with-init-then-var-no-init-same-as-param/input.js b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-with-init-then-var-no-init-same-as-param/input.js new file mode 100644 index 0000000000..7a0460a5ad --- /dev/null +++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-with-init-then-var-no-init-same-as-param/input.js @@ -0,0 +1,6 @@ +function f(a = 2, b = 3) { + var { a } = { a: 4 }; + var a; + var b; + return a + b; +} diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-with-init-then-var-no-init-same-as-param/output.js b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-with-init-then-var-no-init-same-as-param/output.js new file mode 100644 index 0000000000..ea3a7ea661 --- /dev/null +++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/var-with-init-then-var-no-init-same-as-param/output.js @@ -0,0 +1,11 @@ +function f() { + var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2; + var b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3; + return function () { + var _a = { + a: 4 + }, + a = _a.a; + return a + b; + }(); +}