Skip to content

Commit 0deff30

Browse files
committed
feat!: Close var and function references on global scope
1 parent 931df4c commit 0deff30

File tree

4 files changed

+66
-55
lines changed

4 files changed

+66
-55
lines changed

packages/eslint-scope/lib/scope.js

Lines changed: 6 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -122,18 +122,6 @@ function registerScope(scopeManager, scope) {
122122
}
123123
}
124124

125-
/**
126-
* Should be statically
127-
* @param {Object} def def
128-
* @returns {boolean} should be statically
129-
*/
130-
function shouldBeStatically(def) {
131-
return (
132-
(def.type === Variable.ClassName) ||
133-
(def.type === Variable.Variable && def.parent.kind !== "var")
134-
);
135-
}
136-
137125
/**
138126
* @constructor Scope
139127
*/
@@ -267,22 +255,7 @@ class Scope {
267255
}
268256

269257
__shouldStaticallyClose(scopeManager) {
270-
return (!this.dynamic || scopeManager.__isOptimistic());
271-
}
272-
273-
__shouldStaticallyCloseForGlobal(ref) {
274-
275-
// On global scope, let/const/class declarations should be resolved statically.
276-
const name = ref.identifier.name;
277-
278-
if (!this.set.has(name)) {
279-
return false;
280-
}
281-
282-
const variable = this.set.get(name);
283-
const defs = variable.defs;
284-
285-
return defs.length > 0 && defs.every(shouldBeStatically);
258+
return (!this.dynamic || scopeManager.__isOptimistic() || this.type === "global");
286259
}
287260

288261
__staticCloseRef(ref) {
@@ -302,26 +275,13 @@ class Scope {
302275
} while (current);
303276
}
304277

305-
__globalCloseRef(ref) {
306-
307-
// let/const/class declarations should be resolved statically.
308-
// others should be resolved dynamically.
309-
if (this.__shouldStaticallyCloseForGlobal(ref)) {
310-
this.__staticCloseRef(ref);
311-
} else {
312-
this.__dynamicCloseRef(ref);
313-
}
314-
}
315-
316278
__close(scopeManager) {
317279
let closeRef;
318280

319281
if (this.__shouldStaticallyClose(scopeManager)) {
320282
closeRef = this.__staticCloseRef;
321-
} else if (this.type !== "global") {
322-
closeRef = this.__dynamicCloseRef;
323283
} else {
324-
closeRef = this.__globalCloseRef;
284+
closeRef = this.__dynamicCloseRef;
325285
}
326286

327287
// Try Resolving all references in this scope.
@@ -560,9 +520,11 @@ class GlobalScope extends Scope {
560520

561521
}
562522

563-
this.implicit.left = this.__left;
523+
super.__close(scopeManager);
524+
525+
this.implicit.left = [...this.through];
564526

565-
return super.__close(scopeManager);
527+
return null;
566528
}
567529

568530
__defineImplicit(node, def) {

packages/eslint-scope/tests/implicit-global-reference.test.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ describe("implicit global reference", () => {
4444
);
4545

4646
expect(scopes[0].implicit.variables.map(variable => variable.name)).to.be.eql([]);
47+
expect(scopes[0].implicit.left.map(reference => reference.identifier.name)).to.be.eql([]);
48+
expect(scopes[0].through.map(reference => reference.identifier.name)).to.be.eql([]);
4749
});
4850

4951
it("assignments global scope without definition", () => {
@@ -66,6 +68,18 @@ describe("implicit global reference", () => {
6668
"x"
6769
]
6870
);
71+
expect(scopes[0].implicit.left.map(reference => reference.identifier.name)).to.be.eql(
72+
[
73+
"x",
74+
"x"
75+
]
76+
);
77+
expect(scopes[0].through.map(reference => reference.identifier.name)).to.be.eql(
78+
[
79+
"x",
80+
"x"
81+
]
82+
);
6983
});
7084

7185
it("assignments global scope without definition eval", () => {
@@ -120,6 +134,16 @@ describe("implicit global reference", () => {
120134
"x"
121135
]
122136
);
137+
expect(scopes[0].implicit.left.map(reference => reference.identifier.name)).to.be.eql(
138+
[
139+
"x"
140+
]
141+
);
142+
expect(scopes[0].through.map(reference => reference.identifier.name)).to.be.eql(
143+
[
144+
"x"
145+
]
146+
);
123147
});
124148

