Skip to content
This repository was archived by the owner on Jan 19, 2019. It is now read-only.

Commit eb23b26

Browse files
authored
Update: add proper scope analysis (fixes #535) (#540)
* Update: add proper scope analysis (fixes #535) * add computed-properties-in-type fixture * add computed-properties-in-interface fixture * add function-overload fixture * add method-overload fixture * add class-properties fixture * add decorators fixture * update visitor-keys * add declare-global fixture * fix typo * add test for typeof in array destructuring * add namespace fixture * add declare-module fixture * fix crash * add declare-function.ts fixture * add abstract-class fixture * add typeof-in-call-signature fixture * add test for #416 * add test for #435 * add test for #437 * add test for #443 * add test for #459 * add test for #466 * add test for #471 * add test for #487 * add test for #535 * add test for #536 * add test for #476 * fix test to use `expect()`
1 parent 150ffe8 commit eb23b26

30 files changed

+6647
-28
lines changed

analyze-scope.js

+598
Large diffs are not rendered by default.

package.json

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"version": "20.1.1",
88
"files": [
99
"parser.js",
10+
"analyze-scope.js",
1011
"visitor-keys.js"
1112
],
1213
"engines": {
@@ -43,7 +44,9 @@
4344
"typescript": "*"
4445
},
4546
"dependencies": {
47+
"eslint-scope": "^4.0.0",
4648
"eslint-visitor-keys": "^1.0.0",
49+
"lodash": "^4.17.11",
4750
"typescript-estree": "5.0.0"
4851
},
4952
"devDependencies": {

parser.js

+35-5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
const parse = require("typescript-estree").parse;
1212
const astNodeTypes = require("typescript-estree").AST_NODE_TYPES;
1313
const traverser = require("eslint/lib/util/traverser");
14+
const analyzeScope = require("./analyze-scope");
1415
const visitorKeys = require("./visitor-keys");
1516

1617
//------------------------------------------------------------------------------
@@ -33,16 +34,45 @@ exports.parseForESLint = function parseForESLint(code, options) {
3334
}
3435

3536
const ast = parse(code, options);
37+
const extraOptions = {
38+
sourceType: ast.sourceType
39+
};
40+
3641
traverser.traverse(ast, {
3742
enter: node => {
38-
if (node.type === "DeclareFunction" || node.type === "FunctionExpression" || node.type === "FunctionDeclaration") {
39-
if (!node.body) {
40-
node.type = `TSEmptyBody${node.type}`;
41-
}
43+
switch (node.type) {
44+
// Just for backword compatibility.
45+
case "DeclareFunction":
46+
if (!node.body) {
47+
node.type = `TSEmptyBody${node.type}`;
48+
}
49+
break;
50+
51+
// Function#body cannot be null in ESTree spec.
52+
case "FunctionExpression":
53+
case "FunctionDeclaration":
54+
if (!node.body) {
55+
node.type = `TSEmptyBody${node.type}`;
56+
}
57+
break;
58+
59+
// Import/Export declarations cannot appear in script.
60+
// But if those appear only in namespace/module blocks, `ast.sourceType` was `"script"`.
61+
// This doesn't modify `ast.sourceType` directly for backrard compatibility.
62+
case "ImportDeclaration":
63+
case "ExportAllDeclaration":
64+
case "ExportDefaultDeclaration":
65+
case "ExportNamedDeclaration":
66+
extraOptions.sourceType = "module";
67+
break;
68+
69+
// no default
4270
}
4371
}
4472
});
45-
return { ast, visitorKeys };
73+
74+
const scopeManager = analyzeScope(ast, options, extraOptions);
75+
return { ast, scopeManager, visitorKeys };
4676
};
4777

4878
exports.parse = function(code, options) {

tests/fixtures/scope-analysis/535.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
function foo({ bar }: { bar: string }) {
2+
bar;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
abstract class A {
2+
abstract a: string
3+
abstract f(): number
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const s = Symbol()
2+
class A {
3+
a: typeof s
4+
[s]: number
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const s1 = Symbol(), s2 = Symbol()
2+
interface A {
3+
[s1]: number
4+
[s2](s1: number, s2: number): number;
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const s1 = Symbol(), s2 = Symbol()
2+
type A = {
3+
[s1]: number
4+
[s2](s1: number, s2: number): number;
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
declare function f(a: number): number
2+
f
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
declare global {
2+
let C: number
3+
}
4+
5+
C = 1
6+
7+
export {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const a = 1
2+
declare module "foo" {
3+
export const a: number
4+
export const b: typeof a
5+
}
6+
a
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
function dec(target: any) {
2+
}
3+
function gec() {
4+
return (target: any, propertyKey: string) => {}
5+
}
6+
7+
@dec
8+
class C {
9+
@gec() field: string
10+
@gec() method(): string {
11+
return ""
12+
}
13+
}

tests/fixtures/scope-analysis/enum.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const a: number = 1
2+
enum E {
3+
A = a,
4+
B = a + 1,
5+
C = A + B
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
function f(): void
2+
function f(a: number): void
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function f(): void
2+
function f(a: number): void
3+
function f(a?: number): void {
4+
// do something.
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
type A = number
2+
interface B {
3+
prop1: A
4+
}
5+
interface C extends B {
6+
method(a: { b: A }): { c: A }
7+
}
8+
9+
var a: C
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const s = Symbol()
2+
class A {
3+
f(): void
4+
f(a: typeof s): void
5+
f(a?: any): void {
6+
// do something.
7+
}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const a = 1
2+
namespace N {
3+
export const a = 2
4+
a
5+
}
6+
a
7+
N.a
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
type A = number
2+
var a: { b: A }
3+
class C {
4+
f(a: { b: A }): { b: A } {
5+
return {b: 1}
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
type A = number;
2+
a = <A> b;
3+
a = b as A;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
var obj = { value: 1 }
2+
a = <typeof obj>b;
3+
a = b as typeof obj;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const obj = { value: 1 }
2+
interface A {
3+
<T extends typeof obj>(a: typeof obj, b: T): typeof obj
4+
new <T extends typeof obj>(a: typeof obj, b: T): typeof obj
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
function f(a: number): typeof a { // this `a` is the parameter `a`.
2+
return 1
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
function g<T extends typeof g>(g: T): number {
2+
return 1
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
var obj = { value: 1 }
2+
var obj2: typeof obj = { value: 2 }
3+
var { value }: typeof obj = { value: 2 }
4+
var [element]: (typeof obj)[] = [{ value: 2 }]
+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
var obj = { value: 1 }
2+
type B = typeof obj

0 commit comments

Comments
 (0)