sync babel 5.x with 6.0 scope/traversal fixes - fixes #2570, fixes #2574

This commit is contained in:
Sebastian McKenzie 2015-10-23 17:51:18 +01:00
parent 71031be044
commit 0b71625440
3 changed files with 153 additions and 164 deletions

View File

@ -11,28 +11,15 @@ import extend from "lodash/object/extend";
import object from "../../helpers/object";
import * as t from "../../types";
/**
* [Please add a description.]
*/
var collectorVisitor = {
/**
* [Please add a description.]
*/
For(node, parent, scope) {
for (var key of (t.FOR_INIT_KEYS: Array)) {
var declar = this.get(key);
if (declar.isVar()) scope.getFunctionParent().registerBinding("var", declar);
For() {
for (let key of (t.FOR_INIT_KEYS: Array)) {
let declar = this.get(key);
if (declar.isVar()) this.scope.getFunctionParent().registerBinding("var", declar);
}
},
/**
* [Please add a description.]
*/
Declaration(node, parent, scope) {
Declaration() {
// delegate block scope handling to the `blockVariableVisitor`
if (this.isBlockScoped()) return;
@ -40,124 +27,73 @@ var collectorVisitor = {
if (this.isExportDeclaration() && this.get("declaration").isDeclaration()) return;
// we've ran into a declaration!
scope.getFunctionParent().registerDeclaration(this);
this.scope.getFunctionParent().registerDeclaration(this);
},
/**
* [Please add a description.]
*/
ReferencedIdentifier(node) {
var binding = this.scope.getBinding(node.name);
if (binding) {
binding.reference(this);
} else {
this.scope.getProgramParent().addGlobal(node);
}
ReferencedIdentifier(node, parent, scope, state) {
state.references.push(this);
},
/**
* [Please add a description.]
*/
ForXStatement() {
var left = this.get("left");
ForXStatement(node, parent, scope, state) {
let left = this.get("left");
if (left.isPattern() || left.isIdentifier()) {
this.scope.registerConstantViolation(left, left);
state.constantViolations.push(left);
}
},
/**
* [Please add a description.]
*/
ExportDeclaration: {
exit(node) {
var declar = node.declaration;
exit(node, parent, scope) {
let declar = node.declaration;
if (t.isClassDeclaration(declar) || t.isFunctionDeclaration(declar)) {
this.scope.getBinding(declar.id.name).reference();
let binding = scope.getBinding(declar.id.name);
if (binding) binding.reference();
} else if (t.isVariableDeclaration(declar)) {
for (var decl of (declar.declarations: Array)) {
var ids = t.getBindingIdentifiers(decl);
for (var name in ids) {
this.scope.getBinding(name).reference();
for (let decl of (declar.declarations: Array<Object>)) {
let ids = t.getBindingIdentifiers(decl);
for (let name in ids) {
let binding = scope.getBinding(name);
if (binding) binding.reference();
}
}
}
}
},
/**
* [Please add a description.]
*/
LabeledStatement(node) {
this.scope.getProgramParent().addGlobal(node);
LabeledStatement() {
this.scope.getProgramParent().addGlobal(this.node);
this.scope.getBlockParent().registerDeclaration(this);
},
/**
* [Please add a description.]
*/
AssignmentExpression(node, parent, scope, state) {
state.assignments.push(this);
},
AssignmentExpression() {
// register undeclared bindings as globals
var ids = this.getBindingIdentifiers();
var programParent;
for (var name in ids) {
if (this.scope.getBinding(name)) continue;
UpdateExpression(node, parent, scope, state) {
state.constantViolations.push(this.get("argument"));
},
programParent = programParent || this.scope.getProgramParent();
programParent.addGlobal(ids[name]);
UnaryExpression(node, parent, scope, state) {
if (this.node.operator === "delete") {
state.constantViolations.push(this.get("argument"));
}
// register as constant violation
this.scope.registerConstantViolation(this, this.get("left"), this.get("right"));
},
/**
* [Please add a description.]
*/
UpdateExpression(node, parent, scope) {
scope.registerConstantViolation(this, this.get("argument"), null);
},
/**
* [Please add a description.]
*/
UnaryExpression(node, parent, scope) {
if (node.operator === "delete") scope.registerConstantViolation(this, this.get("left"), null);
},
/**
* [Please add a description.]
*/
BlockScoped(node, parent, scope) {
BlockScoped() {
let scope = this.scope;
if (scope.path === this) scope = scope.parent;
scope.getBlockParent().registerDeclaration(this);
},
/**
* [Please add a description.]
*/
ClassDeclaration(node, parent, scope) {
var name = node.id.name;
scope.bindings[name] = scope.getBinding(name);
ClassDeclaration() {
let name = this.node.id.name;
this.scope.bindings[name] = this.scope.getBinding(name);
},
/**
* [Please add a description.]
*/
Block(node, parent, scope) {
var paths = this.get("body");
for (var path of (paths: Array)) {
if (path.isFunctionDeclaration()) {
scope.getBlockParent().registerDeclaration(path);
Block() {
let paths = this.get("body");
for (let bodyPath of (paths: Array)) {
if (bodyPath.isFunctionDeclaration()) {
this.scope.getBlockParent().registerDeclaration(bodyPath);
}
}
}
@ -529,7 +465,7 @@ export default class Scope {
if (path.isLabeledStatement()) {
this.registerBinding("label", path);
} else if (path.isFunctionDeclaration()) {
this.registerBinding("hoisted", path);
this.registerBinding("hoisted", path.get("id"));
} else if (path.isVariableDeclaration()) {
var declarations = path.get("declarations");
for (let declar of (declarations: Array)) {
@ -556,11 +492,11 @@ export default class Scope {
* [Please add a description.]
*/
registerConstantViolation(root: NodePath, left: NodePath, right: NodePath) {
var ids = left.getBindingIdentifiers();
for (var name in ids) {
var binding = this.getBinding(name);
if (binding) binding.reassign(root, left, right);
registerConstantViolation(path) {
let ids = path.getBindingIdentifiers();
for (let name in ids) {
let binding = this.getBinding(name);
if (binding) binding.reassign(path);
}
}
@ -754,8 +690,8 @@ export default class Scope {
// ForStatement - left, init
if (path.isLoop()) {
for (let key of (t.FOR_INIT_KEYS: Array)) {
var node = path.get(key);
for (let key of (t.FOR_INIT_KEYS: Array<string>)) {
let node = path.get(key);
if (node.isBlockScoped()) this.registerBinding(node.node.kind, node);
}
}
@ -763,21 +699,19 @@ export default class Scope {
// FunctionExpression - id
if (path.isFunctionExpression() && path.has("id")) {
if (!t.isProperty(path.parent, { method: true })) {
this.registerBinding("var", path);
}
this.registerBinding("local", path);
}
// Class
if (path.isClassExpression() && path.has("id")) {
this.registerBinding("var", path);
this.registerBinding("local", path);
}
// Function - params, rest
if (path.isFunction()) {
var params = path.get("params");
let params = path.get("params");
for (let param of (params: Array)) {
this.registerBinding("param", param);
}
@ -800,9 +734,47 @@ export default class Scope {
var parent = this.getProgramParent();
if (parent.crawling) return;
let state = {
references: [],
constantViolations: [],
assignments: [],
};
this.crawling = true;
path.traverse(collectorVisitor);
path.traverse(collectorVisitor, state);
this.crawling = false;
// register assignments
for (let path of state.assignments) {
// register undeclared bindings as globals
let ids = path.getBindingIdentifiers();
let programParent;
for (let name in ids) {
if (path.scope.getBinding(name)) continue;
programParent = programParent || path.scope.getProgramParent();
programParent.addGlobal(ids[name]);
}
// register as constant violation
path.scope.registerConstantViolation(path);
}
// register references
for (let ref of state.references) {
let binding = ref.scope.getBinding(ref.node.name);
if (binding) {
binding.reference(ref);
} else {
ref.scope.getProgramParent().addGlobal(ref.node);
}
}
// register constant violations
for (let path of state.constantViolations) {
path.scope.registerConstantViolation(path);
}
}
/**

View File

@ -1,23 +1,27 @@
import object from "../helpers/object";
/* @flow */
import * as t from "./index";
/**
* Return a list of binding identifiers associated with the input `node`.
*/
export function getBindingIdentifiers(node: Object, duplicates?): Object {
var search = [].concat(node);
var ids = object();
export function getBindingIdentifiers(
node: Object,
duplicates?: boolean,
): Object {
let search = [].concat(node);
let ids = Object.create(null);
while (search.length) {
var id = search.shift();
let id = search.shift();
if (!id) continue;
var key = t.getBindingIdentifiers.keys[id.type];
let keys = t.getBindingIdentifiers.keys[id.type];
if (t.isIdentifier(id)) {
if (duplicates) {
var _ids = ids[id.name] = ids[id.name] || [];
let _ids = ids[id.name] = ids[id.name] || [];
_ids.push(id);
} else {
ids[id.name] = id;
@ -26,8 +30,13 @@ export function getBindingIdentifiers(node: Object, duplicates?): Object {
if (t.isDeclaration(node.declaration)) {
search.push(node.declaration);
}
} else if (key && id[key]) {
search = search.concat(id[key]);
} else if (keys) {
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
if (id[key]) {
search = search.concat(id[key]);
}
}
}
}
@ -39,42 +48,42 @@ export function getBindingIdentifiers(node: Object, duplicates?): Object {
*/
getBindingIdentifiers.keys = {
DeclareClass: "id",
DeclareFunction: "id",
DeclareModule: "id",
DeclareVariable: "id",
InterfaceDeclaration: "id",
TypeAlias: "id",
DeclareClass: ["id"],
DeclareFunction: ["id"],
DeclareModule: ["id"],
DeclareVariable: ["id"],
InterfaceDeclaration: ["id"],
TypeAlias: ["id"],
ComprehensionExpression: "blocks",
ComprehensionBlock: "left",
ComprehensionExpression: ["blocks"],
ComprehensionBlock: ["left"],
CatchClause: "param",
LabeledStatement: "label",
UnaryExpression: "argument",
AssignmentExpression: "left",
CatchClause: ["param"],
LabeledStatement: ["label"],
UnaryExpression: ["argument"],
AssignmentExpression: ["left"],
ImportSpecifier: "local",
ImportNamespaceSpecifier: "local",
ImportDefaultSpecifier: "local",
ImportDeclaration: "specifiers",
ImportSpecifier: ["local"],
ImportNamespaceSpecifier: ["local"],
ImportDefaultSpecifier: ["local"],
ImportDeclaration: ["specifiers"],
FunctionDeclaration: "id",
FunctionExpression: "id",
FunctionDeclaration: ["id", "params"],
FunctionExpression: ["id", "params"],
ClassDeclaration: "id",
ClassExpression: "id",
ClassDeclaration: ["id"],
ClassExpression: ["id"],
RestElement: "argument",
UpdateExpression: "argument",
RestElement: ["argument"],
UpdateExpression: ["argument"],
SpreadProperty: "argument",
Property: "value",
SpreadProperty: ["argument"],
Property: ["value"],
AssignmentPattern: "left",
ArrayPattern: "elements",
ObjectPattern: "properties",
AssignmentPattern: ["left"],
ArrayPattern: ["elements"],
ObjectPattern: ["properties"],
VariableDeclaration: "declarations",
VariableDeclarator: "id"
VariableDeclaration: ["declarations"],
VariableDeclarator: ["id"]
};

View File

@ -6,13 +6,21 @@ import * as t from "./index";
* Check if the input `node` is a binding identifier.
*/
export function isBinding(node: Object, parent: Object): boolean {
var bindingKey = getBindingIdentifiers.keys[parent.type];
if (bindingKey) {
return parent[bindingKey] === node;
} else {
return false;
}
export function isBinding(node: Object, parent: Object): boolean {
let keys = getBindingIdentifiers.keys[parent.type];
if (keys) {
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
let val = parent[key];
if (Array.isArray(val)) {
if (val.indexOf(node) >= 0) return true;
} else {
if (val === node) return true;
}
}
}
return false;
}
/**