diff --git a/CHANGELOG.md b/CHANGELOG.md index 950b1e150b..e9d27b5838 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.12.12 + + * Make scope tracker more reliable to handle all edgecases. + # 1.12.11 * Block scope classes. diff --git a/lib/6to5/traverse/scope.js b/lib/6to5/traverse/scope.js index 9039501e0c..335fe9d942 100644 --- a/lib/6to5/traverse/scope.js +++ b/lib/6to5/traverse/scope.js @@ -4,6 +4,8 @@ var traverse = require("./index"); var t = require("../types"); var _ = require("lodash"); +var FOR_KEYS = ["left", "init"]; + function Scope(parent, block) { this.parent = parent; this.block = block; @@ -19,29 +21,59 @@ Scope.prototype.getIds = function () { var self = this; var ids = block._scopeIds = {}; - if (t.isBlockStatement(block)) { - _.each(block.body, function (node) { - if (t.isVariableDeclaration(node) && node.kind !== "var") { - self.add(node, ids); - } - }); - } else if (t.isProgram(block) || t.isFunction(block)) { - traverse(block, function (node, parent) { - if (parent !== block && t.isVariableDeclaration(node) && node.kind !== "var") { - return; - } + // ForStatement - left, init - if (t.isDeclaration(node)) { - self.add(node, ids); - } else if (t.isFunction(node)) { - return false; - } + if (t.isFor(block)) { + _.each(FOR_KEYS, function (key) { + var node = block[key]; + if (t.isLet(node)) self.add(node, ids); }); - } else if (t.isCatchClause(block)) { + + block = block.body; + } + + // Program, BlockStatement - let variables + + if (t.isBlockStatement(block) || t.isProgram(block)) { + _.each(block.body, function (node) { + // check for non-var `VariableDeclaration`s + if (t.isLet(node)) self.add(node, ids); + }); + } + + // CatchClause - param + + if (t.isCatchClause(block)) { self.add(block.param, ids); } + // Program, Function - var variables + + if (t.isProgram(block) || t.isFunction(block)) { + traverse(block, function (node) { + if (t.isFor(node)) { + _.each(FOR_KEYS, function (key) { + var declar = node[key]; + if (t.isVar(declar)) self.add(declar, ids); + }); + } + + // this block is a function so we'll stop since none of the variables + // declared within are accessible + if (t.isFunction(node)) return false; + + // we've ran into a declaration! + // we'll let the BlockStatement scope deal with `let` declarations + if (t.isDeclaration(node) && !t.isLet(node)) { + self.add(node, ids); + } + }); + } + + // Function - params + if (t.isFunction(block)) { + this.add(block.rest, ids); _.each(block.params, function (param) { self.add(param, ids); }); @@ -51,6 +83,7 @@ Scope.prototype.getIds = function () { }; Scope.prototype.add = function (node, ids) { + if (!node) return; _.merge(ids || this.ids, t.getIds(node, true)); }; diff --git a/lib/6to5/types/alias-keys.json b/lib/6to5/types/alias-keys.json index b2f4aef530..b8920e3cd3 100644 --- a/lib/6to5/types/alias-keys.json +++ b/lib/6to5/types/alias-keys.json @@ -35,9 +35,9 @@ "ClassDeclaration": ["Statement", "Declaration", "Class"], "ClassExpression": ["Class"], - "ForOfStatement": ["Statement", "For"], - "ForInStatement": ["Statement", "For"], - "ForStatement": ["Statement", "For"], + "ForOfStatement": ["Statement", "For", "Scope"], + "ForInStatement": ["Statement", "For", "Scope"], + "ForStatement": ["Statement", "For", "Scope"], "ObjectPattern": ["Pattern"], "ArrayPattern": ["Pattern"], diff --git a/lib/6to5/types/index.js b/lib/6to5/types/index.js index c921215e4f..28d3fc17ad 100644 --- a/lib/6to5/types/index.js +++ b/lib/6to5/types/index.js @@ -185,6 +185,14 @@ t.getIds = function (node, map, ignoreTypes) { return ids; }; +t.isLet = function (node) { + return t.isVariableDeclaration(node) && (node.kind !== "var" || node._let); +}; + +t.isVar = function (node) { + return t.isVariableDeclaration(node, { kind: "var" }) && !node._let; +}; + t.getIds.nodes = { AssignmentExpression: "left", ImportSpecifier: "id", diff --git a/test/fixtures/transformation/let-scoping/duplicate-in-upper-scope/exec.js b/test/fixtures/transformation/let-scoping/duplicate-in-upper-scope/exec.js new file mode 100644 index 0000000000..b0c9c0f6c1 --- /dev/null +++ b/test/fixtures/transformation/let-scoping/duplicate-in-upper-scope/exec.js @@ -0,0 +1,5 @@ +let x = [0]; +for (let x of x) { + assert.equal(x, 0); +} +assert.deepEqual(x, [0]);