Migrate some duplicate binding tests to traverse (#9532)
* Migrate try-catch duplicate error * Remove exception for functions and let in the same scope * Migrate duplicate bindings tests to traverse * Add test for subscope and let/const * Add more test cases
This commit is contained in:
parent
21eb0837e8
commit
b32d271fee
@ -1,5 +0,0 @@
|
|||||||
try {
|
|
||||||
throw 0;
|
|
||||||
} catch (e) {
|
|
||||||
let e = new TypeError('Duplicate variable declaration; will throw an error.');
|
|
||||||
}
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"plugins": ["../../../../lib"],
|
|
||||||
"throws": "Duplicate declaration \"e\""
|
|
||||||
}
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
const MULTIPLIER = 5;
|
|
||||||
|
|
||||||
class MULTIPLIER {
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"throws": "Duplicate declaration \"MULTIPLIER\""
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
const MULTIPLIER = 5;
|
|
||||||
|
|
||||||
var MULTIPLIER = "overwrite";
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"throws": "Duplicate declaration \"MULTIPLIER\""
|
|
||||||
}
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
const MULTIPLIER = 5;
|
|
||||||
|
|
||||||
function MULTIPLIER() {
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"throws": "Duplicate declaration \"MULTIPLIER\""
|
|
||||||
}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
try {} catch (a) { let a }
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"throws": "Duplicate declaration \"a\""
|
|
||||||
}
|
|
||||||
@ -349,9 +349,6 @@ export default class Scope {
|
|||||||
// class expression
|
// class expression
|
||||||
if (local.kind === "local") return;
|
if (local.kind === "local") return;
|
||||||
|
|
||||||
// ignore hoisted functions if there's also a local let
|
|
||||||
if (kind === "hoisted" && local.kind === "let") return;
|
|
||||||
|
|
||||||
const duplicate =
|
const duplicate =
|
||||||
// don't allow duplicate bindings to exist alongside
|
// don't allow duplicate bindings to exist alongside
|
||||||
kind === "let" ||
|
kind === "let" ||
|
||||||
|
|||||||
25
packages/babel-traverse/test/__snapshots__/scope.js.snap
Normal file
25
packages/babel-traverse/test/__snapshots__/scope.js.snap
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`scope duplicate bindings catch const 1`] = `"Duplicate declaration \\"e\\""`;
|
||||||
|
|
||||||
|
exports[`scope duplicate bindings catch let 1`] = `"Duplicate declaration \\"e\\""`;
|
||||||
|
|
||||||
|
exports[`scope duplicate bindings global class/function 1`] = `"Duplicate declaration \\"foo\\""`;
|
||||||
|
|
||||||
|
exports[`scope duplicate bindings global const/class 1`] = `"Duplicate declaration \\"foo\\""`;
|
||||||
|
|
||||||
|
exports[`scope duplicate bindings global const/const 1`] = `"Duplicate declaration \\"foo\\""`;
|
||||||
|
|
||||||
|
exports[`scope duplicate bindings global const/function 1`] = `"Duplicate declaration \\"foo\\""`;
|
||||||
|
|
||||||
|
exports[`scope duplicate bindings global const/let 1`] = `"Duplicate declaration \\"foo\\""`;
|
||||||
|
|
||||||
|
exports[`scope duplicate bindings global const/var 1`] = `"Duplicate declaration \\"foo\\""`;
|
||||||
|
|
||||||
|
exports[`scope duplicate bindings global let/class 1`] = `"Duplicate declaration \\"foo\\""`;
|
||||||
|
|
||||||
|
exports[`scope duplicate bindings global let/function 1`] = `"Duplicate declaration \\"foo\\""`;
|
||||||
|
|
||||||
|
exports[`scope duplicate bindings global let/let 1`] = `"Duplicate declaration \\"foo\\""`;
|
||||||
|
|
||||||
|
exports[`scope duplicate bindings global let/var 1`] = `"Duplicate declaration \\"foo\\""`;
|
||||||
@ -1,8 +1,10 @@
|
|||||||
import traverse from "../lib";
|
import traverse, { NodePath } from "../lib";
|
||||||
import { parse } from "@babel/parser";
|
import { parse } from "@babel/parser";
|
||||||
|
import * as t from "@babel/types";
|
||||||
|
|
||||||
function getPath(code, options) {
|
function getPath(code, options) {
|
||||||
const ast = parse(code, options);
|
const ast =
|
||||||
|
typeof code === "string" ? parse(code, options) : createNode(code);
|
||||||
let path;
|
let path;
|
||||||
traverse(ast, {
|
traverse(ast, {
|
||||||
Program: function(_path) {
|
Program: function(_path) {
|
||||||
@ -26,8 +28,27 @@ function getIdentifierPath(code) {
|
|||||||
return nodePath;
|
return nodePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("scope", function() {
|
function createNode(node) {
|
||||||
describe("binding paths", function() {
|
const ast = t.file(t.program(Array.isArray(node) ? node : [node]));
|
||||||
|
|
||||||
|
// This puts the path into the cache internally
|
||||||
|
// We afterwards traverse ast, as we need to start traversing
|
||||||
|
// at the File node and not the Program node
|
||||||
|
NodePath.get({
|
||||||
|
hub: {
|
||||||
|
buildError: (_, msg) => new Error(msg),
|
||||||
|
},
|
||||||
|
parentPath: null,
|
||||||
|
parent: ast,
|
||||||
|
container: ast,
|
||||||
|
key: "program",
|
||||||
|
}).setContext();
|
||||||
|
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("scope", () => {
|
||||||
|
describe("binding paths", () => {
|
||||||
it("function declaration id", function() {
|
it("function declaration id", function() {
|
||||||
expect(
|
expect(
|
||||||
getPath("function foo() {}").scope.getBinding("foo").path.type,
|
getPath("function foo() {}").scope.getBinding("foo").path.type,
|
||||||
@ -250,4 +271,136 @@ describe("scope", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("duplicate bindings", () => {
|
||||||
|
/*
|
||||||
|
* These tests do not use the parser as the parser has
|
||||||
|
* its own scope tracking and we want to test the scope tracking
|
||||||
|
* of traverse here and see if it handles duplicate bindings correctly
|
||||||
|
*/
|
||||||
|
describe("catch", () => {
|
||||||
|
// try {} catch (e) { let e; }
|
||||||
|
const createTryCatch = function(kind) {
|
||||||
|
return t.tryStatement(
|
||||||
|
t.blockStatement([]),
|
||||||
|
t.catchClause(
|
||||||
|
t.identifier("e"),
|
||||||
|
t.blockStatement([
|
||||||
|
t.variableDeclaration(kind, [
|
||||||
|
t.variableDeclarator(t.identifier("e"), t.stringLiteral("1")),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
["let", "const"].forEach(name => {
|
||||||
|
it(name, () => {
|
||||||
|
const ast = createTryCatch(name);
|
||||||
|
|
||||||
|
expect(() => getPath(ast)).toThrowErrorMatchingSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("var", () => {
|
||||||
|
const ast = createTryCatch("var");
|
||||||
|
|
||||||
|
expect(() => getPath(ast)).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
["let", "const"].forEach(name => {
|
||||||
|
it(`${name} and function in sub scope`, () => {
|
||||||
|
const ast = [
|
||||||
|
t.variableDeclaration(name, [
|
||||||
|
t.variableDeclarator(t.identifier("foo")),
|
||||||
|
]),
|
||||||
|
t.blockStatement([
|
||||||
|
t.functionDeclaration(
|
||||||
|
t.identifier("foo"),
|
||||||
|
[],
|
||||||
|
t.blockStatement([]),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(() => getPath(ast)).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("global", () => {
|
||||||
|
// node1, node2, success
|
||||||
|
// every line will run 2 tests `node1;node2;` and `node2;node1;`
|
||||||
|
// unless node1 === node2
|
||||||
|
const cases = [
|
||||||
|
["const", "let", false],
|
||||||
|
|
||||||
|
["const", "const", false],
|
||||||
|
["const", "function", false],
|
||||||
|
["const", "class", false],
|
||||||
|
["const", "var", false],
|
||||||
|
|
||||||
|
["let", "let", false],
|
||||||
|
["let", "class", false],
|
||||||
|
["let", "function", false],
|
||||||
|
["let", "var", false],
|
||||||
|
|
||||||
|
//["var", "class", true],
|
||||||
|
["var", "function", true],
|
||||||
|
["var", "var", true],
|
||||||
|
|
||||||
|
["class", "function", false],
|
||||||
|
];
|
||||||
|
|
||||||
|
const createNode = function(kind) {
|
||||||
|
switch (kind) {
|
||||||
|
case "let":
|
||||||
|
case "const":
|
||||||
|
case "var":
|
||||||
|
return t.variableDeclaration(kind, [
|
||||||
|
t.variableDeclarator(t.identifier("foo")),
|
||||||
|
]);
|
||||||
|
case "class":
|
||||||
|
return t.classDeclaration(
|
||||||
|
t.identifier("foo"),
|
||||||
|
null,
|
||||||
|
t.classBody([]),
|
||||||
|
);
|
||||||
|
case "function":
|
||||||
|
return t.functionDeclaration(
|
||||||
|
t.identifier("foo"),
|
||||||
|
[],
|
||||||
|
t.blockStatement([]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createAST = function(kind1, kind2) {
|
||||||
|
return [createNode(kind1), createNode(kind2)];
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [kind1, kind2, success] of cases) {
|
||||||
|
it(`${kind1}/${kind2}`, () => {
|
||||||
|
const ast = createAST(kind1, kind2);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
expect(() => getPath(ast)).not.toThrow();
|
||||||
|
} else {
|
||||||
|
expect(() => getPath(ast)).toThrowErrorMatchingSnapshot();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/*if (kind1 !== kind2) {
|
||||||
|
it(`${kind2}/${kind1}`, () => {
|
||||||
|
const ast = createAST(kind2, kind1);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
expect(() => getPath(ast)).not.toThrow();
|
||||||
|
} else {
|
||||||
|
expect(() => getPath(ast)).toThrowErrorMatchingSnapshot();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user