From 4a727fa083d749dba9eaf39322856f5f69c28cd8 Mon Sep 17 00:00:00 2001 From: Armano Date: Tue, 21 Jan 2020 18:01:43 +0100 Subject: [PATCH] feat(parser): clean up scope-analysis types (#1481) --- .../src/ts-eslint-scope/Reference.ts | 17 +++- .../src/ts-eslint-scope/Referencer.ts | 4 +- .../src/ts-eslint-scope/Scope.ts | 26 ++--- .../src/ts-eslint-scope/ScopeManager.ts | 7 +- .../src/ts-eslint-scope/Variable.ts | 4 + packages/parser/tests/tools/scope-analysis.ts | 99 ++++++++++--------- 6 files changed, 95 insertions(+), 62 deletions(-) diff --git a/packages/experimental-utils/src/ts-eslint-scope/Reference.ts b/packages/experimental-utils/src/ts-eslint-scope/Reference.ts index 15afc7dcdc1..96d2fab4df5 100644 --- a/packages/experimental-utils/src/ts-eslint-scope/Reference.ts +++ b/packages/experimental-utils/src/ts-eslint-scope/Reference.ts @@ -3,6 +3,8 @@ import ESLintReference from 'eslint-scope/lib/reference'; import { Scope } from './Scope'; import { Variable } from './Variable'; +export type ReferenceFlag = 0x1 | 0x2 | 0x3; + interface Reference { identifier: TSESTree.Identifier; from: Scope; @@ -10,6 +12,11 @@ interface Reference { writeExpr: TSESTree.Node | null; init: boolean; + partial: boolean; + __maybeImplicitGlobal: boolean; + tainted?: boolean; + typeMode?: boolean; + isWrite(): boolean; isRead(): boolean; isWriteOnly(): boolean; @@ -17,7 +24,15 @@ interface Reference { isReadWrite(): boolean; } const Reference = ESLintReference as { - new (): Reference; + new ( + identifier: TSESTree.Identifier, + scope: Scope, + flag?: ReferenceFlag, + writeExpr?: TSESTree.Node | null, + maybeImplicitGlobal?: boolean, + partial?: boolean, + init?: boolean, + ): Reference; READ: 0x1; WRITE: 0x2; diff --git a/packages/experimental-utils/src/ts-eslint-scope/Referencer.ts b/packages/experimental-utils/src/ts-eslint-scope/Referencer.ts index aa0cbc8d3e9..6169d9def9a 100644 --- a/packages/experimental-utils/src/ts-eslint-scope/Referencer.ts +++ b/packages/experimental-utils/src/ts-eslint-scope/Referencer.ts @@ -44,8 +44,8 @@ interface Referencer extends Visitor { AssignmentExpression(node: TSESTree.Node): void; CatchClause(node: TSESTree.Node): void; - Program(node: TSESTree.Node): void; - Identifier(node: TSESTree.Node): void; + Program(node: TSESTree.Program): void; + Identifier(node: TSESTree.Identifier): void; UpdateExpression(node: TSESTree.Node): void; MemberExpression(node: TSESTree.Node): void; Property(node: TSESTree.Node): void; diff --git a/packages/experimental-utils/src/ts-eslint-scope/Scope.ts b/packages/experimental-utils/src/ts-eslint-scope/Scope.ts index bd6455d7020..6c5481aa4d7 100644 --- a/packages/experimental-utils/src/ts-eslint-scope/Scope.ts +++ b/packages/experimental-utils/src/ts-eslint-scope/Scope.ts @@ -15,7 +15,7 @@ import { ClassScope as ESLintClassScope, } from 'eslint-scope/lib/scope'; import { Definition } from './Definition'; -import { Reference } from './Reference'; +import { Reference, ReferenceFlag } from './Reference'; import { ScopeManager } from './ScopeManager'; import { Variable } from './Variable'; @@ -46,7 +46,9 @@ interface Scope { references: Reference[]; through: Reference[]; thisFound?: boolean; + taints: Map; functionExpressionScope: boolean; + __left: Reference[]; __shouldStaticallyClose(scopeManager: ScopeManager): boolean; __shouldStaticallyCloseForGlobal(ref: any): boolean; @@ -54,15 +56,15 @@ interface Scope { __dynamicCloseRef(ref: any): void; __globalCloseRef(ref: any): void; __close(scopeManager: ScopeManager): Scope; - __isValidResolution(ref: any, variable: any): boolean; - __resolve(ref: any): boolean; + __isValidResolution(ref: any, variable: any): variable is Variable; + __resolve(ref: Reference): boolean; __delegateToUpperScope(ref: any): void; __addDeclaredVariablesOfNode(variable: any, node: TSESTree.Node): void; __defineGeneric( - name: any, - set: any, - variables: any, - node: any, + name: string, + set: Map, + variables: Variable[], + node: TSESTree.Identifier, def: Definition, ): void; @@ -70,11 +72,11 @@ interface Scope { __referencing( node: TSESTree.Node, - assign: number, - writeExpr: TSESTree.Node, - maybeImplicitGlobal: any, - partial: any, - init: any, + assign?: ReferenceFlag, + writeExpr?: TSESTree.Node, + maybeImplicitGlobal?: any, + partial?: any, + init?: any, ): void; __detectEval(): void; diff --git a/packages/experimental-utils/src/ts-eslint-scope/ScopeManager.ts b/packages/experimental-utils/src/ts-eslint-scope/ScopeManager.ts index e90c3cf4b11..6d05fea395f 100644 --- a/packages/experimental-utils/src/ts-eslint-scope/ScopeManager.ts +++ b/packages/experimental-utils/src/ts-eslint-scope/ScopeManager.ts @@ -16,6 +16,9 @@ interface ScopeManagerOptions { interface ScopeManager { __options: ScopeManagerOptions; __currentScope: Scope; + __nodeToScope: WeakMap; + __declaredVariables: WeakMap; + scopes: Scope[]; globalScope: Scope; @@ -28,7 +31,7 @@ interface ScopeManager { isStrictModeSupported(): boolean; // Returns appropriate scope for this node. - __get(node: TSESTree.Node): Scope; + __get(node: TSESTree.Node): Scope | undefined; getDeclaredVariables(node: TSESTree.Node): Variable[]; acquire(node: TSESTree.Node, inner?: boolean): Scope | null; acquireAll(node: TSESTree.Node): Scope | null; @@ -36,7 +39,7 @@ interface ScopeManager { attach(): void; detach(): void; - __nestScope(scope: Scope): Scope; + __nestScope(scope: T): T; __nestGlobalScope(node: TSESTree.Node): Scope; __nestBlockScope(node: TSESTree.Node): Scope; __nestFunctionScope(node: TSESTree.Node, isMethodDefinition: boolean): Scope; diff --git a/packages/experimental-utils/src/ts-eslint-scope/Variable.ts b/packages/experimental-utils/src/ts-eslint-scope/Variable.ts index 306d5bfca49..49bbf3d9b4a 100644 --- a/packages/experimental-utils/src/ts-eslint-scope/Variable.ts +++ b/packages/experimental-utils/src/ts-eslint-scope/Variable.ts @@ -2,6 +2,7 @@ import { TSESTree } from '@typescript-eslint/typescript-estree'; import ESLintVariable from 'eslint-scope/lib/variable'; import { Reference } from './Reference'; import { Definition } from './Definition'; +import { Scope } from './Scope'; interface Variable { name: string; @@ -9,6 +10,9 @@ interface Variable { references: Reference[]; defs: Definition[]; eslintUsed?: boolean; + stack?: unknown; + tainted?: boolean; + scope?: Scope; } const Variable = ESLintVariable as { diff --git a/packages/parser/tests/tools/scope-analysis.ts b/packages/parser/tests/tools/scope-analysis.ts index 373b863be81..3def6a382de 100644 --- a/packages/parser/tests/tools/scope-analysis.ts +++ b/packages/parser/tests/tools/scope-analysis.ts @@ -1,28 +1,33 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ +import { + TSESTree, + TSESLintScope, + AST_NODE_TYPES, +} from '@typescript-eslint/experimental-utils'; +import { ScopeManager } from '../../src/scope/scope-manager'; /** Reference resolver. */ -export class ReferenceResolver { - map: Map; +export class ReferenceResolver { + map: Map; constructor() { this.map = new Map(); } - resolve(obj: any, properties: any): any { - const resolved = Object.assign({ $id: this.map.size }, properties); + resolve(obj: TKey, properties: T): T & { $id: number } { + const resolved = { ...properties, $id: this.map.size }; this.map.set(obj, resolved); return resolved; } - ref(obj: any): any { + ref(obj: TKey): { $ref: number } | TKey { if (typeof obj !== 'object' || obj === null) { return obj; } const map = this.map; return { - get $ref(): any { - return map.get(obj).$id; + get $ref(): number { + return map.get(obj)!.$id; }, }; } @@ -31,30 +36,38 @@ export class ReferenceResolver { /** * Convert a given node object to JSON object. * This saves only type and range to know what the node is. - * @param {ASTNode} node The AST node object. - * @returns {Object} The object that can be used for JSON.stringify. + * @param node The AST node object. + * @returns The object that can be used for JSON.stringify. */ -export function nodeToJSON(node: any): any { +export function nodeToJSON( + node: TSESTree.Node | null | undefined, +): + | { type: AST_NODE_TYPES; range: [number, number]; name?: string } + | null + | undefined { if (!node) { return node; } - const { type, name, range } = node; - if (node.type === 'Identifier') { - return { type, name, range }; + const { type, range } = node; + if (node.type === AST_NODE_TYPES.Identifier) { + return { type, name: node.name, range }; } return { type, range }; } /** * Convert a given variable object to JSON object. - * @param {Variable} variable The eslint-scope's variable object. - * @param {ReferenceResolver} resolver The reference resolver. - * @returns {Object} The object that can be used for JSON.stringify. + * @param variable The eslint-scope's variable object. + * @param resolver The reference resolver. + * @returns The object that can be used for JSON.stringify. */ -export function variableToJSON(variable: any, resolver: any): any { +export function variableToJSON( + variable: TSESLintScope.Variable, + resolver: ReferenceResolver, +): unknown { const { name, eslintUsed } = variable; - const defs = variable.defs.map((d: any) => ({ + const defs = variable.defs.map(d => ({ type: d.type, name: nodeToJSON(d.name), node: nodeToJSON(d.node), @@ -76,11 +89,14 @@ export function variableToJSON(variable: any, resolver: any): any { /** * Convert a given reference object to JSON object. - * @param {Reference} reference The eslint-scope's reference object. - * @param {ReferenceResolver} resolver The reference resolver. - * @returns {Object} The object that can be used for JSON.stringify. + * @param reference The eslint-scope's reference object. + * @param resolver The reference resolver. + * @returns The object that can be used for JSON.stringify. */ -export function referenceToJSON(reference: any, resolver: any): any { +export function referenceToJSON( + reference: TSESLintScope.Reference, + resolver: ReferenceResolver, +): unknown { const kind = `${reference.isRead() ? 'r' : ''}${ reference.isWrite() ? 'w' : '' }`; @@ -100,35 +116,28 @@ export function referenceToJSON(reference: any, resolver: any): any { /** * Convert a given scope object to JSON object. - * @param {Scope} scope The eslint-scope's scope object. - * @param {ReferenceResolver} resolver The reference resolver. + * @param scope The eslint-scope's scope object. + * @param resolver The reference resolver. * @returns {Object} The object that can be used for JSON.stringify. */ export function scopeToJSON( - scope: any, + scope: TSESLintScope.Scope, resolver = new ReferenceResolver(), -): any { +): unknown { const { type, functionExpressionScope, isStrict } = scope; const block = nodeToJSON(scope.block); - const variables = scope.variables.map((v: any) => - variableToJSON(v, resolver), - ); - const references = scope.references.map((r: any) => - referenceToJSON(r, resolver), - ); - const variableMap = Array.from(scope.set.entries()).reduce( - (map: any, [name, variable]: any) => { - map[name] = resolver.ref(variable); - return map; - }, - {}, - ); + const variables = scope.variables.map(v => variableToJSON(v, resolver)); + const references = scope.references.map(r => referenceToJSON(r, resolver)); + const variableMap = Array.from(scope.set.entries()).reduce< + Record + >((map, [name, variable]) => { + map[name] = resolver.ref(variable); + return map; + }, {}); const throughReferences = scope.through.map(resolver.ref, resolver); const variableScope = resolver.ref(scope.variableScope); const upperScope = resolver.ref(scope.upper); - const childScopes = scope.childScopes.map((c: any) => - scopeToJSON(c, resolver), - ); + const childScopes = scope.childScopes.map(c => scopeToJSON(c, resolver)); return resolver.resolve(scope, { type, @@ -145,12 +154,12 @@ export function scopeToJSON( }); } -export function getScopeTree(scopeManager: any): any { +export function getScopeTree(scopeManager: ScopeManager): unknown { const { globalScope } = scopeManager; // Do the postprocess to test. // https://github.com/eslint/eslint/blob/84ce72fdeba082b7b132e4ac6b714fb1a93831b7/lib/linter.js#L112-L129 - globalScope.through = globalScope.through.filter((reference: any) => { + globalScope.through = globalScope.through.filter(reference => { const name = reference.identifier.name; const variable = globalScope.set.get(name); if (variable) {