Fix parameter expression get binding (#10912)
* fix: parameter expression closure should not have access to the declaration inside function body * fix: renameVisitor should skip when a pattern is a scope * address review comments
This commit is contained in:
parent
ee5b79d75d
commit
fc5365fe4a
@ -64,7 +64,8 @@ export const Expression = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Scope = {
|
export const Scope = {
|
||||||
types: ["Scopable"],
|
// When pattern is inside the function params, it is a scope
|
||||||
|
types: ["Scopable", "Pattern"],
|
||||||
checkPath(path) {
|
checkPath(path) {
|
||||||
return t.isScope(path.node, path.parent);
|
return t.isScope(path.node, path.parent);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -897,10 +897,27 @@ export default class Scope {
|
|||||||
|
|
||||||
getBinding(name: string) {
|
getBinding(name: string) {
|
||||||
let scope = this;
|
let scope = this;
|
||||||
|
let previousPath;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
const binding = scope.getOwnBinding(name);
|
const binding = scope.getOwnBinding(name);
|
||||||
if (binding) return binding;
|
if (binding) {
|
||||||
|
// Check if a pattern is a part of parameter expressions.
|
||||||
|
// 9.2.10.28: The closure created by this expression should not have visibility of
|
||||||
|
// declarations in the function body. If the binding is not a `param`-kind,
|
||||||
|
// then it must be defined inside the function body, thus it should be skipped
|
||||||
|
if (
|
||||||
|
previousPath &&
|
||||||
|
previousPath.isPattern() &&
|
||||||
|
previousPath.parentPath.isFunction() &&
|
||||||
|
binding.kind !== "param"
|
||||||
|
) {
|
||||||
|
// do nothing
|
||||||
|
} else {
|
||||||
|
return binding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previousPath = scope.path;
|
||||||
} while ((scope = scope.parent));
|
} while ((scope = scope.parent));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
7
packages/babel-traverse/test/fixtures/rename/parameter-default-1/input.js
vendored
Normal file
7
packages/babel-traverse/test/fixtures/rename/parameter-default-1/input.js
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
let a = "outside";
|
||||||
|
|
||||||
|
function f(g = () => a) {
|
||||||
|
let a = "inside";
|
||||||
|
return g();
|
||||||
|
}
|
||||||
|
|
||||||
3
packages/babel-traverse/test/fixtures/rename/parameter-default-1/options.json
vendored
Normal file
3
packages/babel-traverse/test/fixtures/rename/parameter-default-1/options.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["./plugin"]
|
||||||
|
}
|
||||||
6
packages/babel-traverse/test/fixtures/rename/parameter-default-1/output.js
vendored
Normal file
6
packages/babel-traverse/test/fixtures/rename/parameter-default-1/output.js
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
let a = "outside";
|
||||||
|
|
||||||
|
function f(g = () => a) {
|
||||||
|
let z = "inside";
|
||||||
|
return g();
|
||||||
|
}
|
||||||
9
packages/babel-traverse/test/fixtures/rename/parameter-default-1/plugin.js
vendored
Normal file
9
packages/babel-traverse/test/fixtures/rename/parameter-default-1/plugin.js
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module.exports = function() {
|
||||||
|
return {
|
||||||
|
visitor: {
|
||||||
|
FunctionDeclaration(path) {
|
||||||
|
path.scope.rename("a", "z");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
5
packages/babel-traverse/test/fixtures/rename/parameter-default-10/input.js
vendored
Normal file
5
packages/babel-traverse/test/fixtures/rename/parameter-default-10/input.js
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
let a = "outside";
|
||||||
|
|
||||||
|
function r({ a: b }, { [a]: { c } = a }) {
|
||||||
|
g(a);
|
||||||
|
}
|
||||||
3
packages/babel-traverse/test/fixtures/rename/parameter-default-10/options.json
vendored
Normal file
3
packages/babel-traverse/test/fixtures/rename/parameter-default-10/options.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["./plugin"]
|
||||||
|
}
|
||||||
11
packages/babel-traverse/test/fixtures/rename/parameter-default-10/output.js
vendored
Normal file
11
packages/babel-traverse/test/fixtures/rename/parameter-default-10/output.js
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
let z = "outside";
|
||||||
|
|
||||||
|
function r({
|
||||||
|
a: b
|
||||||
|
}, {
|
||||||
|
[z]: {
|
||||||
|
c
|
||||||
|
} = z
|
||||||
|
}) {
|
||||||
|
g(z);
|
||||||
|
}
|
||||||
9
packages/babel-traverse/test/fixtures/rename/parameter-default-10/plugin.js
vendored
Normal file
9
packages/babel-traverse/test/fixtures/rename/parameter-default-10/plugin.js
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module.exports = function() {
|
||||||
|
return {
|
||||||
|
visitor: {
|
||||||
|
FunctionDeclaration(path) {
|
||||||
|
path.scope.rename("a", "z");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
5
packages/babel-traverse/test/fixtures/rename/parameter-default-2/input.js
vendored
Normal file
5
packages/babel-traverse/test/fixtures/rename/parameter-default-2/input.js
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
let a = "outside";
|
||||||
|
|
||||||
|
function h(a, g = () => a) {
|
||||||
|
return g();
|
||||||
|
}
|
||||||
3
packages/babel-traverse/test/fixtures/rename/parameter-default-2/options.json
vendored
Normal file
3
packages/babel-traverse/test/fixtures/rename/parameter-default-2/options.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["./plugin"]
|
||||||
|
}
|
||||||
5
packages/babel-traverse/test/fixtures/rename/parameter-default-2/output.js
vendored
Normal file
5
packages/babel-traverse/test/fixtures/rename/parameter-default-2/output.js
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
let a = "outside";
|
||||||
|
|
||||||
|
function h(z, g = () => z) {
|
||||||
|
return g();
|
||||||
|
}
|
||||||
9
packages/babel-traverse/test/fixtures/rename/parameter-default-2/plugin.js
vendored
Normal file
9
packages/babel-traverse/test/fixtures/rename/parameter-default-2/plugin.js
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module.exports = function() {
|
||||||
|
return {
|
||||||
|
visitor: {
|
||||||
|
FunctionDeclaration(path) {
|
||||||
|
path.scope.rename("a", "z");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
6
packages/babel-traverse/test/fixtures/rename/parameter-default-3/input.js
vendored
Normal file
6
packages/babel-traverse/test/fixtures/rename/parameter-default-3/input.js
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
let a = "outside";
|
||||||
|
|
||||||
|
function j(g = a) {
|
||||||
|
let a = "inside";
|
||||||
|
return g;
|
||||||
|
}
|
||||||
3
packages/babel-traverse/test/fixtures/rename/parameter-default-3/options.json
vendored
Normal file
3
packages/babel-traverse/test/fixtures/rename/parameter-default-3/options.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["./plugin"]
|
||||||
|
}
|
||||||
6
packages/babel-traverse/test/fixtures/rename/parameter-default-3/output.js
vendored
Normal file
6
packages/babel-traverse/test/fixtures/rename/parameter-default-3/output.js
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
let a = "outside";
|
||||||
|
|
||||||
|
function j(g = a) {
|
||||||
|
let z = "inside";
|
||||||
|
return g;
|
||||||
|
}
|
||||||
9
packages/babel-traverse/test/fixtures/rename/parameter-default-3/plugin.js
vendored
Normal file
9
packages/babel-traverse/test/fixtures/rename/parameter-default-3/plugin.js
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module.exports = function() {
|
||||||
|
return {
|
||||||
|
visitor: {
|
||||||
|
FunctionDeclaration(path) {
|
||||||
|
path.scope.rename("a", "z");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
8
packages/babel-traverse/test/fixtures/rename/parameter-default-4/input.js
vendored
Normal file
8
packages/babel-traverse/test/fixtures/rename/parameter-default-4/input.js
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
let a = "outside";
|
||||||
|
|
||||||
|
function k([{
|
||||||
|
g = a
|
||||||
|
}]) {
|
||||||
|
let a = "inside";
|
||||||
|
return g;
|
||||||
|
}
|
||||||
3
packages/babel-traverse/test/fixtures/rename/parameter-default-4/options.json
vendored
Normal file
3
packages/babel-traverse/test/fixtures/rename/parameter-default-4/options.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["./plugin"]
|
||||||
|
}
|
||||||
8
packages/babel-traverse/test/fixtures/rename/parameter-default-4/output.js
vendored
Normal file
8
packages/babel-traverse/test/fixtures/rename/parameter-default-4/output.js
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
let a = "outside";
|
||||||
|
|
||||||
|
function k([{
|
||||||
|
g = a
|
||||||
|
}]) {
|
||||||
|
let z = "inside";
|
||||||
|
return g;
|
||||||
|
}
|
||||||
9
packages/babel-traverse/test/fixtures/rename/parameter-default-4/plugin.js
vendored
Normal file
9
packages/babel-traverse/test/fixtures/rename/parameter-default-4/plugin.js
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module.exports = function() {
|
||||||
|
return {
|
||||||
|
visitor: {
|
||||||
|
FunctionDeclaration(path) {
|
||||||
|
path.scope.rename("a", "z");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
8
packages/babel-traverse/test/fixtures/rename/parameter-default-5/input.js
vendored
Normal file
8
packages/babel-traverse/test/fixtures/rename/parameter-default-5/input.js
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
let a = "outside";
|
||||||
|
|
||||||
|
function f([{
|
||||||
|
[a]: g
|
||||||
|
}]) {
|
||||||
|
let a = "inside";
|
||||||
|
return g;
|
||||||
|
}
|
||||||
3
packages/babel-traverse/test/fixtures/rename/parameter-default-5/options.json
vendored
Normal file
3
packages/babel-traverse/test/fixtures/rename/parameter-default-5/options.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["./plugin"]
|
||||||
|
}
|
||||||
8
packages/babel-traverse/test/fixtures/rename/parameter-default-5/output.js
vendored
Normal file
8
packages/babel-traverse/test/fixtures/rename/parameter-default-5/output.js
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
let a = "outside";
|
||||||
|
|
||||||
|
function f([{
|
||||||
|
[a]: g
|
||||||
|
}]) {
|
||||||
|
let z = "inside";
|
||||||
|
return g;
|
||||||
|
}
|
||||||
9
packages/babel-traverse/test/fixtures/rename/parameter-default-5/plugin.js
vendored
Normal file
9
packages/babel-traverse/test/fixtures/rename/parameter-default-5/plugin.js
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module.exports = function() {
|
||||||
|
return {
|
||||||
|
visitor: {
|
||||||
|
FunctionDeclaration(path) {
|
||||||
|
path.scope.rename("a", "z");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
5
packages/babel-traverse/test/fixtures/rename/parameter-default-6/input.js
vendored
Normal file
5
packages/babel-traverse/test/fixtures/rename/parameter-default-6/input.js
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
let a = "outside";
|
||||||
|
|
||||||
|
function n(g = (a = a) => {}) {
|
||||||
|
let a = "inside";
|
||||||
|
}
|
||||||
3
packages/babel-traverse/test/fixtures/rename/parameter-default-6/options.json
vendored
Normal file
3
packages/babel-traverse/test/fixtures/rename/parameter-default-6/options.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["./plugin"]
|
||||||
|
}
|
||||||
5
packages/babel-traverse/test/fixtures/rename/parameter-default-6/output.js
vendored
Normal file
5
packages/babel-traverse/test/fixtures/rename/parameter-default-6/output.js
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
let a = "outside";
|
||||||
|
|
||||||
|
function n(g = (a = a) => {}) {
|
||||||
|
let z = "inside";
|
||||||
|
}
|
||||||
9
packages/babel-traverse/test/fixtures/rename/parameter-default-6/plugin.js
vendored
Normal file
9
packages/babel-traverse/test/fixtures/rename/parameter-default-6/plugin.js
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module.exports = function() {
|
||||||
|
return {
|
||||||
|
visitor: {
|
||||||
|
FunctionDeclaration(path) {
|
||||||
|
path.scope.rename("a", "z");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
5
packages/babel-traverse/test/fixtures/rename/parameter-default-7/input.js
vendored
Normal file
5
packages/babel-traverse/test/fixtures/rename/parameter-default-7/input.js
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
let a = "outside";
|
||||||
|
|
||||||
|
function n(a, g = (a = a) => {}) {
|
||||||
|
a = "inside";
|
||||||
|
}
|
||||||
3
packages/babel-traverse/test/fixtures/rename/parameter-default-7/options.json
vendored
Normal file
3
packages/babel-traverse/test/fixtures/rename/parameter-default-7/options.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["./plugin"]
|
||||||
|
}
|
||||||
5
packages/babel-traverse/test/fixtures/rename/parameter-default-7/output.js
vendored
Normal file
5
packages/babel-traverse/test/fixtures/rename/parameter-default-7/output.js
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
let a = "outside";
|
||||||
|
|
||||||
|
function n(z, g = (a = a) => {}) {
|
||||||
|
z = "inside";
|
||||||
|
}
|
||||||
9
packages/babel-traverse/test/fixtures/rename/parameter-default-7/plugin.js
vendored
Normal file
9
packages/babel-traverse/test/fixtures/rename/parameter-default-7/plugin.js
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module.exports = function() {
|
||||||
|
return {
|
||||||
|
visitor: {
|
||||||
|
FunctionDeclaration(path) {
|
||||||
|
path.scope.rename("a", "z");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
5
packages/babel-traverse/test/fixtures/rename/parameter-default-8/input.js
vendored
Normal file
5
packages/babel-traverse/test/fixtures/rename/parameter-default-8/input.js
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
let a = "outside";
|
||||||
|
|
||||||
|
function q(a, g = (b = a) => b) {
|
||||||
|
g(a);
|
||||||
|
}
|
||||||
3
packages/babel-traverse/test/fixtures/rename/parameter-default-8/options.json
vendored
Normal file
3
packages/babel-traverse/test/fixtures/rename/parameter-default-8/options.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["./plugin"]
|
||||||
|
}
|
||||||
5
packages/babel-traverse/test/fixtures/rename/parameter-default-8/output.js
vendored
Normal file
5
packages/babel-traverse/test/fixtures/rename/parameter-default-8/output.js
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
let a = "outside";
|
||||||
|
|
||||||
|
function q(z, g = (b = z) => b) {
|
||||||
|
g(z);
|
||||||
|
}
|
||||||
9
packages/babel-traverse/test/fixtures/rename/parameter-default-8/plugin.js
vendored
Normal file
9
packages/babel-traverse/test/fixtures/rename/parameter-default-8/plugin.js
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module.exports = function() {
|
||||||
|
return {
|
||||||
|
visitor: {
|
||||||
|
FunctionDeclaration(path) {
|
||||||
|
path.scope.rename("a", "z");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
5
packages/babel-traverse/test/fixtures/rename/parameter-default-9/input.js
vendored
Normal file
5
packages/babel-traverse/test/fixtures/rename/parameter-default-9/input.js
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
let a = "outside";
|
||||||
|
|
||||||
|
function r(a, g = (a, b = a) => a) {
|
||||||
|
g(a);
|
||||||
|
}
|
||||||
3
packages/babel-traverse/test/fixtures/rename/parameter-default-9/options.json
vendored
Normal file
3
packages/babel-traverse/test/fixtures/rename/parameter-default-9/options.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["./plugin"]
|
||||||
|
}
|
||||||
5
packages/babel-traverse/test/fixtures/rename/parameter-default-9/output.js
vendored
Normal file
5
packages/babel-traverse/test/fixtures/rename/parameter-default-9/output.js
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
let a = "outside";
|
||||||
|
|
||||||
|
function r(z, g = (a, b = a) => a) {
|
||||||
|
g(z);
|
||||||
|
}
|
||||||
9
packages/babel-traverse/test/fixtures/rename/parameter-default-9/plugin.js
vendored
Normal file
9
packages/babel-traverse/test/fixtures/rename/parameter-default-9/plugin.js
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module.exports = function() {
|
||||||
|
return {
|
||||||
|
visitor: {
|
||||||
|
FunctionDeclaration(path) {
|
||||||
|
path.scope.rename("a", "z");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -73,6 +73,25 @@ describe("scope", () => {
|
|||||||
).toBe("Identifier");
|
).toBe("Identifier");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("function parameter expression", function() {
|
||||||
|
it("should not have visibility of declarations inside function body", () => {
|
||||||
|
expect(
|
||||||
|
getPath(
|
||||||
|
`var a = "outside"; (function foo(b = a) { let a = "inside" })`,
|
||||||
|
)
|
||||||
|
.get("body.1.expression.params.0")
|
||||||
|
.scope.getBinding("a").path.node.init.value,
|
||||||
|
).toBe("outside");
|
||||||
|
});
|
||||||
|
it("should have visibility on parameter bindings", () => {
|
||||||
|
expect(
|
||||||
|
getPath(`var a = "outside"; (function foo(b = a, a = "inside") {})`)
|
||||||
|
.get("body.1.expression.params.0")
|
||||||
|
.scope.getBinding("a").path.node.right.value,
|
||||||
|
).toBe("inside");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("variable declaration", function() {
|
it("variable declaration", function() {
|
||||||
expect(getPath("var foo = null;").scope.getBinding("foo").path.type).toBe(
|
expect(getPath("var foo = null;").scope.getBinding("foo").path.type).toBe(
|
||||||
"VariableDeclarator",
|
"VariableDeclarator",
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import {
|
|||||||
isCatchClause,
|
isCatchClause,
|
||||||
isBlockStatement,
|
isBlockStatement,
|
||||||
isScopable,
|
isScopable,
|
||||||
|
isPattern,
|
||||||
} from "./generated";
|
} from "./generated";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,5 +19,11 @@ export default function isScope(node: Object, parent: Object): boolean {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a Pattern is an immediate descendent of a Function, it must be in the params.
|
||||||
|
// Hence we skipped the parentKey === "params" check
|
||||||
|
if (isPattern(node) && isFunction(parent)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return isScopable(node);
|
return isScopable(node);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user