125149
it("assignment doesn't leak", () => {
@@ -151,6 +175,8 @@ describe("implicit global reference", () => {
151175
);
152176

153177
expect(scopes[0].implicit.variables.map(variable => variable.name)).to.be.eql([]);
178+
expect(scopes[0].implicit.left.map(reference => reference.identifier.name)).to.be.eql([]);
179+
expect(scopes[0].through.map(reference => reference.identifier.name)).to.be.eql([]);
154180
});
155181

156182
it("for-in-statement leaks", () => {
@@ -177,6 +203,18 @@ describe("implicit global reference", () => {
177203
"x"
178204
]
179205
);
206+
expect(scopes[0].implicit.left.map(reference => reference.identifier.name)).to.be.eql(
207+
[
208+
"x",
209+
"y"
210+
]
211+
);
212+
expect(scopes[0].through.map(reference => reference.identifier.name)).to.be.eql(
213+
[
214+
"x",
215+
"y"
216+
]
217+
);
180218
});
181219

182220
it("for-in-statement doesn't leaks", () => {
@@ -208,5 +246,15 @@ describe("implicit global reference", () => {
208246
);
209247

210248
expect(scopes[0].implicit.variables.map(variable => variable.name)).to.be.eql([]);
249+
expect(scopes[0].implicit.left.map(reference => reference.identifier.name)).to.be.eql(
250+
[
251+
"y"
252+
]
253+
);
254+
expect(scopes[0].through.map(reference => reference.identifier.name)).to.be.eql(
255+
[
256+
"y"
257+
]
258+
);
211259
});
212260
});

packages/eslint-scope/tests/label.test.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,10 @@ describe("label", () => {
6666
expect(globalScope.type).to.be.equal("global");
6767
expect(globalScope.variables).to.have.length(1);
6868
expect(globalScope.variables[0].name).to.be.equal("foo");
69-
expect(globalScope.through.length).to.be.equal(3);
70-
expect(globalScope.through[2].identifier.name).to.be.equal("foo");
71-
expect(globalScope.through[2].isRead()).to.be.true;
69+
expect(globalScope.through.length).to.be.equal(1);
70+
expect(globalScope.through[0].identifier.name).to.be.equal("console");
71+
expect(globalScope.variables[0].references.length).to.be.equal(2);
72+
expect(globalScope.variables[0].references[1].isRead()).to.be.true;
7273
});
7374
});
7475

packages/eslint-scope/tests/references.test.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ describe("References:", () => {
155155
});
156156

157157
describe("When there is a `var` declaration on global,", () => {
158-
it("the reference on global should NOT be resolved.", () => {
158+
it("the reference on global should be resolved.", () => {
159159
const ast = espree("var a = 0;");
160160

161161
const scopeManager = analyze(ast, { ecmaVersion: 6 });
@@ -171,13 +171,13 @@ describe("References:", () => {
171171

172172
expect(reference.from).to.equal(scope);
173173
expect(reference.identifier.name).to.equal("a");
174-
expect(reference.resolved).to.be.null;
174+
expect(reference.resolved).to.equal(scope.variables[0]);
175175
expect(reference.writeExpr).to.not.be.undefined;
176176
expect(reference.isWrite()).to.be.true;
177177
expect(reference.isRead()).to.be.false;
178178
});
179179

180-
it("the reference in functions should NOT be resolved.", () => {
180+
it("the reference in functions should be resolved.", () => {
181181
const ast = espree(`
182182
var a = 0;
183183
function foo() {
@@ -198,15 +198,15 @@ describe("References:", () => {
198198

199199
expect(reference.from).to.equal(scope);
200200
expect(reference.identifier.name).to.equal("a");
201-
expect(reference.resolved).to.be.null;
201+
expect(reference.resolved).to.equal(scopeManager.scopes[0].variables[0]);
202202
expect(reference.writeExpr).to.be.undefined;
203203
expect(reference.isWrite()).to.be.false;
204204
expect(reference.isRead()).to.be.true;
205205
});
206206
});
207207

208208
describe("When there is a `function` declaration on global,", () => {
209-
it("the reference on global should NOT be resolved.", () => {
209+
it("the reference on global should be resolved.", () => {
210210
const ast = espree(`
211211
function a() {}
212212
a();
@@ -225,13 +225,13 @@ describe("References:", () => {
225225

226226
expect(reference.from).to.equal(scope);
227227
expect(reference.identifier.name).to.equal("a");
228-
expect(reference.resolved).to.be.null;
228+
expect(reference.resolved).to.equal(scope.variables[0]);
229229
expect(reference.writeExpr).to.be.undefined;
230230
expect(reference.isWrite()).to.be.false;
231231
expect(reference.isRead()).to.be.true;
232232
});
233233

234-
it("the reference in functions should NOT be resolved.", () => {
234+
it("the reference in functions should be resolved.", () => {
235235
const ast = espree(`
236236
function a() {}
237237
function foo() {
@@ -252,7 +252,7 @@ describe("References:", () => {
252252

253253
expect(reference.from).to.equal(scope);
254254
expect(reference.identifier.name).to.equal("a");
255-
expect(reference.resolved).to.be.null;
255+
expect(reference.resolved).to.equal(scopeManager.scopes[0].variables[0]);
256256
expect(reference.writeExpr).to.be.undefined;
257257
expect(reference.isWrite()).to.be.false;
258258
expect(reference.isRead()).to.be.true;

0 commit comments

Comments
 (0)