From 4e83b529e2d25c971c952ab1521876958224e894 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Sun, 4 Nov 2018 10:33:51 +0900 Subject: [PATCH 01/29] Update: add proper scope analysis (fixes #535) --- analyze-scope.js | 323 +++ package.json | 2 + parser.js | 40 +- tests/fixtures/scope-analysis/535.ts | 3 + tests/fixtures/scope-analysis/enum.ts | 6 + .../scope-analysis/ignore-type-only-stuff.ts | 9 + .../scope-analysis/type-annotations.ts | 7 + .../scope-analysis/type-assertions.ts | 3 + .../scope-analysis/typeof-in-assertions.ts | 3 + .../scope-analysis/typeof-in-return-type.ts | 3 + .../typeof-in-type-parameters.ts | 3 + .../fixtures/scope-analysis/typeof-in-var.ts | 3 + tests/fixtures/scope-analysis/typeof.ts | 2 + .../lib/__snapshots__/scope-analysis.js.snap | 2144 +++++++++++++++++ tests/lib/scope-analysis.js | 171 ++ visitor-keys.js | 3 + 16 files changed, 2720 insertions(+), 5 deletions(-) create mode 100644 analyze-scope.js create mode 100644 tests/fixtures/scope-analysis/535.ts create mode 100644 tests/fixtures/scope-analysis/enum.ts create mode 100644 tests/fixtures/scope-analysis/ignore-type-only-stuff.ts create mode 100644 tests/fixtures/scope-analysis/type-annotations.ts create mode 100644 tests/fixtures/scope-analysis/type-assertions.ts create mode 100644 tests/fixtures/scope-analysis/typeof-in-assertions.ts create mode 100644 tests/fixtures/scope-analysis/typeof-in-return-type.ts create mode 100644 tests/fixtures/scope-analysis/typeof-in-type-parameters.ts create mode 100644 tests/fixtures/scope-analysis/typeof-in-var.ts create mode 100644 tests/fixtures/scope-analysis/typeof.ts create mode 100644 tests/lib/__snapshots__/scope-analysis.js.snap create mode 100644 tests/lib/scope-analysis.js diff --git a/analyze-scope.js b/analyze-scope.js new file mode 100644 index 0000000..93be3e9 --- /dev/null +++ b/analyze-scope.js @@ -0,0 +1,323 @@ +"use strict"; + +/* eslint-disable new-cap, no-underscore-dangle */ + +const escope = require("eslint-scope"); +const { Definition, ParameterDefinition } = require("eslint-scope/lib/definition"); +const OriginalPatternVisitor = require("eslint-scope/lib/pattern-visitor"); +const Reference = require("eslint-scope/lib/reference"); +const OriginalReferencer = require("eslint-scope/lib/referencer"); +const Scope = require("eslint-scope/lib/scope").Scope; +const fallback = require("eslint-visitor-keys").getKeys; +const childVisitorKeys = require("./visitor-keys"); + +/** The scope class for enum. */ +class EnumScope extends Scope { + constructor(scopeManager, upperScope, block) { + super(scopeManager, "enum", upperScope, block, false); + } +} + +class PatternVisitor extends OriginalPatternVisitor { + Identifier(node) { + super.Identifier(node); + if (node.typeAnnotation) { + this.rightHandNodes.push(node.typeAnnotation); + } + } + + ArrayPattern(node) { + node.elements.forEach(this.visit, this); + if (node.typeAnnotation) { + this.rightHandNodes.push(node.typeAnnotation); + } + } + + ObjectPattern(node) { + node.properties.forEach(this.visit, this); + if (node.typeAnnotation) { + this.rightHandNodes.push(node.typeAnnotation); + } + } +} + +class Referencer extends OriginalReferencer { + constructor(...args) { + super(...args); + this.typeMode = false; + } + + /** + * Override to use PatternVisitor we overrode. + * @param {Identifier} node The Identifier node to visit. + * @param {Object} [options] The flag to visit right-hand side nodes. + * @param {Function} callback The callback function for left-hand side nodes. + * @returns {void} + */ + visitPattern(node, options, callback) { + if (!node) { + return; + } + + if (typeof options === "function") { + callback = options; + options = { processRightHandNodes: false }; + } + + const visitor = new PatternVisitor(this.options, node, callback); + visitor.visit(node); + + if (options.processRightHandNodes) { + visitor.rightHandNodes.forEach(this.visit, this); + } + } + + /** + * Override. + * Visit `node.typeParameters` and `node.returnType` additionally to find `typeof` expressions. + * @param {FunctionDeclaration|FunctionExpression|ArrowFunctionExpression} node The function node to visit. + * @returns {void} + */ + visitFunction(node) { + const { type, id, typeParameters, params, returnType, body } = node; + const scopeManager = this.scopeManager; + const upperScope = this.currentScope(); + + // Process the name. + if (type === "FunctionDeclaration") { + upperScope.__define( + id, + new Definition("FunctionName", id, node, null, null, null) + ); + } else if (type === "FunctionExpression" && id) { + scopeManager.__nestFunctionExpressionNameScope(node); + } + + // Process the type parameters + if (typeParameters) { + this.visit(typeParameters); + } + + // Open the function scope. + scopeManager.__nestFunctionScope(node, this.isInnerMethodDefinition); + const innerScope = this.currentScope(); + + // Process parameter declarations. + for (let i = 0; i < params.length; ++i) { + this.visitPattern( + params[i], + { processRightHandNodes: true }, + (pattern, info) => { + innerScope.__define( + pattern, + new ParameterDefinition( + pattern, + node, + i, + info.rest + ) + ); + this.referencingDefaultValue( + pattern, + info.assignments, + null, + true + ); + } + ); + } + + // Process the return type. + if (returnType) { + this.visit(returnType); + } + + // Process the body. + if (body) { + if (body.type === "BlockStatement") { + this.visitChildren(body); + } else { + this.visit(body); + } + } + + // Close the function scope. + this.close(node); + } + + /** + * Override. + * Ignore it in the type mode. + * @param {Identifier} node The Identifier node to visit. + * @returns {void} + */ + Identifier(node) { + if (this.typeMode) { + return; + } + super.Identifier(node); + } + + /** + * Override. + * Don't make variable if `kind === "type"`. + * It doesn't declare variables but declare types. + * @param {VariableDeclaration} node The VariableDeclaration node to visit. + * @returns {void} + */ + VariableDeclaration(node) { + if (node.kind !== "type") { + super.VariableDeclaration(node); + return; + } + + // To detect typeof. + this.typeMode = true; + this.visitChildren(node); + this.typeMode = false; + } + + /** + * Don't make variable because it declares only types. + * Switch to the type mode and visit child nodes to find `typeof x` expression in type declarations. + * @param {TSInterfaceDeclaration} node The TSInterfaceDeclaration node to visit. + * @returns {void} + */ + TSInterfaceDeclaration(node) { + if (this.typeMode) { + this.visitChildren(node); + } else { + this.typeMode = true; + this.visitChildren(node); + this.typeMode = false; + } + } + + /** + * Switch to the type mode and visit child nodes to find `typeof x` expression in type declarations. + * @param {TSTypeAnnotation} node The TSTypeAnnotation node to visit. + * @returns {void} + */ + TSTypeAnnotation(node) { + if (this.typeMode) { + this.visitChildren(node); + } else { + this.typeMode = true; + this.visitChildren(node); + this.typeMode = false; + } + } + + /** + * Switch to the type mode and visit child nodes to find `typeof x` expression in type declarations. + * @param {TSTypeParameterDeclaration} node The TSTypeParameterDeclaration node to visit. + * @returns {void} + */ + TSTypeParameterDeclaration(node) { + if (this.typeMode) { + this.visitChildren(node); + } else { + this.typeMode = true; + this.visitChildren(node); + this.typeMode = false; + } + } + + /** + * Create reference objects for the references are in `typeof` expression. + * @param {TSTypeQuery} node The TSTypeQuery node to visit. + * @returns {void} + */ + TSTypeQuery(node) { + if (this.typeMode) { + this.typeMode = false; + this.visitChildren(node); + this.typeMode = true; + } else { + this.visitChildren(node); + } + } + + /** + * Create variable object for the enum. + * The enum declaration creates a scope for the enum members. + * + * enum E { + * A, + * B, + * C = A + B // A and B are references to the enum member. + * } + * + * const a = 0 + * enum E { + * A = a // a is above constant. + * } + * + * @param {TSEnumDeclaration} node The TSEnumDeclaration node to visit. + * @returns {void} + */ + TSEnumDeclaration(node) { + const { id, members } = node; + const scopeManager = this.scopeManager; + const scope = this.currentScope(); + + if (id) { + scope.__define(id, new Definition("EnumName", id, node)); + } + + scopeManager.__nestScope(new EnumScope(scopeManager, scope, node)); + for (const member of members) { + this.visit(member); + } + this.close(node); + } + + /** + * Create variable object for the enum member and create reference object for the initializer. + * And visit the initializer. + * + * @param {TSEnumMember} node The TSEnumMember node to visit. + * @returns {void} + */ + TSEnumMember(node) { + const { id, initializer } = node; + const scope = this.currentScope(); + + scope.__define(id, new Definition("EnumMemberName", id, node)); + if (initializer) { + scope.__referencing( + id, + Reference.WRITE, + initializer, + null, + false, + true + ); + this.visit(initializer); + } + } +} + +module.exports = function(ast, parserOptions, extraOptions) { + const options = { + ignoreEval: true, + optimistic: false, + directive: false, + nodejsScope: + ast.sourceType === "script" && + (parserOptions.ecmaFeatures && + parserOptions.ecmaFeatures.globalReturn) === true, + impliedStrict: false, + sourceType: extraOptions.sourceType, + ecmaVersion: parserOptions.ecmaVersion || 2018, + childVisitorKeys, + fallback + }; + + const scopeManager = new escope.ScopeManager(options); + const referencer = new Referencer(options, scopeManager); + + referencer.visit(ast); + + return scopeManager; +}; diff --git a/package.json b/package.json index 2eb62bc..cdecd32 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "version": "20.1.1", "files": [ "parser.js", + "analyze-scope.js", "visitor-keys.js" ], "engines": { @@ -50,6 +51,7 @@ }, "dependencies": { "eslint": "4.19.1", + "eslint-scope": "^4.0.0", "eslint-visitor-keys": "^1.0.0", "typescript-estree": "2.1.0" }, diff --git a/parser.js b/parser.js index 32b4e7c..8e9049f 100644 --- a/parser.js +++ b/parser.js @@ -11,6 +11,7 @@ const parse = require("typescript-estree").parse; const astNodeTypes = require("typescript-estree").AST_NODE_TYPES; const traverser = require("eslint/lib/util/traverser"); +const analyzeScope = require("./analyze-scope"); const visitorKeys = require("./visitor-keys"); //------------------------------------------------------------------------------ @@ -21,16 +22,45 @@ exports.version = require("./package.json").version; exports.parseForESLint = function parseForESLint(code, options) { const ast = parse(code, options); + const extraOptions = { + sourceType: ast.sourceType + }; + traverser.traverse(ast, { enter: node => { - if (node.type === "DeclareFunction" || node.type === "FunctionExpression" || node.type === "FunctionDeclaration") { - if (!node.body) { - node.type = `TSEmptyBody${node.type}`; - } + switch (node.type) { + // Just for backword compatibility. + case "DeclareFunction": + if (!node.body) { + node.type = `TSEmptyBody${node.type}`; + } + break; + + // Function#body cannot be null in ESTree spec. + case "FunctionExpression": + case "FunctionDeclaration": + if (!node.body) { + node.type = `TSEmptyBody${node.type}`; + } + break; + + // Import/Export declarations cannot appear in script. + // But if those appear only in namespace/module blocks, `ast.sourceType` was `"script"`. + // This doesn't modify `ast.sourceType` directly for backrard compatibility. + case "ImportDeclaration": + case "ExportAllDeclaration": + case "ExportDefaultDeclaration": + case "ExportNamedDeclaration": + extraOptions.sourceType = "module"; + break; + + // no default } } }); - return { ast, visitorKeys }; + + const scopeManager = analyzeScope(ast, options, extraOptions); + return { ast, scopeManager, visitorKeys }; }; exports.parse = function(code, options) { diff --git a/tests/fixtures/scope-analysis/535.ts b/tests/fixtures/scope-analysis/535.ts new file mode 100644 index 0000000..07dfa60 --- /dev/null +++ b/tests/fixtures/scope-analysis/535.ts @@ -0,0 +1,3 @@ +function foo({ bar }: { bar: string }) { + bar; +} diff --git a/tests/fixtures/scope-analysis/enum.ts b/tests/fixtures/scope-analysis/enum.ts new file mode 100644 index 0000000..98464d4 --- /dev/null +++ b/tests/fixtures/scope-analysis/enum.ts @@ -0,0 +1,6 @@ +const a: number = 1 +enum E { + A = a, + B = a + 1, + C = A + B +} diff --git a/tests/fixtures/scope-analysis/ignore-type-only-stuff.ts b/tests/fixtures/scope-analysis/ignore-type-only-stuff.ts new file mode 100644 index 0000000..45cabe3 --- /dev/null +++ b/tests/fixtures/scope-analysis/ignore-type-only-stuff.ts @@ -0,0 +1,9 @@ +type A = number +interface B { + prop1: A +} +interface C extends B { + method(a: { b: A }): { c: A } +} + +var a: C diff --git a/tests/fixtures/scope-analysis/type-annotations.ts b/tests/fixtures/scope-analysis/type-annotations.ts new file mode 100644 index 0000000..f5d4e63 --- /dev/null +++ b/tests/fixtures/scope-analysis/type-annotations.ts @@ -0,0 +1,7 @@ +type A = number +var a: { b: A } +class C { + f(a: { b: A }): { b: A } { + return {b: 1} + } +} diff --git a/tests/fixtures/scope-analysis/type-assertions.ts b/tests/fixtures/scope-analysis/type-assertions.ts new file mode 100644 index 0000000..0a4caa7 --- /dev/null +++ b/tests/fixtures/scope-analysis/type-assertions.ts @@ -0,0 +1,3 @@ +type A = number; +a = b; +a = b as A; diff --git a/tests/fixtures/scope-analysis/typeof-in-assertions.ts b/tests/fixtures/scope-analysis/typeof-in-assertions.ts new file mode 100644 index 0000000..023291c --- /dev/null +++ b/tests/fixtures/scope-analysis/typeof-in-assertions.ts @@ -0,0 +1,3 @@ +var obj = { value: 1 } +a = b; +a = b as typeof obj; diff --git a/tests/fixtures/scope-analysis/typeof-in-return-type.ts b/tests/fixtures/scope-analysis/typeof-in-return-type.ts new file mode 100644 index 0000000..15f6b7c --- /dev/null +++ b/tests/fixtures/scope-analysis/typeof-in-return-type.ts @@ -0,0 +1,3 @@ +function f(a: number): typeof a { // this `a` is the parameter `a`. + return 1 +} diff --git a/tests/fixtures/scope-analysis/typeof-in-type-parameters.ts b/tests/fixtures/scope-analysis/typeof-in-type-parameters.ts new file mode 100644 index 0000000..2bf99cb --- /dev/null +++ b/tests/fixtures/scope-analysis/typeof-in-type-parameters.ts @@ -0,0 +1,3 @@ +function g(g: T): number { + return 1 +} diff --git a/tests/fixtures/scope-analysis/typeof-in-var.ts b/tests/fixtures/scope-analysis/typeof-in-var.ts new file mode 100644 index 0000000..3e7d87c --- /dev/null +++ b/tests/fixtures/scope-analysis/typeof-in-var.ts @@ -0,0 +1,3 @@ +var obj = { value: 1 } +var obj2: typeof obj = { value: 2 } +var { value }: typeof obj = { value: 2 } diff --git a/tests/fixtures/scope-analysis/typeof.ts b/tests/fixtures/scope-analysis/typeof.ts new file mode 100644 index 0000000..10e4143 --- /dev/null +++ b/tests/fixtures/scope-analysis/typeof.ts @@ -0,0 +1,2 @@ +var obj = { value: 1 } +type B = typeof obj diff --git a/tests/lib/__snapshots__/scope-analysis.js.snap b/tests/lib/__snapshots__/scope-analysis.js.snap new file mode 100644 index 0000000..25834ae --- /dev/null +++ b/tests/lib/__snapshots__/scope-analysis.js.snap @@ -0,0 +1,2144 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/535.ts 1`] = ` +Object { + "$id": 5, + "block": Object { + "range": Array [ + 0, + 52, + ], + "type": "Program", + }, + "childScopes": Array [ + Object { + "$id": 4, + "block": Object { + "range": Array [ + 0, + 51, + ], + "type": "FunctionDeclaration", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [ + Object { + "$id": 3, + "from": Object { + "$ref": 4, + }, + "identifier": Object { + "name": "bar", + "range": Array [ + 45, + 48, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 2, + }, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [], + "type": "function", + "upperScope": Object { + "$ref": 5, + }, + "variableMap": Object { + "arguments": Object { + "$ref": 1, + }, + "bar": Object { + "$ref": 2, + }, + }, + "variableScope": Object { + "$ref": 4, + }, + "variables": Array [ + Object { + "$id": 1, + "defs": Array [], + "identifiers": Array [], + "name": "arguments", + "references": Array [], + "scope": Object { + "$ref": 4, + }, + }, + Object { + "$id": 2, + "defs": Array [ + Object { + "name": Object { + "name": "bar", + "range": Array [ + 15, + 18, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 0, + 51, + ], + "type": "FunctionDeclaration", + }, + "parent": null, + "type": "Parameter", + }, + ], + "identifiers": Array [ + Object { + "name": "bar", + "range": Array [ + 15, + 18, + ], + "type": "Identifier", + }, + ], + "name": "bar", + "references": Array [ + Object { + "$ref": 3, + }, + ], + "scope": Object { + "$ref": 4, + }, + }, + ], + }, + ], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [], + "throughReferences": Array [], + "type": "global", + "upperScope": null, + "variableMap": Object { + "foo": Object { + "$ref": 0, + }, + }, + "variableScope": Object { + "$ref": 5, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "foo", + "range": Array [ + 9, + 12, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 0, + 51, + ], + "type": "FunctionDeclaration", + }, + "parent": null, + "type": "FunctionName", + }, + ], + "identifiers": Array [ + Object { + "name": "foo", + "range": Array [ + 9, + 12, + ], + "type": "Identifier", + }, + ], + "name": "foo", + "references": Array [], + "scope": Object { + "$ref": 5, + }, + }, + ], +} +`; + +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/enum.ts 1`] = ` +Object { + "$id": 14, + "block": Object { + "range": Array [ + 0, + 71, + ], + "type": "Program", + }, + "childScopes": Array [ + Object { + "$id": 13, + "block": Object { + "range": Array [ + 20, + 70, + ], + "type": "TSEnumDeclaration", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [ + Object { + "$id": 6, + "from": Object { + "$ref": 13, + }, + "identifier": Object { + "name": "A", + "range": Array [ + 33, + 34, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 3, + }, + "writeExpr": Object { + "name": "a", + "range": Array [ + 37, + 38, + ], + "type": "Identifier", + }, + }, + Object { + "$id": 7, + "from": Object { + "$ref": 13, + }, + "identifier": Object { + "name": "a", + "range": Array [ + 37, + 38, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + Object { + "$id": 8, + "from": Object { + "$ref": 13, + }, + "identifier": Object { + "name": "B", + "range": Array [ + 44, + 45, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 4, + }, + "writeExpr": Object { + "range": Array [ + 48, + 53, + ], + "type": "BinaryExpression", + }, + }, + Object { + "$id": 9, + "from": Object { + "$ref": 13, + }, + "identifier": Object { + "name": "a", + "range": Array [ + 48, + 49, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + Object { + "$id": 10, + "from": Object { + "$ref": 13, + }, + "identifier": Object { + "name": "C", + "range": Array [ + 59, + 60, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 5, + }, + "writeExpr": Object { + "range": Array [ + 63, + 68, + ], + "type": "BinaryExpression", + }, + }, + Object { + "$id": 11, + "from": Object { + "$ref": 13, + }, + "identifier": Object { + "name": "A", + "range": Array [ + 63, + 64, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 3, + }, + "writeExpr": undefined, + }, + Object { + "$id": 12, + "from": Object { + "$ref": 13, + }, + "identifier": Object { + "name": "B", + "range": Array [ + 67, + 68, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 4, + }, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [ + Object { + "$ref": 7, + }, + Object { + "$ref": 9, + }, + ], + "type": "enum", + "upperScope": Object { + "$ref": 14, + }, + "variableMap": Object { + "A": Object { + "$ref": 3, + }, + "B": Object { + "$ref": 4, + }, + "C": Object { + "$ref": 5, + }, + }, + "variableScope": Object { + "$ref": 14, + }, + "variables": Array [ + Object { + "$id": 3, + "defs": Array [ + Object { + "name": Object { + "name": "A", + "range": Array [ + 33, + 34, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 33, + 38, + ], + "type": "TSEnumMember", + }, + "parent": undefined, + "type": "EnumMemberName", + }, + ], + "identifiers": Array [ + Object { + "name": "A", + "range": Array [ + 33, + 34, + ], + "type": "Identifier", + }, + ], + "name": "A", + "references": Array [ + Object { + "$ref": 6, + }, + Object { + "$ref": 11, + }, + ], + "scope": Object { + "$ref": 13, + }, + }, + Object { + "$id": 4, + "defs": Array [ + Object { + "name": Object { + "name": "B", + "range": Array [ + 44, + 45, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 44, + 53, + ], + "type": "TSEnumMember", + }, + "parent": undefined, + "type": "EnumMemberName", + }, + ], + "identifiers": Array [ + Object { + "name": "B", + "range": Array [ + 44, + 45, + ], + "type": "Identifier", + }, + ], + "name": "B", + "references": Array [ + Object { + "$ref": 8, + }, + Object { + "$ref": 12, + }, + ], + "scope": Object { + "$ref": 13, + }, + }, + Object { + "$id": 5, + "defs": Array [ + Object { + "name": Object { + "name": "C", + "range": Array [ + 59, + 60, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 59, + 68, + ], + "type": "TSEnumMember", + }, + "parent": undefined, + "type": "EnumMemberName", + }, + ], + "identifiers": Array [ + Object { + "name": "C", + "range": Array [ + 59, + 60, + ], + "type": "Identifier", + }, + ], + "name": "C", + "references": Array [ + Object { + "$ref": 10, + }, + ], + "scope": Object { + "$ref": 13, + }, + }, + ], + }, + ], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [ + Object { + "$id": 2, + "from": Object { + "$ref": 14, + }, + "identifier": Object { + "name": "a", + "range": Array [ + 6, + 15, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": Object { + "range": Array [ + 18, + 19, + ], + "type": "Literal", + }, + }, + ], + "throughReferences": Array [], + "type": "global", + "upperScope": null, + "variableMap": Object { + "E": Object { + "$ref": 1, + }, + "a": Object { + "$ref": 0, + }, + }, + "variableScope": Object { + "$ref": 14, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "a", + "range": Array [ + 6, + 15, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 6, + 19, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 0, + 19, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "identifiers": Array [ + Object { + "name": "a", + "range": Array [ + 6, + 15, + ], + "type": "Identifier", + }, + ], + "name": "a", + "references": Array [ + Object { + "$ref": 2, + }, + Object { + "$ref": 7, + }, + Object { + "$ref": 9, + }, + ], + "scope": Object { + "$ref": 14, + }, + }, + Object { + "$id": 1, + "defs": Array [ + Object { + "name": Object { + "name": "E", + "range": Array [ + 25, + 26, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 20, + 70, + ], + "type": "TSEnumDeclaration", + }, + "parent": undefined, + "type": "EnumName", + }, + ], + "identifiers": Array [ + Object { + "name": "E", + "range": Array [ + 25, + 26, + ], + "type": "Identifier", + }, + ], + "name": "E", + "references": Array [], + "scope": Object { + "$ref": 14, + }, + }, + ], +} +`; + +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/ignore-type-only-stuff.ts 1`] = ` +Object { + "$id": 1, + "block": Object { + "range": Array [ + 0, + 115, + ], + "type": "Program", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [], + "throughReferences": Array [], + "type": "global", + "upperScope": null, + "variableMap": Object { + "a": Object { + "$ref": 0, + }, + }, + "variableScope": Object { + "$ref": 1, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "a", + "range": Array [ + 110, + 114, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 110, + 114, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 106, + 114, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "identifiers": Array [ + Object { + "name": "a", + "range": Array [ + 110, + 114, + ], + "type": "Identifier", + }, + ], + "name": "a", + "references": Array [], + "scope": Object { + "$ref": 1, + }, + }, + ], +} +`; + +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/type-annotations.ts 1`] = ` +Object { + "$id": 7, + "block": Object { + "range": Array [ + 0, + 103, + ], + "type": "Program", + }, + "childScopes": Array [ + Object { + "$id": 6, + "block": Object { + "range": Array [ + 32, + 102, + ], + "type": "ClassDeclaration", + }, + "childScopes": Array [ + Object { + "$id": 5, + "block": Object { + "range": Array [ + 47, + 100, + ], + "type": "FunctionExpression", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": true, + "references": Array [], + "throughReferences": Array [], + "type": "function", + "upperScope": Object { + "$ref": 6, + }, + "variableMap": Object { + "a": Object { + "$ref": 4, + }, + "arguments": Object { + "$ref": 3, + }, + }, + "variableScope": Object { + "$ref": 5, + }, + "variables": Array [ + Object { + "$id": 3, + "defs": Array [], + "identifiers": Array [], + "name": "arguments", + "references": Array [], + "scope": Object { + "$ref": 5, + }, + }, + Object { + "$id": 4, + "defs": Array [ + Object { + "name": Object { + "name": "a", + "range": Array [ + 48, + 59, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 47, + 100, + ], + "type": "FunctionExpression", + }, + "parent": null, + "type": "Parameter", + }, + ], + "identifiers": Array [ + Object { + "name": "a", + "range": Array [ + 48, + 59, + ], + "type": "Identifier", + }, + ], + "name": "a", + "references": Array [], + "scope": Object { + "$ref": 5, + }, + }, + ], + }, + ], + "functionExpressionScope": false, + "isStrict": true, + "references": Array [], + "throughReferences": Array [], + "type": "class", + "upperScope": Object { + "$ref": 7, + }, + "variableMap": Object { + "C": Object { + "$ref": 2, + }, + }, + "variableScope": Object { + "$ref": 7, + }, + "variables": Array [ + Object { + "$id": 2, + "defs": Array [ + Object { + "name": Object { + "name": "C", + "range": Array [ + 38, + 39, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 32, + 102, + ], + "type": "ClassDeclaration", + }, + "parent": undefined, + "type": "ClassName", + }, + ], + "identifiers": Array [ + Object { + "name": "C", + "range": Array [ + 38, + 39, + ], + "type": "Identifier", + }, + ], + "name": "C", + "references": Array [], + "scope": Object { + "$ref": 6, + }, + }, + ], + }, + ], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [], + "throughReferences": Array [], + "type": "global", + "upperScope": null, + "variableMap": Object { + "C": Object { + "$ref": 1, + }, + "a": Object { + "$ref": 0, + }, + }, + "variableScope": Object { + "$ref": 7, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "a", + "range": Array [ + 20, + 31, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 20, + 31, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 16, + 31, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "identifiers": Array [ + Object { + "name": "a", + "range": Array [ + 20, + 31, + ], + "type": "Identifier", + }, + ], + "name": "a", + "references": Array [], + "scope": Object { + "$ref": 7, + }, + }, + Object { + "$id": 1, + "defs": Array [ + Object { + "name": Object { + "name": "C", + "range": Array [ + 38, + 39, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 32, + 102, + ], + "type": "ClassDeclaration", + }, + "parent": null, + "type": "ClassName", + }, + ], + "identifiers": Array [ + Object { + "name": "C", + "range": Array [ + 38, + 39, + ], + "type": "Identifier", + }, + ], + "name": "C", + "references": Array [], + "scope": Object { + "$ref": 7, + }, + }, + ], +} +`; + +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/type-assertions.ts 1`] = ` +Object { + "$id": 4, + "block": Object { + "range": Array [ + 0, + 40, + ], + "type": "Program", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [ + Object { + "$id": 0, + "from": Object { + "$ref": 4, + }, + "identifier": Object { + "name": "a", + "range": Array [ + 17, + 18, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": null, + "writeExpr": Object { + "range": Array [ + 21, + 26, + ], + "type": "TSTypeAssertionExpression", + }, + }, + Object { + "$id": 1, + "from": Object { + "$ref": 4, + }, + "identifier": Object { + "name": "b", + "range": Array [ + 25, + 26, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": null, + "writeExpr": undefined, + }, + Object { + "$id": 2, + "from": Object { + "$ref": 4, + }, + "identifier": Object { + "name": "a", + "range": Array [ + 28, + 29, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": null, + "writeExpr": Object { + "range": Array [ + 32, + 38, + ], + "type": "TSAsExpression", + }, + }, + Object { + "$id": 3, + "from": Object { + "$ref": 4, + }, + "identifier": Object { + "name": "b", + "range": Array [ + 32, + 33, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": null, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [ + Object { + "$ref": 0, + }, + Object { + "$ref": 1, + }, + Object { + "$ref": 2, + }, + Object { + "$ref": 3, + }, + ], + "type": "global", + "upperScope": null, + "variableMap": Object {}, + "variableScope": Object { + "$ref": 4, + }, + "variables": Array [], +} +`; + +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/typeof.ts 1`] = ` +Object { + "$id": 3, + "block": Object { + "range": Array [ + 0, + 43, + ], + "type": "Program", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [ + Object { + "$id": 1, + "from": Object { + "$ref": 3, + }, + "identifier": Object { + "name": "obj", + "range": Array [ + 4, + 7, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": Object { + "range": Array [ + 10, + 22, + ], + "type": "ObjectExpression", + }, + }, + Object { + "$id": 2, + "from": Object { + "$ref": 3, + }, + "identifier": Object { + "name": "obj", + "range": Array [ + 39, + 42, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [], + "type": "global", + "upperScope": null, + "variableMap": Object { + "obj": Object { + "$ref": 0, + }, + }, + "variableScope": Object { + "$ref": 3, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "obj", + "range": Array [ + 4, + 7, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 4, + 22, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 0, + 22, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "identifiers": Array [ + Object { + "name": "obj", + "range": Array [ + 4, + 7, + ], + "type": "Identifier", + }, + ], + "name": "obj", + "references": Array [ + Object { + "$ref": 1, + }, + Object { + "$ref": 2, + }, + ], + "scope": Object { + "$ref": 3, + }, + }, + ], +} +`; + +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/typeof-in-assertions.ts 1`] = ` +Object { + "$id": 8, + "block": Object { + "range": Array [ + 0, + 63, + ], + "type": "Program", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [ + Object { + "$id": 1, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "obj", + "range": Array [ + 4, + 7, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": Object { + "range": Array [ + 10, + 22, + ], + "type": "ObjectExpression", + }, + }, + Object { + "$id": 2, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "a", + "range": Array [ + 23, + 24, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": null, + "writeExpr": Object { + "range": Array [ + 27, + 40, + ], + "type": "TSTypeAssertionExpression", + }, + }, + Object { + "$id": 3, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "obj", + "range": Array [ + 35, + 38, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + Object { + "$id": 4, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "b", + "range": Array [ + 39, + 40, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": null, + "writeExpr": undefined, + }, + Object { + "$id": 5, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "a", + "range": Array [ + 42, + 43, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": null, + "writeExpr": Object { + "range": Array [ + 46, + 61, + ], + "type": "TSAsExpression", + }, + }, + Object { + "$id": 6, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "b", + "range": Array [ + 46, + 47, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": null, + "writeExpr": undefined, + }, + Object { + "$id": 7, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "obj", + "range": Array [ + 58, + 61, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [ + Object { + "$ref": 2, + }, + Object { + "$ref": 4, + }, + Object { + "$ref": 5, + }, + Object { + "$ref": 6, + }, + ], + "type": "global", + "upperScope": null, + "variableMap": Object { + "obj": Object { + "$ref": 0, + }, + }, + "variableScope": Object { + "$ref": 8, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "obj", + "range": Array [ + 4, + 7, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 4, + 22, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 0, + 22, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "identifiers": Array [ + Object { + "name": "obj", + "range": Array [ + 4, + 7, + ], + "type": "Identifier", + }, + ], + "name": "obj", + "references": Array [ + Object { + "$ref": 1, + }, + Object { + "$ref": 3, + }, + Object { + "$ref": 7, + }, + ], + "scope": Object { + "$ref": 8, + }, + }, + ], +} +`; + +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/typeof-in-return-type.ts 1`] = ` +Object { + "$id": 5, + "block": Object { + "range": Array [ + 0, + 83, + ], + "type": "Program", + }, + "childScopes": Array [ + Object { + "$id": 4, + "block": Object { + "range": Array [ + 0, + 82, + ], + "type": "FunctionDeclaration", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [ + Object { + "$id": 3, + "from": Object { + "$ref": 4, + }, + "identifier": Object { + "name": "a", + "range": Array [ + 30, + 31, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 2, + }, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [], + "type": "function", + "upperScope": Object { + "$ref": 5, + }, + "variableMap": Object { + "a": Object { + "$ref": 2, + }, + "arguments": Object { + "$ref": 1, + }, + }, + "variableScope": Object { + "$ref": 4, + }, + "variables": Array [ + Object { + "$id": 1, + "defs": Array [], + "identifiers": Array [], + "name": "arguments", + "references": Array [], + "scope": Object { + "$ref": 4, + }, + }, + Object { + "$id": 2, + "defs": Array [ + Object { + "name": Object { + "name": "a", + "range": Array [ + 11, + 20, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 0, + 82, + ], + "type": "FunctionDeclaration", + }, + "parent": null, + "type": "Parameter", + }, + ], + "identifiers": Array [ + Object { + "name": "a", + "range": Array [ + 11, + 20, + ], + "type": "Identifier", + }, + ], + "name": "a", + "references": Array [ + Object { + "$ref": 3, + }, + ], + "scope": Object { + "$ref": 4, + }, + }, + ], + }, + ], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [], + "throughReferences": Array [], + "type": "global", + "upperScope": null, + "variableMap": Object { + "f": Object { + "$ref": 0, + }, + }, + "variableScope": Object { + "$ref": 5, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "f", + "range": Array [ + 9, + 10, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 0, + 82, + ], + "type": "FunctionDeclaration", + }, + "parent": null, + "type": "FunctionName", + }, + ], + "identifiers": Array [ + Object { + "name": "f", + "range": Array [ + 9, + 10, + ], + "type": "Identifier", + }, + ], + "name": "f", + "references": Array [], + "scope": Object { + "$ref": 5, + }, + }, + ], +} +`; + +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/typeof-in-type-parameters.ts 1`] = ` +Object { + "$id": 5, + "block": Object { + "range": Array [ + 0, + 62, + ], + "type": "Program", + }, + "childScopes": Array [ + Object { + "$id": 4, + "block": Object { + "range": Array [ + 0, + 61, + ], + "type": "FunctionDeclaration", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [], + "throughReferences": Array [], + "type": "function", + "upperScope": Object { + "$ref": 5, + }, + "variableMap": Object { + "arguments": Object { + "$ref": 2, + }, + "g": Object { + "$ref": 3, + }, + }, + "variableScope": Object { + "$ref": 4, + }, + "variables": Array [ + Object { + "$id": 2, + "defs": Array [], + "identifiers": Array [], + "name": "arguments", + "references": Array [], + "scope": Object { + "$ref": 4, + }, + }, + Object { + "$id": 3, + "defs": Array [ + Object { + "name": Object { + "name": "g", + "range": Array [ + 31, + 35, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 0, + 61, + ], + "type": "FunctionDeclaration", + }, + "parent": null, + "type": "Parameter", + }, + ], + "identifiers": Array [ + Object { + "name": "g", + "range": Array [ + 31, + 35, + ], + "type": "Identifier", + }, + ], + "name": "g", + "references": Array [], + "scope": Object { + "$ref": 4, + }, + }, + ], + }, + ], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [ + Object { + "$id": 1, + "from": Object { + "$ref": 5, + }, + "identifier": Object { + "name": "g", + "range": Array [ + 28, + 29, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [], + "type": "global", + "upperScope": null, + "variableMap": Object { + "g": Object { + "$ref": 0, + }, + }, + "variableScope": Object { + "$ref": 5, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "g", + "range": Array [ + 9, + 10, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 0, + 61, + ], + "type": "FunctionDeclaration", + }, + "parent": null, + "type": "FunctionName", + }, + ], + "identifiers": Array [ + Object { + "name": "g", + "range": Array [ + 9, + 10, + ], + "type": "Identifier", + }, + ], + "name": "g", + "references": Array [ + Object { + "$ref": 1, + }, + ], + "scope": Object { + "$ref": 5, + }, + }, + ], +} +`; + +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/typeof-in-var.ts 1`] = ` +Object { + "$id": 8, + "block": Object { + "range": Array [ + 0, + 100, + ], + "type": "Program", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [ + Object { + "$id": 3, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "obj", + "range": Array [ + 4, + 7, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": Object { + "range": Array [ + 10, + 22, + ], + "type": "ObjectExpression", + }, + }, + Object { + "$id": 4, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "obj2", + "range": Array [ + 27, + 43, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 1, + }, + "writeExpr": Object { + "range": Array [ + 46, + 58, + ], + "type": "ObjectExpression", + }, + }, + Object { + "$id": 5, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "obj", + "range": Array [ + 40, + 43, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + Object { + "$id": 6, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "value", + "range": Array [ + 65, + 70, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 2, + }, + "writeExpr": Object { + "range": Array [ + 87, + 99, + ], + "type": "ObjectExpression", + }, + }, + Object { + "$id": 7, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "obj", + "range": Array [ + 81, + 84, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [], + "type": "global", + "upperScope": null, + "variableMap": Object { + "obj": Object { + "$ref": 0, + }, + "obj2": Object { + "$ref": 1, + }, + "value": Object { + "$ref": 2, + }, + }, + "variableScope": Object { + "$ref": 8, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "obj", + "range": Array [ + 4, + 7, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 4, + 22, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 0, + 22, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "identifiers": Array [ + Object { + "name": "obj", + "range": Array [ + 4, + 7, + ], + "type": "Identifier", + }, + ], + "name": "obj", + "references": Array [ + Object { + "$ref": 3, + }, + Object { + "$ref": 5, + }, + Object { + "$ref": 7, + }, + ], + "scope": Object { + "$ref": 8, + }, + }, + Object { + "$id": 1, + "defs": Array [ + Object { + "name": Object { + "name": "obj2", + "range": Array [ + 27, + 43, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 27, + 58, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 23, + 58, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "identifiers": Array [ + Object { + "name": "obj2", + "range": Array [ + 27, + 43, + ], + "type": "Identifier", + }, + ], + "name": "obj2", + "references": Array [ + Object { + "$ref": 4, + }, + ], + "scope": Object { + "$ref": 8, + }, + }, + Object { + "$id": 2, + "defs": Array [ + Object { + "name": Object { + "name": "value", + "range": Array [ + 65, + 70, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 63, + 99, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 59, + 99, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "identifiers": Array [ + Object { + "name": "value", + "range": Array [ + 65, + 70, + ], + "type": "Identifier", + }, + ], + "name": "value", + "references": Array [ + Object { + "$ref": 6, + }, + ], + "scope": Object { + "$ref": 8, + }, + }, + ], +} +`; diff --git a/tests/lib/scope-analysis.js b/tests/lib/scope-analysis.js new file mode 100644 index 0000000..bbb3d4a --- /dev/null +++ b/tests/lib/scope-analysis.js @@ -0,0 +1,171 @@ +/** + * @fileoverview Tests for TypeScript-specific scope analysis + * @author Toru Nagashima + */ +"use strict"; + +const fs = require("fs"); +const path = require("path"); +const { parseForESLint } = require("../.."); + +/** Reference resolver. */ +class ReferenceResolver { + constructor() { + this.map = new Map(); + } + + resolve(obj, properties) { + const resolved = Object.assign({ $id: this.map.size }, properties); + this.map.set(obj, resolved); + return resolved; + } + + ref(obj) { + if (typeof obj !== "object" || obj === null) { + return obj; + } + + const { map } = this; + return { + get $ref() { + return map.get(obj).$id; + } + }; + } +} + +/** + * 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. + */ +function nodeToJSON(node) { + if (!node) { + return node; + } + + const { type, name, range } = node; + if (node.type === "Identifier") { + return { type, 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. + */ +function variableToJSON(variable, resolver) { + const { name } = variable; + const defs = variable.defs.map(d => ({ + type: d.type, + name: nodeToJSON(d.name), + node: nodeToJSON(d.node), + parent: nodeToJSON(d.parent) + })); + const identifiers = variable.identifiers.map(nodeToJSON); + const references = variable.references.map(resolver.ref, resolver); + const scope = resolver.ref(variable.scope); + + return resolver.resolve(variable, { + name, + defs, + identifiers, + references, + scope + }); +} + +/** + * 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. + */ +function referenceToJSON(reference, resolver) { + const kind = `${reference.isRead() ? "r" : ""}${reference.isWrite() ? "w" : ""}`; + const from = resolver.ref(reference.from); + const identifier = nodeToJSON(reference.identifier); + const writeExpr = nodeToJSON(reference.writeExpr); + const resolved = resolver.ref(reference.resolved); + + return resolver.resolve(reference, { + kind, + from, + identifier, + writeExpr, + resolved + }); +} + +/** + * Convert a given scope object to JSON object. + * @param {Scope} scope The eslint-scope's scope object. + * @param {ReferenceResolver} resolver The reference resolver. + * @returns {Object} The object that can be used for JSON.stringify. + */ +function scopeToJSON(scope, resolver = new ReferenceResolver()) { + const { type, functionExpressionScope, isStrict } = scope; + const block = nodeToJSON(scope.block); + 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((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 => scopeToJSON(c, resolver)); + + return resolver.resolve(scope, { + type, + functionExpressionScope, + isStrict, + block, + variables, + references, + variableMap, + throughReferences, + variableScope, + upperScope, + childScopes + }); +} + +describe("TypeScript scope analysis", () => { + const root = "tests/fixtures/scope-analysis"; + const files = fs.readdirSync(root).map(filename => path.join(root, filename).replace(/\\/g, "/")); + + for (const filePath of files) { + test(filePath, () => { + const code = fs.readFileSync(filePath, "utf8"); + const { scopeManager } = parseForESLint(code, { + loc: false, + range: true, + tokens: false, + ecmaFeatures: {} + }); + const { globalScope } = scopeManager; + + // Do the postprocess to test. + // https://github.com/eslint/eslint/blob/4fe328787dd02d7a1f6fc21167f6175c860825e3/lib/linter.js#L222 + globalScope.through = globalScope.through.filter(reference => { + const name = reference.identifier.name; + const variable = globalScope.set.get(name); + if (variable) { + reference.resolved = variable; + variable.references.push(reference); + return false; + } + return true; + }); + + const scopeTree = scopeToJSON(globalScope); + expect(scopeTree).toMatchSnapshot(); + }); + } +}); diff --git a/visitor-keys.js b/visitor-keys.js index 18cf592..9f58086 100644 --- a/visitor-keys.js +++ b/visitor-keys.js @@ -42,6 +42,8 @@ module.exports = Evk.unionWith({ TSConstructorType: ["typeAnnotation", "parameters"], TSConstructSignature: ["typeAnnotation", "typeParameters"], TSDeclareKeyword: [], + TSEmptyBodyFunctionDeclaration: ["id", "params", "body", "returnType", "typeParameters"], + TSEmptyBodyFunctionExpression: ["id", "params", "body", "returnType", "typeParameters"], TSEnumDeclaration: ["members"], TSEnumMember: ["initializer"], TSExportAssignment: ["expression"], @@ -81,6 +83,7 @@ module.exports = Evk.unionWith({ TSTypeParameterInstantiation: ["params"], TSTypePredicate: ["typeAnnotation", "parameterName"], TSTypeReference: ["typeName", "typeParameters"], + TSTypeQuery: ["exprName"], TSUnionType: ["types"], TSUndefinedKeyword: [], TSVoidKeyword: [] From 1cfbb6e8178313494c199db4bac7159af7f5b34c Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 6 Nov 2018 15:09:43 +0900 Subject: [PATCH 02/29] add computed-properties-in-type fixture --- analyze-scope.js | 48 +++- .../computed-properties-in-type.ts | 5 + .../lib/__snapshots__/scope-analysis.js.snap | 267 ++++++++++++++++++ 3 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/scope-analysis/computed-properties-in-type.ts diff --git a/analyze-scope.js b/analyze-scope.js index 93be3e9..8abadad 100644 --- a/analyze-scope.js +++ b/analyze-scope.js @@ -224,7 +224,7 @@ class Referencer extends OriginalReferencer { } /** - * Create reference objects for the references are in `typeof` expression. + * Create reference objects for the references in `typeof` expression. * @param {TSTypeQuery} node The TSTypeQuery node to visit. * @returns {void} */ @@ -238,6 +238,52 @@ class Referencer extends OriginalReferencer { } } + /** + * Create reference objects for the references in computed keys. + * @param {TSPropertySignature} node The TSPropertySignature node to visit. + * @returns {void} + */ + TSPropertySignature(node) { + const upperTypeMode = this.typeMode; + const { computed, key, typeAnnotation, initializer } = node; + + if (computed) { + this.typeMode = false; + this.visit(key); + this.typeMode = true; + } else { + this.typeMode = true; + this.visit(key); + } + this.visit(typeAnnotation); + this.visit(initializer); + + this.typeMode = upperTypeMode; + } + + /** + * Create reference objects for the references in computed keys. + * @param {TSMethodSignature} node The TSMethodSignature node to visit. + * @returns {void} + */ + TSMethodSignature(node) { + const upperTypeMode = this.typeMode; + const { computed, key, params, typeAnnotation } = node; + + if (computed) { + this.typeMode = false; + this.visit(key); + this.typeMode = true; + } else { + this.typeMode = true; + this.visit(key); + } + this.visit(params); + this.visit(typeAnnotation); + + this.typeMode = upperTypeMode; + } + /** * Create variable object for the enum. * The enum declaration creates a scope for the enum members. diff --git a/tests/fixtures/scope-analysis/computed-properties-in-type.ts b/tests/fixtures/scope-analysis/computed-properties-in-type.ts new file mode 100644 index 0000000..46f7294 --- /dev/null +++ b/tests/fixtures/scope-analysis/computed-properties-in-type.ts @@ -0,0 +1,5 @@ +const s1 = Symbol(), s2 = Symbol() +type A = { + [s1]: number + [s2](s1: number, s2: number): number; +} diff --git a/tests/lib/__snapshots__/scope-analysis.js.snap b/tests/lib/__snapshots__/scope-analysis.js.snap index 25834ae..5ce3711 100644 --- a/tests/lib/__snapshots__/scope-analysis.js.snap +++ b/tests/lib/__snapshots__/scope-analysis.js.snap @@ -175,6 +175,273 @@ Object { } `; +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/computed-properties-in-type.ts 1`] = ` +Object { + "$id": 8, + "block": Object { + "range": Array [ + 0, + 107, + ], + "type": "Program", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [ + Object { + "$id": 2, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "s1", + "range": Array [ + 6, + 8, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": Object { + "range": Array [ + 11, + 19, + ], + "type": "CallExpression", + }, + }, + Object { + "$id": 3, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "Symbol", + "range": Array [ + 11, + 17, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": null, + "writeExpr": undefined, + }, + Object { + "$id": 4, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "s2", + "range": Array [ + 21, + 23, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 1, + }, + "writeExpr": Object { + "range": Array [ + 26, + 34, + ], + "type": "CallExpression", + }, + }, + Object { + "$id": 5, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "Symbol", + "range": Array [ + 26, + 32, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": null, + "writeExpr": undefined, + }, + Object { + "$id": 6, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "s1", + "range": Array [ + 51, + 53, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + Object { + "$id": 7, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "s2", + "range": Array [ + 68, + 70, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 1, + }, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [ + Object { + "$ref": 3, + }, + Object { + "$ref": 5, + }, + ], + "type": "global", + "upperScope": null, + "variableMap": Object { + "s1": Object { + "$ref": 0, + }, + "s2": Object { + "$ref": 1, + }, + }, + "variableScope": Object { + "$ref": 8, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "s1", + "range": Array [ + 6, + 8, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 6, + 19, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 0, + 34, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "identifiers": Array [ + Object { + "name": "s1", + "range": Array [ + 6, + 8, + ], + "type": "Identifier", + }, + ], + "name": "s1", + "references": Array [ + Object { + "$ref": 2, + }, + Object { + "$ref": 6, + }, + ], + "scope": Object { + "$ref": 8, + }, + }, + Object { + "$id": 1, + "defs": Array [ + Object { + "name": Object { + "name": "s2", + "range": Array [ + 21, + 23, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 21, + 34, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 0, + 34, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "identifiers": Array [ + Object { + "name": "s2", + "range": Array [ + 21, + 23, + ], + "type": "Identifier", + }, + ], + "name": "s2", + "references": Array [ + Object { + "$ref": 4, + }, + Object { + "$ref": 7, + }, + ], + "scope": Object { + "$ref": 8, + }, + }, + ], +} +`; + exports[`TypeScript scope analysis tests/fixtures/scope-analysis/enum.ts 1`] = ` Object { "$id": 14, From 1ec6b3a8b9890b385484758ea582a507ea5d7e33 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 6 Nov 2018 15:21:49 +0900 Subject: [PATCH 03/29] add computed-properties-in-interface fixture --- .../computed-properties-in-interface.ts | 5 + .../lib/__snapshots__/scope-analysis.js.snap | 267 ++++++++++++++++++ 2 files changed, 272 insertions(+) create mode 100644 tests/fixtures/scope-analysis/computed-properties-in-interface.ts diff --git a/tests/fixtures/scope-analysis/computed-properties-in-interface.ts b/tests/fixtures/scope-analysis/computed-properties-in-interface.ts new file mode 100644 index 0000000..b821bc7 --- /dev/null +++ b/tests/fixtures/scope-analysis/computed-properties-in-interface.ts @@ -0,0 +1,5 @@ +const s1 = Symbol(), s2 = Symbol() +interface A { + [s1]: number + [s2](s1: number, s2: number): number; +} diff --git a/tests/lib/__snapshots__/scope-analysis.js.snap b/tests/lib/__snapshots__/scope-analysis.js.snap index 5ce3711..cafdb59 100644 --- a/tests/lib/__snapshots__/scope-analysis.js.snap +++ b/tests/lib/__snapshots__/scope-analysis.js.snap @@ -175,6 +175,273 @@ Object { } `; +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/computed-properties-in-interface.ts 1`] = ` +Object { + "$id": 8, + "block": Object { + "range": Array [ + 0, + 110, + ], + "type": "Program", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [ + Object { + "$id": 2, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "s1", + "range": Array [ + 6, + 8, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": Object { + "range": Array [ + 11, + 19, + ], + "type": "CallExpression", + }, + }, + Object { + "$id": 3, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "Symbol", + "range": Array [ + 11, + 17, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": null, + "writeExpr": undefined, + }, + Object { + "$id": 4, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "s2", + "range": Array [ + 21, + 23, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 1, + }, + "writeExpr": Object { + "range": Array [ + 26, + 34, + ], + "type": "CallExpression", + }, + }, + Object { + "$id": 5, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "Symbol", + "range": Array [ + 26, + 32, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": null, + "writeExpr": undefined, + }, + Object { + "$id": 6, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "s1", + "range": Array [ + 54, + 56, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + Object { + "$id": 7, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "s2", + "range": Array [ + 71, + 73, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 1, + }, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [ + Object { + "$ref": 3, + }, + Object { + "$ref": 5, + }, + ], + "type": "global", + "upperScope": null, + "variableMap": Object { + "s1": Object { + "$ref": 0, + }, + "s2": Object { + "$ref": 1, + }, + }, + "variableScope": Object { + "$ref": 8, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "s1", + "range": Array [ + 6, + 8, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 6, + 19, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 0, + 34, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "identifiers": Array [ + Object { + "name": "s1", + "range": Array [ + 6, + 8, + ], + "type": "Identifier", + }, + ], + "name": "s1", + "references": Array [ + Object { + "$ref": 2, + }, + Object { + "$ref": 6, + }, + ], + "scope": Object { + "$ref": 8, + }, + }, + Object { + "$id": 1, + "defs": Array [ + Object { + "name": Object { + "name": "s2", + "range": Array [ + 21, + 23, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 21, + 34, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 0, + 34, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "identifiers": Array [ + Object { + "name": "s2", + "range": Array [ + 21, + 23, + ], + "type": "Identifier", + }, + ], + "name": "s2", + "references": Array [ + Object { + "$ref": 4, + }, + Object { + "$ref": 7, + }, + ], + "scope": Object { + "$ref": 8, + }, + }, + ], +} +`; + exports[`TypeScript scope analysis tests/fixtures/scope-analysis/computed-properties-in-type.ts 1`] = ` Object { "$id": 8, From 80b13cfad17d92db6156038bf87155308dc34953 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 6 Nov 2018 17:11:20 +0900 Subject: [PATCH 04/29] add function-overload fixture --- analyze-scope.js | 43 +++- .../scope-analysis/function-overload-2.ts | 2 + .../scope-analysis/function-overload.ts | 5 + .../lib/__snapshots__/scope-analysis.js.snap | 220 ++++++++++++++++++ 4 files changed, 264 insertions(+), 6 deletions(-) create mode 100644 tests/fixtures/scope-analysis/function-overload-2.ts create mode 100644 tests/fixtures/scope-analysis/function-overload.ts diff --git a/analyze-scope.js b/analyze-scope.js index 8abadad..620827f 100644 --- a/analyze-scope.js +++ b/analyze-scope.js @@ -89,6 +89,17 @@ class Referencer extends OriginalReferencer { id, new Definition("FunctionName", id, node, null, null, null) ); + + // Remove overload definition to avoid confusion of no-redeclare rule. + const { defs, identifiers } = upperScope.set.get(id.name); + for (let i = 0; i < defs.length; ++i) { + const def = defs[i]; + if (def.type === "FunctionName" && def.node.type === "TSEmptyBodyFunctionDeclaration") { + defs.splice(i, 1); + identifiers.splice(i, 1); + break; + } + } } else if (type === "FunctionExpression" && id) { scopeManager.__nestFunctionExpressionNameScope(node); } @@ -133,12 +144,10 @@ class Referencer extends OriginalReferencer { } // Process the body. - if (body) { - if (body.type === "BlockStatement") { - this.visitChildren(body); - } else { - this.visit(body); - } + if (body.type === "BlockStatement") { + this.visitChildren(body); + } else { + this.visit(body); } // Close the function scope. @@ -177,6 +186,28 @@ class Referencer extends OriginalReferencer { this.typeMode = false; } + /** + * Define the variable of this function declaration only once. + * Because to avoid confusion of `no-redeclare` rule by overloading. + * @param {TSEmptyBodyFunctionDeclaration} node The TSEmptyBodyFunctionDeclaration node to visit. + * @returns {void} + */ + TSEmptyBodyFunctionDeclaration(node) { + const { id } = node; + const scope = this.currentScope(); + + // Ignore this if other overloadings have already existed. + const variable = scope.set.get(id.name); + const defs = variable && variable.defs; + const existed = defs && defs.some(d => d.type === "FunctionName"); + if (!existed) { + scope.__define( + id, + new Definition("FunctionName", id, node, null, null, null) + ); + } + } + /** * Don't make variable because it declares only types. * Switch to the type mode and visit child nodes to find `typeof x` expression in type declarations. diff --git a/tests/fixtures/scope-analysis/function-overload-2.ts b/tests/fixtures/scope-analysis/function-overload-2.ts new file mode 100644 index 0000000..548267a --- /dev/null +++ b/tests/fixtures/scope-analysis/function-overload-2.ts @@ -0,0 +1,2 @@ +function f(): void +function f(a: number): void diff --git a/tests/fixtures/scope-analysis/function-overload.ts b/tests/fixtures/scope-analysis/function-overload.ts new file mode 100644 index 0000000..7d23f50 --- /dev/null +++ b/tests/fixtures/scope-analysis/function-overload.ts @@ -0,0 +1,5 @@ +function f(): void +function f(a: number): void +function f(a?: number): void { + // do something. +} diff --git a/tests/lib/__snapshots__/scope-analysis.js.snap b/tests/lib/__snapshots__/scope-analysis.js.snap index cafdb59..f3e4d28 100644 --- a/tests/lib/__snapshots__/scope-analysis.js.snap +++ b/tests/lib/__snapshots__/scope-analysis.js.snap @@ -1193,6 +1193,226 @@ Object { } `; +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/function-overload.ts 1`] = ` +Object { + "$id": 4, + "block": Object { + "range": Array [ + 0, + 101, + ], + "type": "Program", + }, + "childScopes": Array [ + Object { + "$id": 3, + "block": Object { + "range": Array [ + 47, + 100, + ], + "type": "FunctionDeclaration", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [], + "throughReferences": Array [], + "type": "function", + "upperScope": Object { + "$ref": 4, + }, + "variableMap": Object { + "a": Object { + "$ref": 2, + }, + "arguments": Object { + "$ref": 1, + }, + }, + "variableScope": Object { + "$ref": 3, + }, + "variables": Array [ + Object { + "$id": 1, + "defs": Array [], + "identifiers": Array [], + "name": "arguments", + "references": Array [], + "scope": Object { + "$ref": 3, + }, + }, + Object { + "$id": 2, + "defs": Array [ + Object { + "name": Object { + "name": "a", + "range": Array [ + 58, + 68, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 47, + 100, + ], + "type": "FunctionDeclaration", + }, + "parent": null, + "type": "Parameter", + }, + ], + "identifiers": Array [ + Object { + "name": "a", + "range": Array [ + 58, + 68, + ], + "type": "Identifier", + }, + ], + "name": "a", + "references": Array [], + "scope": Object { + "$ref": 3, + }, + }, + ], + }, + ], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [], + "throughReferences": Array [], + "type": "global", + "upperScope": null, + "variableMap": Object { + "f": Object { + "$ref": 0, + }, + }, + "variableScope": Object { + "$ref": 4, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "f", + "range": Array [ + 56, + 57, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 47, + 100, + ], + "type": "FunctionDeclaration", + }, + "parent": null, + "type": "FunctionName", + }, + ], + "identifiers": Array [ + Object { + "name": "f", + "range": Array [ + 56, + 57, + ], + "type": "Identifier", + }, + ], + "name": "f", + "references": Array [], + "scope": Object { + "$ref": 4, + }, + }, + ], +} +`; + +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/function-overload-2.ts 1`] = ` +Object { + "$id": 1, + "block": Object { + "range": Array [ + 0, + 47, + ], + "type": "Program", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [], + "throughReferences": Array [], + "type": "global", + "upperScope": null, + "variableMap": Object { + "f": Object { + "$ref": 0, + }, + }, + "variableScope": Object { + "$ref": 1, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "f", + "range": Array [ + 9, + 10, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 0, + 18, + ], + "type": "TSEmptyBodyFunctionDeclaration", + }, + "parent": null, + "type": "FunctionName", + }, + ], + "identifiers": Array [ + Object { + "name": "f", + "range": Array [ + 9, + 10, + ], + "type": "Identifier", + }, + ], + "name": "f", + "references": Array [], + "scope": Object { + "$ref": 1, + }, + }, + ], +} +`; + exports[`TypeScript scope analysis tests/fixtures/scope-analysis/ignore-type-only-stuff.ts 1`] = ` Object { "$id": 1, From 3d7cfb266659cc2cfc6bdb337814bfe3b549c6fe Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 6 Nov 2018 18:29:50 +0900 Subject: [PATCH 05/29] add method-overload fixture --- analyze-scope.js | 49 ++- .../scope-analysis/method-overload.ts | 8 + .../lib/__snapshots__/scope-analysis.js.snap | 346 ++++++++++++++++++ 3 files changed, 389 insertions(+), 14 deletions(-) create mode 100644 tests/fixtures/scope-analysis/method-overload.ts diff --git a/analyze-scope.js b/analyze-scope.js index 620827f..be2ce46 100644 --- a/analyze-scope.js +++ b/analyze-scope.js @@ -105,9 +105,7 @@ class Referencer extends OriginalReferencer { } // Process the type parameters - if (typeParameters) { - this.visit(typeParameters); - } + this.visit(typeParameters); // Open the function scope. scopeManager.__nestFunctionScope(node, this.isInnerMethodDefinition); @@ -139,9 +137,7 @@ class Referencer extends OriginalReferencer { } // Process the return type. - if (returnType) { - this.visit(returnType); - } + this.visit(returnType); // Process the body. if (body.type === "BlockStatement") { @@ -156,15 +152,15 @@ class Referencer extends OriginalReferencer { /** * Override. - * Ignore it in the type mode. + * Don't create the reference object in the type mode. * @param {Identifier} node The Identifier node to visit. * @returns {void} */ Identifier(node) { - if (this.typeMode) { - return; + if (!this.typeMode) { + super.Identifier(node); } - super.Identifier(node); + this.visit(node.typeAnnotation); } /** @@ -193,8 +189,9 @@ class Referencer extends OriginalReferencer { * @returns {void} */ TSEmptyBodyFunctionDeclaration(node) { - const { id } = node; + const upperTypeMode = this.typeMode; const scope = this.currentScope(); + const { id, typeParameters, params, returnType } = node; // Ignore this if other overloadings have already existed. const variable = scope.set.get(id.name); @@ -206,6 +203,29 @@ class Referencer extends OriginalReferencer { new Definition("FunctionName", id, node, null, null, null) ); } + + // Find `typeof` expressions. + this.typeMode = true; + this.visit(typeParameters); + params.forEach(this.visit, this); + this.visit(returnType); + this.typeMode = upperTypeMode; + } + + /** + * Create reference objects for the references in parameters and return type. + * @param {TSEmptyBodyFunctionExpression} node The TSEmptyBodyFunctionExpression node to visit. + * @returns {void} + */ + TSEmptyBodyFunctionExpression(node) { + const upperTypeMode = this.typeMode; + const { typeParameters, params, returnType } = node; + + this.typeMode = true; + this.visit(typeParameters); + params.forEach(this.visit, this); + this.visit(returnType); + this.typeMode = upperTypeMode; } /** @@ -299,7 +319,7 @@ class Referencer extends OriginalReferencer { */ TSMethodSignature(node) { const upperTypeMode = this.typeMode; - const { computed, key, params, typeAnnotation } = node; + const { computed, key, typeParameters, params, typeAnnotation } = node; if (computed) { this.typeMode = false; @@ -309,8 +329,9 @@ class Referencer extends OriginalReferencer { this.typeMode = true; this.visit(key); } - this.visit(params); - this.visit(typeAnnotation); + this.visit(typeParameters); + params.forEach(this.visit, this); + this.visit(typeAnnotation); // Maybe returnType? this.typeMode = upperTypeMode; } diff --git a/tests/fixtures/scope-analysis/method-overload.ts b/tests/fixtures/scope-analysis/method-overload.ts new file mode 100644 index 0000000..fd8cbbd --- /dev/null +++ b/tests/fixtures/scope-analysis/method-overload.ts @@ -0,0 +1,8 @@ +const s = Symbol() +class A { + f(): void + f(a: typeof s): void + f(a?: any): void { + // do something. + } +} diff --git a/tests/lib/__snapshots__/scope-analysis.js.snap b/tests/lib/__snapshots__/scope-analysis.js.snap index f3e4d28..19e256f 100644 --- a/tests/lib/__snapshots__/scope-analysis.js.snap +++ b/tests/lib/__snapshots__/scope-analysis.js.snap @@ -1488,6 +1488,352 @@ Object { } `; +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/method-overload.ts 1`] = ` +Object { + "$id": 10, + "block": Object { + "range": Array [ + 0, + 124, + ], + "type": "Program", + }, + "childScopes": Array [ + Object { + "$id": 9, + "block": Object { + "range": Array [ + 19, + 123, + ], + "type": "ClassDeclaration", + }, + "childScopes": Array [ + Object { + "$id": 8, + "block": Object { + "range": Array [ + 73, + 121, + ], + "type": "FunctionExpression", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": true, + "references": Array [], + "throughReferences": Array [], + "type": "function", + "upperScope": Object { + "$ref": 9, + }, + "variableMap": Object { + "a": Object { + "$ref": 7, + }, + "arguments": Object { + "$ref": 6, + }, + }, + "variableScope": Object { + "$ref": 8, + }, + "variables": Array [ + Object { + "$id": 6, + "defs": Array [], + "identifiers": Array [], + "name": "arguments", + "references": Array [], + "scope": Object { + "$ref": 8, + }, + }, + Object { + "$id": 7, + "defs": Array [ + Object { + "name": Object { + "name": "a", + "range": Array [ + 74, + 81, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 73, + 121, + ], + "type": "FunctionExpression", + }, + "parent": null, + "type": "Parameter", + }, + ], + "identifiers": Array [ + Object { + "name": "a", + "range": Array [ + 74, + 81, + ], + "type": "Identifier", + }, + ], + "name": "a", + "references": Array [], + "scope": Object { + "$ref": 8, + }, + }, + ], + }, + ], + "functionExpressionScope": false, + "isStrict": true, + "references": Array [ + Object { + "$id": 5, + "from": Object { + "$ref": 9, + }, + "identifier": Object { + "name": "s", + "range": Array [ + 59, + 60, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [ + Object { + "$ref": 5, + }, + ], + "type": "class", + "upperScope": Object { + "$ref": 10, + }, + "variableMap": Object { + "A": Object { + "$ref": 4, + }, + }, + "variableScope": Object { + "$ref": 10, + }, + "variables": Array [ + Object { + "$id": 4, + "defs": Array [ + Object { + "name": Object { + "name": "A", + "range": Array [ + 25, + 26, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 19, + 123, + ], + "type": "ClassDeclaration", + }, + "parent": undefined, + "type": "ClassName", + }, + ], + "identifiers": Array [ + Object { + "name": "A", + "range": Array [ + 25, + 26, + ], + "type": "Identifier", + }, + ], + "name": "A", + "references": Array [], + "scope": Object { + "$ref": 9, + }, + }, + ], + }, + ], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [ + Object { + "$id": 2, + "from": Object { + "$ref": 10, + }, + "identifier": Object { + "name": "s", + "range": Array [ + 6, + 7, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": Object { + "range": Array [ + 10, + 18, + ], + "type": "CallExpression", + }, + }, + Object { + "$id": 3, + "from": Object { + "$ref": 10, + }, + "identifier": Object { + "name": "Symbol", + "range": Array [ + 10, + 16, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": null, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [ + Object { + "$ref": 3, + }, + ], + "type": "global", + "upperScope": null, + "variableMap": Object { + "A": Object { + "$ref": 1, + }, + "s": Object { + "$ref": 0, + }, + }, + "variableScope": Object { + "$ref": 10, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "s", + "range": Array [ + 6, + 7, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 6, + 18, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 0, + 18, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "identifiers": Array [ + Object { + "name": "s", + "range": Array [ + 6, + 7, + ], + "type": "Identifier", + }, + ], + "name": "s", + "references": Array [ + Object { + "$ref": 2, + }, + Object { + "$ref": 5, + }, + ], + "scope": Object { + "$ref": 10, + }, + }, + Object { + "$id": 1, + "defs": Array [ + Object { + "name": Object { + "name": "A", + "range": Array [ + 25, + 26, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 19, + 123, + ], + "type": "ClassDeclaration", + }, + "parent": null, + "type": "ClassName", + }, + ], + "identifiers": Array [ + Object { + "name": "A", + "range": Array [ + 25, + 26, + ], + "type": "Identifier", + }, + ], + "name": "A", + "references": Array [], + "scope": Object { + "$ref": 10, + }, + }, + ], +} +`; + exports[`TypeScript scope analysis tests/fixtures/scope-analysis/type-annotations.ts 1`] = ` Object { "$id": 7, From f61e133edaca2b12678332637a57efc8b9b4d08e Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 6 Nov 2018 19:30:24 +0900 Subject: [PATCH 06/29] add class-properties fixture --- analyze-scope.js | 21 ++ .../scope-analysis/class-properties.ts | 5 + .../lib/__snapshots__/scope-analysis.js.snap | 289 ++++++++++++++++++ 3 files changed, 315 insertions(+) create mode 100644 tests/fixtures/scope-analysis/class-properties.ts diff --git a/analyze-scope.js b/analyze-scope.js index be2ce46..8dda8be 100644 --- a/analyze-scope.js +++ b/analyze-scope.js @@ -182,6 +182,27 @@ class Referencer extends OriginalReferencer { this.typeMode = false; } + /** + * Don't create the reference object for the key if not computed. + * @param {TSEmptyBodyFunctionDeclaration} node The TSEmptyBodyFunctionDeclaration node to visit. + * @returns {void} + */ + ClassProperty(node) { + const upperTypeMode = this.typeMode; + const { computed, key, typeAnnotation, value } = node; + + if (computed) { + this.typeMode = false; + this.visit(key); + } + this.typeMode = true; + this.visit(typeAnnotation); + this.typeMode = false; + this.visit(value); + + this.typeMode = upperTypeMode; + } + /** * Define the variable of this function declaration only once. * Because to avoid confusion of `no-redeclare` rule by overloading. diff --git a/tests/fixtures/scope-analysis/class-properties.ts b/tests/fixtures/scope-analysis/class-properties.ts new file mode 100644 index 0000000..c446050 --- /dev/null +++ b/tests/fixtures/scope-analysis/class-properties.ts @@ -0,0 +1,5 @@ +const s = Symbol() +class A { + a: typeof s + [s]: number +} diff --git a/tests/lib/__snapshots__/scope-analysis.js.snap b/tests/lib/__snapshots__/scope-analysis.js.snap index 19e256f..2f3d421 100644 --- a/tests/lib/__snapshots__/scope-analysis.js.snap +++ b/tests/lib/__snapshots__/scope-analysis.js.snap @@ -175,6 +175,295 @@ Object { } `; +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/class-properties.ts 1`] = ` +Object { + "$id": 8, + "block": Object { + "range": Array [ + 0, + 63, + ], + "type": "Program", + }, + "childScopes": Array [ + Object { + "$id": 7, + "block": Object { + "range": Array [ + 19, + 62, + ], + "type": "ClassDeclaration", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": true, + "references": Array [ + Object { + "$id": 5, + "from": Object { + "$ref": 7, + }, + "identifier": Object { + "name": "s", + "range": Array [ + 43, + 44, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + Object { + "$id": 6, + "from": Object { + "$ref": 7, + }, + "identifier": Object { + "name": "s", + "range": Array [ + 50, + 51, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [ + Object { + "$ref": 5, + }, + Object { + "$ref": 6, + }, + ], + "type": "class", + "upperScope": Object { + "$ref": 8, + }, + "variableMap": Object { + "A": Object { + "$ref": 4, + }, + }, + "variableScope": Object { + "$ref": 8, + }, + "variables": Array [ + Object { + "$id": 4, + "defs": Array [ + Object { + "name": Object { + "name": "A", + "range": Array [ + 25, + 26, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 19, + 62, + ], + "type": "ClassDeclaration", + }, + "parent": undefined, + "type": "ClassName", + }, + ], + "identifiers": Array [ + Object { + "name": "A", + "range": Array [ + 25, + 26, + ], + "type": "Identifier", + }, + ], + "name": "A", + "references": Array [], + "scope": Object { + "$ref": 7, + }, + }, + ], + }, + ], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [ + Object { + "$id": 2, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "s", + "range": Array [ + 6, + 7, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": Object { + "range": Array [ + 10, + 18, + ], + "type": "CallExpression", + }, + }, + Object { + "$id": 3, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "Symbol", + "range": Array [ + 10, + 16, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": null, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [ + Object { + "$ref": 3, + }, + ], + "type": "global", + "upperScope": null, + "variableMap": Object { + "A": Object { + "$ref": 1, + }, + "s": Object { + "$ref": 0, + }, + }, + "variableScope": Object { + "$ref": 8, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "s", + "range": Array [ + 6, + 7, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 6, + 18, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 0, + 18, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "identifiers": Array [ + Object { + "name": "s", + "range": Array [ + 6, + 7, + ], + "type": "Identifier", + }, + ], + "name": "s", + "references": Array [ + Object { + "$ref": 2, + }, + Object { + "$ref": 5, + }, + Object { + "$ref": 6, + }, + ], + "scope": Object { + "$ref": 8, + }, + }, + Object { + "$id": 1, + "defs": Array [ + Object { + "name": Object { + "name": "A", + "range": Array [ + 25, + 26, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 19, + 62, + ], + "type": "ClassDeclaration", + }, + "parent": null, + "type": "ClassName", + }, + ], + "identifiers": Array [ + Object { + "name": "A", + "range": Array [ + 25, + 26, + ], + "type": "Identifier", + }, + ], + "name": "A", + "references": Array [], + "scope": Object { + "$ref": 8, + }, + }, + ], +} +`; + exports[`TypeScript scope analysis tests/fixtures/scope-analysis/computed-properties-in-interface.ts 1`] = ` Object { "$id": 8, From bdd99e9ae4913f689b8a0200ab32a16c6cc17f64 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 6 Nov 2018 19:41:16 +0900 Subject: [PATCH 07/29] add decorators fixture --- analyze-scope.js | 38 +- tests/fixtures/scope-analysis/decorators.ts | 13 + .../lib/__snapshots__/scope-analysis.js.snap | 570 ++++++++++++++++++ 3 files changed, 619 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/scope-analysis/decorators.ts diff --git a/analyze-scope.js b/analyze-scope.js index 8dda8be..dae4924 100644 --- a/analyze-scope.js +++ b/analyze-scope.js @@ -150,6 +150,17 @@ class Referencer extends OriginalReferencer { this.close(node); } + /** + * Override. + * Visit decorators. + * @param {ClassDeclaration|ClassExpression} node The class node to visit. + * @returns {void} + */ + visitClass(node) { + this.visitDecorators(node.decorators); + super.visitClass(node); + } + /** * Override. * Don't create the reference object in the type mode. @@ -163,6 +174,17 @@ class Referencer extends OriginalReferencer { this.visit(node.typeAnnotation); } + /** + * Override. + * Visit decorators. + * @param {MethodDefinition} node The MethodDefinition node to visit. + * @returns {void} + */ + MethodDefinition(node) { + this.visitDecorators(node.decorators); + super.MethodDefinition(node); + } + /** * Override. * Don't make variable if `kind === "type"`. @@ -189,10 +211,11 @@ class Referencer extends OriginalReferencer { */ ClassProperty(node) { const upperTypeMode = this.typeMode; - const { computed, key, typeAnnotation, value } = node; + const { computed, decorators, key, typeAnnotation, value } = node; + this.typeMode = false; + this.visitDecorators(decorators); if (computed) { - this.typeMode = false; this.visit(key); } this.typeMode = true; @@ -415,6 +438,17 @@ class Referencer extends OriginalReferencer { this.visit(initializer); } } + + /** + * Process decorators. + * @param {Decorator[]|undefined} decorators The decorator nodes to visit. + * @returns {void} + */ + visitDecorators(decorators) { + if (decorators) { + decorators.forEach(this.visit, this); + } + } } module.exports = function(ast, parserOptions, extraOptions) { diff --git a/tests/fixtures/scope-analysis/decorators.ts b/tests/fixtures/scope-analysis/decorators.ts new file mode 100644 index 0000000..7a779b0 --- /dev/null +++ b/tests/fixtures/scope-analysis/decorators.ts @@ -0,0 +1,13 @@ +function dec(target: any) { +} +function gec() { + return (target: any, proeprtyKey: string) => {} +} + +@dec +class C { + @gec() field: string + @gec() method(): string { + return "" + } +} diff --git a/tests/lib/__snapshots__/scope-analysis.js.snap b/tests/lib/__snapshots__/scope-analysis.js.snap index 2f3d421..f9ee399 100644 --- a/tests/lib/__snapshots__/scope-analysis.js.snap +++ b/tests/lib/__snapshots__/scope-analysis.js.snap @@ -998,6 +998,576 @@ Object { } `; +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/decorators.ts 1`] = ` +Object { + "$id": 18, + "block": Object { + "range": Array [ + 0, + 198, + ], + "type": "Program", + }, + "childScopes": Array [ + Object { + "$id": 6, + "block": Object { + "range": Array [ + 0, + 29, + ], + "type": "FunctionDeclaration", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [], + "throughReferences": Array [], + "type": "function", + "upperScope": Object { + "$ref": 18, + }, + "variableMap": Object { + "arguments": Object { + "$ref": 4, + }, + "target": Object { + "$ref": 5, + }, + }, + "variableScope": Object { + "$ref": 6, + }, + "variables": Array [ + Object { + "$id": 4, + "defs": Array [], + "identifiers": Array [], + "name": "arguments", + "references": Array [], + "scope": Object { + "$ref": 6, + }, + }, + Object { + "$id": 5, + "defs": Array [ + Object { + "name": Object { + "name": "target", + "range": Array [ + 13, + 24, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 0, + 29, + ], + "type": "FunctionDeclaration", + }, + "parent": null, + "type": "Parameter", + }, + ], + "identifiers": Array [ + Object { + "name": "target", + "range": Array [ + 13, + 24, + ], + "type": "Identifier", + }, + ], + "name": "target", + "references": Array [], + "scope": Object { + "$ref": 6, + }, + }, + ], + }, + Object { + "$id": 11, + "block": Object { + "range": Array [ + 30, + 100, + ], + "type": "FunctionDeclaration", + }, + "childScopes": Array [ + Object { + "$id": 10, + "block": Object { + "range": Array [ + 58, + 98, + ], + "type": "ArrowFunctionExpression", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": true, + "references": Array [], + "throughReferences": Array [], + "type": "function", + "upperScope": Object { + "$ref": 11, + }, + "variableMap": Object { + "proeprtyKey": Object { + "$ref": 9, + }, + "target": Object { + "$ref": 8, + }, + }, + "variableScope": Object { + "$ref": 10, + }, + "variables": Array [ + Object { + "$id": 8, + "defs": Array [ + Object { + "name": Object { + "name": "target", + "range": Array [ + 59, + 70, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 58, + 98, + ], + "type": "ArrowFunctionExpression", + }, + "parent": null, + "type": "Parameter", + }, + ], + "identifiers": Array [ + Object { + "name": "target", + "range": Array [ + 59, + 70, + ], + "type": "Identifier", + }, + ], + "name": "target", + "references": Array [], + "scope": Object { + "$ref": 10, + }, + }, + Object { + "$id": 9, + "defs": Array [ + Object { + "name": Object { + "name": "proeprtyKey", + "range": Array [ + 72, + 91, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 58, + 98, + ], + "type": "ArrowFunctionExpression", + }, + "parent": null, + "type": "Parameter", + }, + ], + "identifiers": Array [ + Object { + "name": "proeprtyKey", + "range": Array [ + 72, + 91, + ], + "type": "Identifier", + }, + ], + "name": "proeprtyKey", + "references": Array [], + "scope": Object { + "$ref": 10, + }, + }, + ], + }, + ], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [], + "throughReferences": Array [], + "type": "function", + "upperScope": Object { + "$ref": 18, + }, + "variableMap": Object { + "arguments": Object { + "$ref": 7, + }, + }, + "variableScope": Object { + "$ref": 11, + }, + "variables": Array [ + Object { + "$id": 7, + "defs": Array [], + "identifiers": Array [], + "name": "arguments", + "references": Array [], + "scope": Object { + "$ref": 11, + }, + }, + ], + }, + Object { + "$id": 17, + "block": Object { + "range": Array [ + 102, + 197, + ], + "type": "ClassDeclaration", + }, + "childScopes": Array [ + Object { + "$id": 16, + "block": Object { + "range": Array [ + 159, + 195, + ], + "type": "FunctionExpression", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": true, + "references": Array [], + "throughReferences": Array [], + "type": "function", + "upperScope": Object { + "$ref": 17, + }, + "variableMap": Object { + "arguments": Object { + "$ref": 15, + }, + }, + "variableScope": Object { + "$ref": 16, + }, + "variables": Array [ + Object { + "$id": 15, + "defs": Array [], + "identifiers": Array [], + "name": "arguments", + "references": Array [], + "scope": Object { + "$ref": 16, + }, + }, + ], + }, + ], + "functionExpressionScope": false, + "isStrict": true, + "references": Array [ + Object { + "$id": 13, + "from": Object { + "$ref": 17, + }, + "identifier": Object { + "name": "gec", + "range": Array [ + 122, + 125, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 1, + }, + "writeExpr": undefined, + }, + Object { + "$id": 14, + "from": Object { + "$ref": 17, + }, + "identifier": Object { + "name": "gec", + "range": Array [ + 147, + 150, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 1, + }, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [ + Object { + "$ref": 13, + }, + Object { + "$ref": 14, + }, + ], + "type": "class", + "upperScope": Object { + "$ref": 18, + }, + "variableMap": Object { + "C": Object { + "$ref": 12, + }, + }, + "variableScope": Object { + "$ref": 18, + }, + "variables": Array [ + Object { + "$id": 12, + "defs": Array [ + Object { + "name": Object { + "name": "C", + "range": Array [ + 113, + 114, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 102, + 197, + ], + "type": "ClassDeclaration", + }, + "parent": undefined, + "type": "ClassName", + }, + ], + "identifiers": Array [ + Object { + "name": "C", + "range": Array [ + 113, + 114, + ], + "type": "Identifier", + }, + ], + "name": "C", + "references": Array [], + "scope": Object { + "$ref": 17, + }, + }, + ], + }, + ], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [ + Object { + "$id": 3, + "from": Object { + "$ref": 18, + }, + "identifier": Object { + "name": "dec", + "range": Array [ + 103, + 106, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [], + "type": "global", + "upperScope": null, + "variableMap": Object { + "C": Object { + "$ref": 2, + }, + "dec": Object { + "$ref": 0, + }, + "gec": Object { + "$ref": 1, + }, + }, + "variableScope": Object { + "$ref": 18, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "dec", + "range": Array [ + 9, + 12, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 0, + 29, + ], + "type": "FunctionDeclaration", + }, + "parent": null, + "type": "FunctionName", + }, + ], + "identifiers": Array [ + Object { + "name": "dec", + "range": Array [ + 9, + 12, + ], + "type": "Identifier", + }, + ], + "name": "dec", + "references": Array [ + Object { + "$ref": 3, + }, + ], + "scope": Object { + "$ref": 18, + }, + }, + Object { + "$id": 1, + "defs": Array [ + Object { + "name": Object { + "name": "gec", + "range": Array [ + 39, + 42, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 30, + 100, + ], + "type": "FunctionDeclaration", + }, + "parent": null, + "type": "FunctionName", + }, + ], + "identifiers": Array [ + Object { + "name": "gec", + "range": Array [ + 39, + 42, + ], + "type": "Identifier", + }, + ], + "name": "gec", + "references": Array [ + Object { + "$ref": 13, + }, + Object { + "$ref": 14, + }, + ], + "scope": Object { + "$ref": 18, + }, + }, + Object { + "$id": 2, + "defs": Array [ + Object { + "name": Object { + "name": "C", + "range": Array [ + 113, + 114, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 102, + 197, + ], + "type": "ClassDeclaration", + }, + "parent": null, + "type": "ClassName", + }, + ], + "identifiers": Array [ + Object { + "name": "C", + "range": Array [ + 113, + 114, + ], + "type": "Identifier", + }, + ], + "name": "C", + "references": Array [], + "scope": Object { + "$ref": 18, + }, + }, + ], +} +`; + exports[`TypeScript scope analysis tests/fixtures/scope-analysis/enum.ts 1`] = ` Object { "$id": 14, From f908e816a457516211369f832bf3a762d2d62f23 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 6 Nov 2018 20:02:20 +0900 Subject: [PATCH 08/29] update visitor-keys --- visitor-keys.js | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/visitor-keys.js b/visitor-keys.js index 9f58086..f9b4284 100644 --- a/visitor-keys.js +++ b/visitor-keys.js @@ -9,28 +9,20 @@ const Evk = require("eslint-visitor-keys"); module.exports = Evk.unionWith({ - ArrayPattern: ["typeAnnotation"], - ArrowFunctionExpression: ["returnType", "typeParameters"], - AssignmentPattern: ["typeAnnotation"], - CallExpression: ["typeParameters"], - ClassDeclaration: ["superTypeParameters", "typeParameters"], - ClassExpression: ["superTypeParameters", "typeParameters"], - ClassImplements: ["typeParameters"], - ClassProperty: ["typeAnnotation"], - FunctionDeclaration: ["returnType", "typeParameters"], - FunctionExpression: ["returnType", "typeParameters"], + // Additional Properties. + ArrayPattern: ["elements", "typeAnnotation"], + ArrowFunctionExpression: ["typeParameters", "params", "returnType", "body"], + ClassDeclaration: ["decorators", "id", "typeParameters", "superClass", "body"], + ClassExpression: ["decorators", "id", "typeParameters", "superClass", "body"], + FunctionDeclaration: ["id", "typeParameters", "params", "returnType", "body"], + FunctionExpression: ["id", "typeParameters", "params", "returnType", "body"], Identifier: ["typeAnnotation"], - InterfaceDeclaration: ["typeParameters"], - NewExpression: ["typeParameters"], - ObjectPattern: ["typeAnnotation"], - /** - * According to https://github.com/estree/estree/blob/master/extensions/type-annotations.md - * RestElement should have "typeAnnotation", but has not. Annotation is added on the "parameter" node - */ - RestElement: [], - TaggedTemplateExpression: ["typeParameters"], - VariableDeclarator: ["typeParameters"], + MethodDefinition: ["decorators", "key", "value"], + ObjectPattern: ["properties", "typeAnnotation"], + // Additional Nodes. + ClassProperty: ["decorators", "key", "typeAnnotation", "value"], + Decorator: ["expression"], TSAbstractClassProperty: ["typeAnnotation", "key", "value"], TSAbstractClassDeclaration: ["id", "body", "superClass", "implements"], TSAbstractKeyword: [], @@ -42,8 +34,8 @@ module.exports = Evk.unionWith({ TSConstructorType: ["typeAnnotation", "parameters"], TSConstructSignature: ["typeAnnotation", "typeParameters"], TSDeclareKeyword: [], - TSEmptyBodyFunctionDeclaration: ["id", "params", "body", "returnType", "typeParameters"], - TSEmptyBodyFunctionExpression: ["id", "params", "body", "returnType", "typeParameters"], + TSEmptyBodyFunctionDeclaration: ["id", "typeParameters", "params", "returnType"], + TSEmptyBodyFunctionExpression: ["id", "typeParameters", "params", "returnType"], TSEnumDeclaration: ["members"], TSEnumMember: ["initializer"], TSExportAssignment: ["expression"], From 00ce3dc7d433bc91e128abcafc5290299fe541f4 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Wed, 7 Nov 2018 14:17:51 +0900 Subject: [PATCH 09/29] add declare-global fixture --- analyze-scope.js | 83 ++++++++ package.json | 1 + .../fixtures/scope-analysis/declare-global.ts | 7 + .../lib/__snapshots__/scope-analysis.js.snap | 186 ++++++++++++++++++ tests/lib/scope-analysis.js | 9 +- 5 files changed, 282 insertions(+), 4 deletions(-) create mode 100644 tests/fixtures/scope-analysis/declare-global.ts diff --git a/analyze-scope.js b/analyze-scope.js index dae4924..d4a100a 100644 --- a/analyze-scope.js +++ b/analyze-scope.js @@ -9,8 +9,56 @@ const Reference = require("eslint-scope/lib/reference"); const OriginalReferencer = require("eslint-scope/lib/referencer"); const Scope = require("eslint-scope/lib/scope").Scope; const fallback = require("eslint-visitor-keys").getKeys; +const lodash = require("lodash"); const childVisitorKeys = require("./visitor-keys"); +/** + * Get `.range[0]` of a given object. + * @param {{range: number[]}} x The object to get. + * @returns {number} The gotten value. + */ +function byRange0(x) { + return x.range[0]; +} + +/** + * Check the TSModuleDeclaration node is `declare global {}` or not. + * @param {TSModuleDeclaration} node The TSModuleDeclaration node to check. + * @param {Token[]} tokens The token list. + * @returns {boolean} `true` if the node is `declare global {}`. + */ +function isGlobalAugmentation(node, tokens) { + const i = lodash.sortedIndexBy(tokens, node, byRange0); + const token1 = tokens[i]; + const token2 = tokens[i + 1]; + + return Boolean( + token1 && + token2 && + (token1.type === "Keyword" || token1.type === "Identifier") && + token1.value === "declare" && + (token2.type === "Keyword" || token2.type === "Identifier") && + token2.value === "global" + ); +} + +/** + * Define the override function of `Scope#__define` for global augmentation. + * @param {Function} define The original Scope#__define method. + * @returns {Function} The override function. + */ +function overrideDefine(define) { + return /* @this {Scope} */ function(node, definition) { + define.call(this, node, definition); + + // Set `variable.eslintUsed` to tell ESLint that the variable is exported. + const variable = this.set.get(node.name); + if (variable) { + variable.eslintUsed = true; + } + }; +} + /** The scope class for enum. */ class EnumScope extends Scope { constructor(scopeManager, upperScope, block) { @@ -439,6 +487,41 @@ class Referencer extends OriginalReferencer { } } + TSModuleDeclaration(node) { + const astRoot = this.scopeManager.globalScope.block; + + // https://github.com/JamesHenry/typescript-estree/issues/27 + if (isGlobalAugmentation(node, astRoot.tokens)) { + this.visitGlobalAugmentation(node); + } else { + // TODO: module/namespace + } + } + + /** + * Process the global augmentation. + * 1. Set the global scope as the current scope. + * 2. Configure the global scope to set `variable.eslintUsed = true` for all defined variables. This means `no-unused-vars` doesn't warn those. + * @param {TSModuleDeclaration} node The TSModuleDeclaration node to visit. + * @returns {void} + */ + visitGlobalAugmentation(node) { + const scopeManager = this.scopeManager; + const currentScope = this.currentScope(); + const globalScope = scopeManager.globalScope; + const originalDefine = globalScope.__define; + + globalScope.__define = overrideDefine(originalDefine); + scopeManager.__currentScope = globalScope; + + for (const moduleItem of node.body.body) { + this.visit(moduleItem); + } + + scopeManager.__currentScope = currentScope; + globalScope.__define = originalDefine; + } + /** * Process decorators. * @param {Decorator[]|undefined} decorators The decorator nodes to visit. diff --git a/package.json b/package.json index cdecd32..56218ab 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "eslint": "4.19.1", "eslint-scope": "^4.0.0", "eslint-visitor-keys": "^1.0.0", + "lodash": "^4.17.11", "typescript-estree": "2.1.0" }, "peerDependencies": { diff --git a/tests/fixtures/scope-analysis/declare-global.ts b/tests/fixtures/scope-analysis/declare-global.ts new file mode 100644 index 0000000..76c4bef --- /dev/null +++ b/tests/fixtures/scope-analysis/declare-global.ts @@ -0,0 +1,7 @@ +declare global { + let C: number +} + +C = 1 + +export {} diff --git a/tests/lib/__snapshots__/scope-analysis.js.snap b/tests/lib/__snapshots__/scope-analysis.js.snap index f9ee399..06648e3 100644 --- a/tests/lib/__snapshots__/scope-analysis.js.snap +++ b/tests/lib/__snapshots__/scope-analysis.js.snap @@ -64,6 +64,7 @@ Object { Object { "$id": 1, "defs": Array [], + "eslintUsed": undefined, "identifiers": Array [], "name": "arguments", "references": Array [], @@ -94,6 +95,7 @@ Object { "type": "Parameter", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "bar", @@ -155,6 +157,7 @@ Object { "type": "FunctionName", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "foo", @@ -282,6 +285,7 @@ Object { "type": "ClassName", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "A", @@ -395,6 +399,7 @@ Object { "type": "Variable", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "s", @@ -444,6 +449,7 @@ Object { "type": "ClassName", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "A", @@ -652,6 +658,7 @@ Object { "type": "Variable", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "s1", @@ -704,6 +711,7 @@ Object { "type": "Variable", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "s2", @@ -919,6 +927,7 @@ Object { "type": "Variable", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "s1", @@ -971,6 +980,7 @@ Object { "type": "Variable", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "s2", @@ -998,6 +1008,141 @@ Object { } `; +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/declare-global.ts 1`] = ` +Object { + "$id": 3, + "block": Object { + "range": Array [ + 0, + 55, + ], + "type": "Program", + }, + "childScopes": Array [ + Object { + "$id": 2, + "block": Object { + "range": Array [ + 0, + 55, + ], + "type": "Program", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": true, + "references": Array [ + Object { + "$id": 1, + "from": Object { + "$ref": 2, + }, + "identifier": Object { + "name": "C", + "range": Array [ + 38, + 39, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": Object { + "range": Array [ + 42, + 43, + ], + "type": "Literal", + }, + }, + ], + "throughReferences": Array [ + Object { + "$ref": 1, + }, + ], + "type": "module", + "upperScope": Object { + "$ref": 3, + }, + "variableMap": Object {}, + "variableScope": Object { + "$ref": 2, + }, + "variables": Array [], + }, + ], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [], + "throughReferences": Array [], + "type": "global", + "upperScope": null, + "variableMap": Object { + "C": Object { + "$ref": 0, + }, + }, + "variableScope": Object { + "$ref": 3, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "C", + "range": Array [ + 25, + 34, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 25, + 34, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 21, + 34, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "eslintUsed": true, + "identifiers": Array [ + Object { + "name": "C", + "range": Array [ + 25, + 34, + ], + "type": "Identifier", + }, + ], + "name": "C", + "references": Array [ + Object { + "$ref": 1, + }, + ], + "scope": Object { + "$ref": 3, + }, + }, + ], +} +`; + exports[`TypeScript scope analysis tests/fixtures/scope-analysis/decorators.ts 1`] = ` Object { "$id": 18, @@ -1042,6 +1187,7 @@ Object { Object { "$id": 4, "defs": Array [], + "eslintUsed": undefined, "identifiers": Array [], "name": "arguments", "references": Array [], @@ -1072,6 +1218,7 @@ Object { "type": "Parameter", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "target", @@ -1153,6 +1300,7 @@ Object { "type": "Parameter", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "target", @@ -1192,6 +1340,7 @@ Object { "type": "Parameter", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "proeprtyKey", @@ -1231,6 +1380,7 @@ Object { Object { "$id": 7, "defs": Array [], + "eslintUsed": undefined, "identifiers": Array [], "name": "arguments", "references": Array [], @@ -1280,6 +1430,7 @@ Object { Object { "$id": 15, "defs": Array [], + "eslintUsed": undefined, "identifiers": Array [], "name": "arguments", "references": Array [], @@ -1376,6 +1527,7 @@ Object { "type": "ClassName", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "C", @@ -1459,6 +1611,7 @@ Object { "type": "FunctionName", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "dec", @@ -1502,6 +1655,7 @@ Object { "type": "FunctionName", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "gec", @@ -1548,6 +1702,7 @@ Object { "type": "ClassName", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "C", @@ -1795,6 +1950,7 @@ Object { "type": "EnumMemberName", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "A", @@ -1841,6 +1997,7 @@ Object { "type": "EnumMemberName", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "B", @@ -1887,6 +2044,7 @@ Object { "type": "EnumMemberName", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "C", @@ -1983,6 +2141,7 @@ Object { "type": "Variable", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "a", @@ -2032,6 +2191,7 @@ Object { "type": "EnumName", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "E", @@ -2096,6 +2256,7 @@ Object { Object { "$id": 1, "defs": Array [], + "eslintUsed": undefined, "identifiers": Array [], "name": "arguments", "references": Array [], @@ -2126,6 +2287,7 @@ Object { "type": "Parameter", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "a", @@ -2183,6 +2345,7 @@ Object { "type": "FunctionName", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "f", @@ -2252,6 +2415,7 @@ Object { "type": "FunctionName", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "f", @@ -2327,6 +2491,7 @@ Object { "type": "Variable", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "a", @@ -2401,6 +2566,7 @@ Object { Object { "$id": 6, "defs": Array [], + "eslintUsed": undefined, "identifiers": Array [], "name": "arguments", "references": Array [], @@ -2431,6 +2597,7 @@ Object { "type": "Parameter", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "a", @@ -2514,6 +2681,7 @@ Object { "type": "ClassName", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "A", @@ -2627,6 +2795,7 @@ Object { "type": "Variable", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "s", @@ -2673,6 +2842,7 @@ Object { "type": "ClassName", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "A", @@ -2747,6 +2917,7 @@ Object { Object { "$id": 3, "defs": Array [], + "eslintUsed": undefined, "identifiers": Array [], "name": "arguments", "references": Array [], @@ -2777,6 +2948,7 @@ Object { "type": "Parameter", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "a", @@ -2836,6 +3008,7 @@ Object { "type": "ClassName", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "C", @@ -2902,6 +3075,7 @@ Object { "type": "Variable", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "a", @@ -2941,6 +3115,7 @@ Object { "type": "ClassName", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "C", @@ -3180,6 +3355,7 @@ Object { "type": "Variable", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "obj", @@ -3419,6 +3595,7 @@ Object { "type": "Variable", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "obj", @@ -3513,6 +3690,7 @@ Object { Object { "$id": 1, "defs": Array [], + "eslintUsed": undefined, "identifiers": Array [], "name": "arguments", "references": Array [], @@ -3543,6 +3721,7 @@ Object { "type": "Parameter", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "a", @@ -3604,6 +3783,7 @@ Object { "type": "FunctionName", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "f", @@ -3668,6 +3848,7 @@ Object { Object { "$id": 2, "defs": Array [], + "eslintUsed": undefined, "identifiers": Array [], "name": "arguments", "references": Array [], @@ -3698,6 +3879,7 @@ Object { "type": "Parameter", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "g", @@ -3775,6 +3957,7 @@ Object { "type": "FunctionName", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "g", @@ -3974,6 +4157,7 @@ Object { "type": "Variable", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "obj", @@ -4029,6 +4213,7 @@ Object { "type": "Variable", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "obj2", @@ -4078,6 +4263,7 @@ Object { "type": "Variable", }, ], + "eslintUsed": undefined, "identifiers": Array [ Object { "name": "value", diff --git a/tests/lib/scope-analysis.js b/tests/lib/scope-analysis.js index bbb3d4a..a72aaba 100644 --- a/tests/lib/scope-analysis.js +++ b/tests/lib/scope-analysis.js @@ -59,7 +59,7 @@ function nodeToJSON(node) { * @returns {Object} The object that can be used for JSON.stringify. */ function variableToJSON(variable, resolver) { - const { name } = variable; + const { name, eslintUsed } = variable; const defs = variable.defs.map(d => ({ type: d.type, name: nodeToJSON(d.name), @@ -75,7 +75,8 @@ function variableToJSON(variable, resolver) { defs, identifiers, references, - scope + scope, + eslintUsed }); } @@ -144,9 +145,9 @@ describe("TypeScript scope analysis", () => { test(filePath, () => { const code = fs.readFileSync(filePath, "utf8"); const { scopeManager } = parseForESLint(code, { - loc: false, + loc: true, range: true, - tokens: false, + tokens: true, ecmaFeatures: {} }); const { globalScope } = scopeManager; From bbf0b6964945154e667a92463bd1420da88e8f82 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Wed, 7 Nov 2018 14:19:04 +0900 Subject: [PATCH 10/29] fix typo --- tests/fixtures/scope-analysis/decorators.ts | 2 +- tests/lib/__snapshots__/scope-analysis.js.snap | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/fixtures/scope-analysis/decorators.ts b/tests/fixtures/scope-analysis/decorators.ts index 7a779b0..34da4b5 100644 --- a/tests/fixtures/scope-analysis/decorators.ts +++ b/tests/fixtures/scope-analysis/decorators.ts @@ -1,7 +1,7 @@ function dec(target: any) { } function gec() { - return (target: any, proeprtyKey: string) => {} + return (target: any, propertyKey: string) => {} } @dec diff --git a/tests/lib/__snapshots__/scope-analysis.js.snap b/tests/lib/__snapshots__/scope-analysis.js.snap index 06648e3..cf82bfc 100644 --- a/tests/lib/__snapshots__/scope-analysis.js.snap +++ b/tests/lib/__snapshots__/scope-analysis.js.snap @@ -1266,7 +1266,7 @@ Object { "$ref": 11, }, "variableMap": Object { - "proeprtyKey": Object { + "propertyKey": Object { "$ref": 9, }, "target": Object { @@ -1322,7 +1322,7 @@ Object { "defs": Array [ Object { "name": Object { - "name": "proeprtyKey", + "name": "propertyKey", "range": Array [ 72, 91, @@ -1343,7 +1343,7 @@ Object { "eslintUsed": undefined, "identifiers": Array [ Object { - "name": "proeprtyKey", + "name": "propertyKey", "range": Array [ 72, 91, @@ -1351,7 +1351,7 @@ Object { "type": "Identifier", }, ], - "name": "proeprtyKey", + "name": "propertyKey", "references": Array [], "scope": Object { "$ref": 10, From 257e9535ff2c93207008c0fbe76de7b737803529 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Wed, 7 Nov 2018 14:21:38 +0900 Subject: [PATCH 11/29] add test for typeof in array destructuring --- .../fixtures/scope-analysis/typeof-in-var.ts | 1 + .../lib/__snapshots__/scope-analysis.js.snap | 142 +++++++++++++++--- 2 files changed, 122 insertions(+), 21 deletions(-) diff --git a/tests/fixtures/scope-analysis/typeof-in-var.ts b/tests/fixtures/scope-analysis/typeof-in-var.ts index 3e7d87c..c970514 100644 --- a/tests/fixtures/scope-analysis/typeof-in-var.ts +++ b/tests/fixtures/scope-analysis/typeof-in-var.ts @@ -1,3 +1,4 @@ var obj = { value: 1 } var obj2: typeof obj = { value: 2 } var { value }: typeof obj = { value: 2 } +var [element]: (typeof obj)[] = [{ value: 2 }] diff --git a/tests/lib/__snapshots__/scope-analysis.js.snap b/tests/lib/__snapshots__/scope-analysis.js.snap index cf82bfc..f4267a0 100644 --- a/tests/lib/__snapshots__/scope-analysis.js.snap +++ b/tests/lib/__snapshots__/scope-analysis.js.snap @@ -3984,11 +3984,11 @@ Object { exports[`TypeScript scope analysis tests/fixtures/scope-analysis/typeof-in-var.ts 1`] = ` Object { - "$id": 8, + "$id": 11, "block": Object { "range": Array [ 0, - 100, + 147, ], "type": "Program", }, @@ -3997,9 +3997,9 @@ Object { "isStrict": false, "references": Array [ Object { - "$id": 3, + "$id": 4, "from": Object { - "$ref": 8, + "$ref": 11, }, "identifier": Object { "name": "obj", @@ -4022,9 +4022,9 @@ Object { }, }, Object { - "$id": 4, + "$id": 5, "from": Object { - "$ref": 8, + "$ref": 11, }, "identifier": Object { "name": "obj2", @@ -4047,9 +4047,9 @@ Object { }, }, Object { - "$id": 5, + "$id": 6, "from": Object { - "$ref": 8, + "$ref": 11, }, "identifier": Object { "name": "obj", @@ -4066,9 +4066,9 @@ Object { "writeExpr": undefined, }, Object { - "$id": 6, + "$id": 7, "from": Object { - "$ref": 8, + "$ref": 11, }, "identifier": Object { "name": "value", @@ -4091,9 +4091,9 @@ Object { }, }, Object { - "$id": 7, + "$id": 8, "from": Object { - "$ref": 8, + "$ref": 11, }, "identifier": Object { "name": "obj", @@ -4109,11 +4109,58 @@ Object { }, "writeExpr": undefined, }, + Object { + "$id": 9, + "from": Object { + "$ref": 11, + }, + "identifier": Object { + "name": "element", + "range": Array [ + 105, + 112, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 3, + }, + "writeExpr": Object { + "range": Array [ + 132, + 146, + ], + "type": "ArrayExpression", + }, + }, + Object { + "$id": 10, + "from": Object { + "$ref": 11, + }, + "identifier": Object { + "name": "obj", + "range": Array [ + 123, + 126, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, ], "throughReferences": Array [], "type": "global", "upperScope": null, "variableMap": Object { + "element": Object { + "$ref": 3, + }, "obj": Object { "$ref": 0, }, @@ -4125,7 +4172,7 @@ Object { }, }, "variableScope": Object { - "$ref": 8, + "$ref": 11, }, "variables": Array [ Object { @@ -4171,17 +4218,20 @@ Object { "name": "obj", "references": Array [ Object { - "$ref": 3, + "$ref": 4, }, Object { - "$ref": 5, + "$ref": 6, }, Object { - "$ref": 7, + "$ref": 8, + }, + Object { + "$ref": 10, }, ], "scope": Object { - "$ref": 8, + "$ref": 11, }, }, Object { @@ -4227,11 +4277,11 @@ Object { "name": "obj2", "references": Array [ Object { - "$ref": 4, + "$ref": 5, }, ], "scope": Object { - "$ref": 8, + "$ref": 11, }, }, Object { @@ -4277,11 +4327,61 @@ Object { "name": "value", "references": Array [ Object { - "$ref": 6, + "$ref": 7, }, ], "scope": Object { - "$ref": 8, + "$ref": 11, + }, + }, + Object { + "$id": 3, + "defs": Array [ + Object { + "name": Object { + "name": "element", + "range": Array [ + 105, + 112, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 104, + 146, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 100, + 146, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "eslintUsed": undefined, + "identifiers": Array [ + Object { + "name": "element", + "range": Array [ + 105, + 112, + ], + "type": "Identifier", + }, + ], + "name": "element", + "references": Array [ + Object { + "$ref": 9, + }, + ], + "scope": Object { + "$ref": 11, }, }, ], From 85b90673b80aab809f6f95a1f9fccc1a83791e92 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Wed, 7 Nov 2018 14:51:27 +0900 Subject: [PATCH 12/29] add namespace fixture --- analyze-scope.js | 30 +- tests/fixtures/scope-analysis/namespace.ts | 7 + .../lib/__snapshots__/scope-analysis.js.snap | 347 ++++++++++++++++++ 3 files changed, 382 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/scope-analysis/namespace.ts diff --git a/analyze-scope.js b/analyze-scope.js index d4a100a..37aa614 100644 --- a/analyze-scope.js +++ b/analyze-scope.js @@ -487,15 +487,40 @@ class Referencer extends OriginalReferencer { } } + /** + * Create the variable object for the module name, and visit children. + * @param {TSModuleDeclaration} node The TSModuleDeclaration node to visit. + * @returns {void} + */ TSModuleDeclaration(node) { const astRoot = this.scopeManager.globalScope.block; + const scope = this.currentScope(); + const { id, body } = node; // https://github.com/JamesHenry/typescript-estree/issues/27 if (isGlobalAugmentation(node, astRoot.tokens)) { this.visitGlobalAugmentation(node); - } else { - // TODO: module/namespace + return; } + + if (id && id.type === "Identifier") { + scope.__define( + id, + new Definition("NamespaceName", id, node, null, null, null) + ); + } + this.visit(body); + } + + /** + * Process the module block. + * @param {TSModuleBlock} node The TSModuleBlock node to visit. + * @returns {void} + */ + TSModuleBlock(node) { + this.scopeManager.__nestBlockScope(node); + this.visitChildren(node); + this.close(node); } /** @@ -514,6 +539,7 @@ class Referencer extends OriginalReferencer { globalScope.__define = overrideDefine(originalDefine); scopeManager.__currentScope = globalScope; + // Skip TSModuleBlock to avoid to create that block scope. for (const moduleItem of node.body.body) { this.visit(moduleItem); } diff --git a/tests/fixtures/scope-analysis/namespace.ts b/tests/fixtures/scope-analysis/namespace.ts new file mode 100644 index 0000000..216c093 --- /dev/null +++ b/tests/fixtures/scope-analysis/namespace.ts @@ -0,0 +1,7 @@ +const a = 1 +namespace N { + export const a = 2 + a +} +a +N.a diff --git a/tests/lib/__snapshots__/scope-analysis.js.snap b/tests/lib/__snapshots__/scope-analysis.js.snap index f4267a0..df4ea14 100644 --- a/tests/lib/__snapshots__/scope-analysis.js.snap +++ b/tests/lib/__snapshots__/scope-analysis.js.snap @@ -2863,6 +2863,353 @@ Object { } `; +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/namespace.ts 1`] = ` +Object { + "$id": 10, + "block": Object { + "range": Array [ + 0, + 63, + ], + "type": "Program", + }, + "childScopes": Array [ + Object { + "$id": 9, + "block": Object { + "range": Array [ + 0, + 63, + ], + "type": "Program", + }, + "childScopes": Array [ + Object { + "$id": 8, + "block": Object { + "range": Array [ + 24, + 56, + ], + "type": "TSModuleBlock", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": true, + "references": Array [ + Object { + "$id": 6, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "a", + "range": Array [ + 43, + 44, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 5, + }, + "writeExpr": Object { + "range": Array [ + 47, + 48, + ], + "type": "Literal", + }, + }, + Object { + "$id": 7, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "a", + "range": Array [ + 53, + 54, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 5, + }, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [], + "type": "block", + "upperScope": Object { + "$ref": 9, + }, + "variableMap": Object { + "a": Object { + "$ref": 5, + }, + }, + "variableScope": Object { + "$ref": 9, + }, + "variables": Array [ + Object { + "$id": 5, + "defs": Array [ + Object { + "name": Object { + "name": "a", + "range": Array [ + 43, + 44, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 43, + 48, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 37, + 48, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "eslintUsed": undefined, + "identifiers": Array [ + Object { + "name": "a", + "range": Array [ + 43, + 44, + ], + "type": "Identifier", + }, + ], + "name": "a", + "references": Array [ + Object { + "$ref": 6, + }, + Object { + "$ref": 7, + }, + ], + "scope": Object { + "$ref": 8, + }, + }, + ], + }, + ], + "functionExpressionScope": false, + "isStrict": true, + "references": Array [ + Object { + "$id": 2, + "from": Object { + "$ref": 9, + }, + "identifier": Object { + "name": "a", + "range": Array [ + 6, + 7, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": Object { + "range": Array [ + 10, + 11, + ], + "type": "Literal", + }, + }, + Object { + "$id": 3, + "from": Object { + "$ref": 9, + }, + "identifier": Object { + "name": "a", + "range": Array [ + 57, + 58, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + Object { + "$id": 4, + "from": Object { + "$ref": 9, + }, + "identifier": Object { + "name": "N", + "range": Array [ + 59, + 60, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 1, + }, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [], + "type": "module", + "upperScope": Object { + "$ref": 10, + }, + "variableMap": Object { + "N": Object { + "$ref": 1, + }, + "a": Object { + "$ref": 0, + }, + }, + "variableScope": Object { + "$ref": 9, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "a", + "range": Array [ + 6, + 7, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 6, + 11, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 0, + 11, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "eslintUsed": undefined, + "identifiers": Array [ + Object { + "name": "a", + "range": Array [ + 6, + 7, + ], + "type": "Identifier", + }, + ], + "name": "a", + "references": Array [ + Object { + "$ref": 2, + }, + Object { + "$ref": 3, + }, + ], + "scope": Object { + "$ref": 9, + }, + }, + Object { + "$id": 1, + "defs": Array [ + Object { + "name": Object { + "name": "N", + "range": Array [ + 22, + 23, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 12, + 56, + ], + "type": "TSModuleDeclaration", + }, + "parent": null, + "type": "NamespaceName", + }, + ], + "eslintUsed": undefined, + "identifiers": Array [ + Object { + "name": "N", + "range": Array [ + 22, + 23, + ], + "type": "Identifier", + }, + ], + "name": "N", + "references": Array [ + Object { + "$ref": 4, + }, + ], + "scope": Object { + "$ref": 9, + }, + }, + ], + }, + ], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [], + "throughReferences": Array [], + "type": "global", + "upperScope": null, + "variableMap": Object {}, + "variableScope": Object { + "$ref": 10, + }, + "variables": Array [], +} +`; + exports[`TypeScript scope analysis tests/fixtures/scope-analysis/type-annotations.ts 1`] = ` Object { "$id": 7, From 7a8e250d7e8ef29068ad1126fe724149ef6a797e Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Wed, 7 Nov 2018 15:10:42 +0900 Subject: [PATCH 13/29] add declare-module fixture --- .../fixtures/scope-analysis/declare-module.ts | 6 + .../lib/__snapshots__/scope-analysis.js.snap | 302 ++++++++++++++++++ 2 files changed, 308 insertions(+) create mode 100644 tests/fixtures/scope-analysis/declare-module.ts diff --git a/tests/fixtures/scope-analysis/declare-module.ts b/tests/fixtures/scope-analysis/declare-module.ts new file mode 100644 index 0000000..9459128 --- /dev/null +++ b/tests/fixtures/scope-analysis/declare-module.ts @@ -0,0 +1,6 @@ +const a = 1 +declare module "foo" { + export const a: number + export const b: typeof a +} +a diff --git a/tests/lib/__snapshots__/scope-analysis.js.snap b/tests/lib/__snapshots__/scope-analysis.js.snap index df4ea14..1d98143 100644 --- a/tests/lib/__snapshots__/scope-analysis.js.snap +++ b/tests/lib/__snapshots__/scope-analysis.js.snap @@ -1143,6 +1143,308 @@ Object { } `; +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/declare-module.ts 1`] = ` +Object { + "$id": 8, + "block": Object { + "range": Array [ + 0, + 95, + ], + "type": "Program", + }, + "childScopes": Array [ + Object { + "$id": 7, + "block": Object { + "range": Array [ + 0, + 95, + ], + "type": "Program", + }, + "childScopes": Array [ + Object { + "$id": 6, + "block": Object { + "range": Array [ + 33, + 92, + ], + "type": "TSModuleBlock", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": true, + "references": Array [ + Object { + "$id": 5, + "from": Object { + "$ref": 6, + }, + "identifier": Object { + "name": "a", + "range": Array [ + 89, + 90, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 3, + }, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [], + "type": "block", + "upperScope": Object { + "$ref": 7, + }, + "variableMap": Object { + "a": Object { + "$ref": 3, + }, + "b": Object { + "$ref": 4, + }, + }, + "variableScope": Object { + "$ref": 7, + }, + "variables": Array [ + Object { + "$id": 3, + "defs": Array [ + Object { + "name": Object { + "name": "a", + "range": Array [ + 52, + 61, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 52, + 61, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 46, + 61, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "eslintUsed": undefined, + "identifiers": Array [ + Object { + "name": "a", + "range": Array [ + 52, + 61, + ], + "type": "Identifier", + }, + ], + "name": "a", + "references": Array [ + Object { + "$ref": 5, + }, + ], + "scope": Object { + "$ref": 6, + }, + }, + Object { + "$id": 4, + "defs": Array [ + Object { + "name": Object { + "name": "b", + "range": Array [ + 79, + 90, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 79, + 90, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 73, + 90, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "eslintUsed": undefined, + "identifiers": Array [ + Object { + "name": "b", + "range": Array [ + 79, + 90, + ], + "type": "Identifier", + }, + ], + "name": "b", + "references": Array [], + "scope": Object { + "$ref": 6, + }, + }, + ], + }, + ], + "functionExpressionScope": false, + "isStrict": true, + "references": Array [ + Object { + "$id": 1, + "from": Object { + "$ref": 7, + }, + "identifier": Object { + "name": "a", + "range": Array [ + 6, + 7, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": Object { + "range": Array [ + 10, + 11, + ], + "type": "Literal", + }, + }, + Object { + "$id": 2, + "from": Object { + "$ref": 7, + }, + "identifier": Object { + "name": "a", + "range": Array [ + 93, + 94, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [], + "type": "module", + "upperScope": Object { + "$ref": 8, + }, + "variableMap": Object { + "a": Object { + "$ref": 0, + }, + }, + "variableScope": Object { + "$ref": 7, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "a", + "range": Array [ + 6, + 7, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 6, + 11, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 0, + 11, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "eslintUsed": undefined, + "identifiers": Array [ + Object { + "name": "a", + "range": Array [ + 6, + 7, + ], + "type": "Identifier", + }, + ], + "name": "a", + "references": Array [ + Object { + "$ref": 1, + }, + Object { + "$ref": 2, + }, + ], + "scope": Object { + "$ref": 7, + }, + }, + ], + }, + ], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [], + "throughReferences": Array [], + "type": "global", + "upperScope": null, + "variableMap": Object {}, + "variableScope": Object { + "$ref": 8, + }, + "variables": Array [], +} +`; + exports[`TypeScript scope analysis tests/fixtures/scope-analysis/decorators.ts 1`] = ` Object { "$id": 18, From 8a9eb40330f5a4f1ebeed85cfb8aa55f64f082ee Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Wed, 7 Nov 2018 15:27:12 +0900 Subject: [PATCH 14/29] fix crash --- analyze-scope.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analyze-scope.js b/analyze-scope.js index 37aa614..3c28972 100644 --- a/analyze-scope.js +++ b/analyze-scope.js @@ -132,7 +132,7 @@ class Referencer extends OriginalReferencer { const upperScope = this.currentScope(); // Process the name. - if (type === "FunctionDeclaration") { + if (type === "FunctionDeclaration" && id) { upperScope.__define( id, new Definition("FunctionName", id, node, null, null, null) From 5e3b3f698e571b89bed7bb98a6322130aa5e356b Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Wed, 7 Nov 2018 16:22:17 +0900 Subject: [PATCH 15/29] add declare-function.ts fixture --- analyze-scope.js | 3 + .../scope-analysis/declare-function.ts | 2 + .../lib/__snapshots__/scope-analysis.js.snap | 94 +++++++++++++++++++ visitor-keys.js | 1 + 4 files changed, 100 insertions(+) create mode 100644 tests/fixtures/scope-analysis/declare-function.ts diff --git a/analyze-scope.js b/analyze-scope.js index 3c28972..32af82f 100644 --- a/analyze-scope.js +++ b/analyze-scope.js @@ -303,6 +303,9 @@ class Referencer extends OriginalReferencer { this.visit(returnType); this.typeMode = upperTypeMode; } + TSEmptyBodyDeclareFunction(node) { + this.TSEmptyBodyFunctionDeclaration(node); + } /** * Create reference objects for the references in parameters and return type. diff --git a/tests/fixtures/scope-analysis/declare-function.ts b/tests/fixtures/scope-analysis/declare-function.ts new file mode 100644 index 0000000..ea6c15b --- /dev/null +++ b/tests/fixtures/scope-analysis/declare-function.ts @@ -0,0 +1,2 @@ +declare function f(a: number): number +f diff --git a/tests/lib/__snapshots__/scope-analysis.js.snap b/tests/lib/__snapshots__/scope-analysis.js.snap index 1d98143..3c3f62f 100644 --- a/tests/lib/__snapshots__/scope-analysis.js.snap +++ b/tests/lib/__snapshots__/scope-analysis.js.snap @@ -1008,6 +1008,100 @@ Object { } `; +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/declare-function.ts 1`] = ` +Object { + "$id": 2, + "block": Object { + "range": Array [ + 0, + 40, + ], + "type": "Program", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [ + Object { + "$id": 1, + "from": Object { + "$ref": 2, + }, + "identifier": Object { + "name": "f", + "range": Array [ + 38, + 39, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [], + "type": "global", + "upperScope": null, + "variableMap": Object { + "f": Object { + "$ref": 0, + }, + }, + "variableScope": Object { + "$ref": 2, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "f", + "range": Array [ + 17, + 18, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 0, + 37, + ], + "type": "TSEmptyBodyDeclareFunction", + }, + "parent": null, + "type": "FunctionName", + }, + ], + "eslintUsed": undefined, + "identifiers": Array [ + Object { + "name": "f", + "range": Array [ + 17, + 18, + ], + "type": "Identifier", + }, + ], + "name": "f", + "references": Array [ + Object { + "$ref": 1, + }, + ], + "scope": Object { + "$ref": 2, + }, + }, + ], +} +`; + exports[`TypeScript scope analysis tests/fixtures/scope-analysis/declare-global.ts 1`] = ` Object { "$id": 3, diff --git a/visitor-keys.js b/visitor-keys.js index f9b4284..606172e 100644 --- a/visitor-keys.js +++ b/visitor-keys.js @@ -34,6 +34,7 @@ module.exports = Evk.unionWith({ TSConstructorType: ["typeAnnotation", "parameters"], TSConstructSignature: ["typeAnnotation", "typeParameters"], TSDeclareKeyword: [], + TSEmptyBodyDeclareFunction: ["id", "typeParameters", "params", "returnType"], TSEmptyBodyFunctionDeclaration: ["id", "typeParameters", "params", "returnType"], TSEmptyBodyFunctionExpression: ["id", "typeParameters", "params", "returnType"], TSEnumDeclaration: ["members"], From efeda5785654c774bb0e773c67c390de9ef2bbc1 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Wed, 7 Nov 2018 16:28:21 +0900 Subject: [PATCH 16/29] add abstract-class fixture --- analyze-scope.js | 10 ++ .../fixtures/scope-analysis/abstract-class.ts | 4 + .../lib/__snapshots__/scope-analysis.js.snap | 95 +++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 tests/fixtures/scope-analysis/abstract-class.ts diff --git a/analyze-scope.js b/analyze-scope.js index 32af82f..d090290 100644 --- a/analyze-scope.js +++ b/analyze-scope.js @@ -526,6 +526,16 @@ class Referencer extends OriginalReferencer { this.close(node); } + TSAbstractClassDeclaration(node) { + this.ClassDeclaration(node); + } + TSAbstractClassProperty(node) { + this.ClassProperty(node); + } + TSAbstractMethodDefinition(node) { + this.MethodDefinition(node); + } + /** * Process the global augmentation. * 1. Set the global scope as the current scope. diff --git a/tests/fixtures/scope-analysis/abstract-class.ts b/tests/fixtures/scope-analysis/abstract-class.ts new file mode 100644 index 0000000..b71fd3e --- /dev/null +++ b/tests/fixtures/scope-analysis/abstract-class.ts @@ -0,0 +1,4 @@ +abstract class A { + abstract a: string + abstract f(): number +} diff --git a/tests/lib/__snapshots__/scope-analysis.js.snap b/tests/lib/__snapshots__/scope-analysis.js.snap index 3c3f62f..a854a97 100644 --- a/tests/lib/__snapshots__/scope-analysis.js.snap +++ b/tests/lib/__snapshots__/scope-analysis.js.snap @@ -178,6 +178,101 @@ Object { } `; +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/abstract-class.ts 1`] = ` +Object { + "$id": 2, + "block": Object { + "range": Array [ + 0, + 69, + ], + "type": "Program", + }, + "childScopes": Array [ + Object { + "$id": 1, + "block": Object { + "range": Array [ + 0, + 68, + ], + "type": "TSAbstractClassDeclaration", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": true, + "references": Array [], + "throughReferences": Array [], + "type": "class", + "upperScope": Object { + "$ref": 2, + }, + "variableMap": Object { + "A": Object { + "$ref": 0, + }, + }, + "variableScope": Object { + "$ref": 2, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "A", + "range": Array [ + 15, + 16, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 0, + 68, + ], + "type": "TSAbstractClassDeclaration", + }, + "parent": undefined, + "type": "ClassName", + }, + ], + "eslintUsed": undefined, + "identifiers": Array [ + Object { + "name": "A", + "range": Array [ + 15, + 16, + ], + "type": "Identifier", + }, + ], + "name": "A", + "references": Array [], + "scope": Object { + "$ref": 1, + }, + }, + ], + }, + ], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [], + "throughReferences": Array [], + "type": "global", + "upperScope": null, + "variableMap": Object {}, + "variableScope": Object { + "$ref": 2, + }, + "variables": Array [], +} +`; + exports[`TypeScript scope analysis tests/fixtures/scope-analysis/class-properties.ts 1`] = ` Object { "$id": 8, From f5de9b0c0bf034c91c4532e1ca56efeec41b1327 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Thu, 8 Nov 2018 14:16:26 +0900 Subject: [PATCH 17/29] add typeof-in-call-signature fixture --- .../typeof-in-call-signature.ts | 5 + .../lib/__snapshots__/scope-analysis.js.snap | 238 ++++++++++++++++++ visitor-keys.js | 3 +- 3 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/scope-analysis/typeof-in-call-signature.ts diff --git a/tests/fixtures/scope-analysis/typeof-in-call-signature.ts b/tests/fixtures/scope-analysis/typeof-in-call-signature.ts new file mode 100644 index 0000000..e3e90ce --- /dev/null +++ b/tests/fixtures/scope-analysis/typeof-in-call-signature.ts @@ -0,0 +1,5 @@ +const obj = { value: 1 } +interface A { + (a: typeof obj, b: T): typeof obj + new (a: typeof obj, b: T): typeof obj +} diff --git a/tests/lib/__snapshots__/scope-analysis.js.snap b/tests/lib/__snapshots__/scope-analysis.js.snap index a854a97..5fe0286 100644 --- a/tests/lib/__snapshots__/scope-analysis.js.snap +++ b/tests/lib/__snapshots__/scope-analysis.js.snap @@ -4464,6 +4464,244 @@ Object { } `; +exports[`TypeScript scope analysis tests/fixtures/scope-analysis/typeof-in-call-signature.ts 1`] = ` +Object { + "$id": 8, + "block": Object { + "range": Array [ + 0, + 165, + ], + "type": "Program", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [ + Object { + "$id": 1, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "obj", + "range": Array [ + 6, + 9, + ], + "type": "Identifier", + }, + "kind": "w", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": Object { + "range": Array [ + 12, + 24, + ], + "type": "ObjectExpression", + }, + }, + Object { + "$id": 2, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "obj", + "range": Array [ + 61, + 64, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + Object { + "$id": 3, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "obj", + "range": Array [ + 76, + 79, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + Object { + "$id": 4, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "obj", + "range": Array [ + 95, + 98, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + Object { + "$id": 5, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "obj", + "range": Array [ + 125, + 128, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + Object { + "$id": 6, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "obj", + "range": Array [ + 140, + 143, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + Object { + "$id": 7, + "from": Object { + "$ref": 8, + }, + "identifier": Object { + "name": "obj", + "range": Array [ + 159, + 162, + ], + "type": "Identifier", + }, + "kind": "r", + "resolved": Object { + "$ref": 0, + }, + "writeExpr": undefined, + }, + ], + "throughReferences": Array [], + "type": "global", + "upperScope": null, + "variableMap": Object { + "obj": Object { + "$ref": 0, + }, + }, + "variableScope": Object { + "$ref": 8, + }, + "variables": Array [ + Object { + "$id": 0, + "defs": Array [ + Object { + "name": Object { + "name": "obj", + "range": Array [ + 6, + 9, + ], + "type": "Identifier", + }, + "node": Object { + "range": Array [ + 6, + 24, + ], + "type": "VariableDeclarator", + }, + "parent": Object { + "range": Array [ + 0, + 24, + ], + "type": "VariableDeclaration", + }, + "type": "Variable", + }, + ], + "eslintUsed": undefined, + "identifiers": Array [ + Object { + "name": "obj", + "range": Array [ + 6, + 9, + ], + "type": "Identifier", + }, + ], + "name": "obj", + "references": Array [ + Object { + "$ref": 1, + }, + Object { + "$ref": 2, + }, + Object { + "$ref": 3, + }, + Object { + "$ref": 4, + }, + Object { + "$ref": 5, + }, + Object { + "$ref": 6, + }, + Object { + "$ref": 7, + }, + ], + "scope": Object { + "$ref": 8, + }, + }, + ], +} +`; + exports[`TypeScript scope analysis tests/fixtures/scope-analysis/typeof-in-return-type.ts 1`] = ` Object { "$id": 5, diff --git a/visitor-keys.js b/visitor-keys.js index 606172e..2fbefd5 100644 --- a/visitor-keys.js +++ b/visitor-keys.js @@ -31,8 +31,9 @@ module.exports = Evk.unionWith({ TSArrayType: ["elementType"], TSAsyncKeyword: [], TSBooleanKeyword: [], + TSCallSignature: ["typeParameters", "parameters", "typeAnnotation"], + TSConstructSignature: ["typeParameters", "params", "typeAnnotation"], TSConstructorType: ["typeAnnotation", "parameters"], - TSConstructSignature: ["typeAnnotation", "typeParameters"], TSDeclareKeyword: [], TSEmptyBodyDeclareFunction: ["id", "typeParameters", "params", "returnType"], TSEmptyBodyFunctionDeclaration: ["id", "typeParameters", "params", "returnType"], From 7ce5d6119de0bd7f8a433e0a91112b3aad1f5d6a Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Thu, 8 Nov 2018 14:36:08 +0900 Subject: [PATCH 18/29] add test for #416 --- tests/lib/scope-analysis.js | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/tests/lib/scope-analysis.js b/tests/lib/scope-analysis.js index a72aaba..5d639a6 100644 --- a/tests/lib/scope-analysis.js +++ b/tests/lib/scope-analysis.js @@ -4,9 +4,11 @@ */ "use strict"; +const assert = require("assert"); const fs = require("fs"); const path = require("path"); -const { parseForESLint } = require("../.."); +const { Linter } = require("eslint"); +const parser = require("../.."); /** Reference resolver. */ class ReferenceResolver { @@ -144,7 +146,7 @@ describe("TypeScript scope analysis", () => { for (const filePath of files) { test(filePath, () => { const code = fs.readFileSync(filePath, "utf8"); - const { scopeManager } = parseForESLint(code, { + const { scopeManager } = parser.parseForESLint(code, { loc: true, range: true, tokens: true, @@ -169,4 +171,24 @@ describe("TypeScript scope analysis", () => { expect(scopeTree).toMatchSnapshot(); }); } + + test("https://github.com/eslint/typescript-eslint-parser/issues/416", () => { + const linter = new Linter(); + linter.defineParser("typescript-eslint-parser", parser); + + const code = ` +export type SomeThing = { + id: string; +} +`; + const config = { + parser: "typescript-eslint-parser", + rules: { + "no-undef": "error" + } + }; + const messages = linter.verify(code, config, { filename: "issue416.ts" }); + + assert.deepStrictEqual(messages, []); + }); }); From ca4b08f536a7a90ac55496ad469e7f7c55bcf5d1 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Thu, 8 Nov 2018 14:38:27 +0900 Subject: [PATCH 19/29] add test for #435 --- tests/lib/scope-analysis.js | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/tests/lib/scope-analysis.js b/tests/lib/scope-analysis.js index 5d639a6..56d5bb6 100644 --- a/tests/lib/scope-analysis.js +++ b/tests/lib/scope-analysis.js @@ -172,10 +172,10 @@ describe("TypeScript scope analysis", () => { }); } - test("https://github.com/eslint/typescript-eslint-parser/issues/416", () => { - const linter = new Linter(); - linter.defineParser("typescript-eslint-parser", parser); + const linter = new Linter(); + linter.defineParser("typescript-eslint-parser", parser); + test("https://github.com/eslint/typescript-eslint-parser/issues/416", () => { const code = ` export type SomeThing = { id: string; @@ -191,4 +191,22 @@ export type SomeThing = { assert.deepStrictEqual(messages, []); }); + + test("https://github.com/eslint/typescript-eslint-parser/issues/435", () => { + const code = ` +interface Foo { + bar: string +} +const bar = 'blah' +`; + const config = { + parser: "typescript-eslint-parser", + rules: { + "no-use-before-define": "error" + } + }; + const messages = linter.verify(code, config, { filename: "issue435.ts" }); + + assert.deepStrictEqual(messages, []); + }); }); From 72f7120c6fa691b94cc71634cf960d17b2595d33 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Thu, 8 Nov 2018 14:40:22 +0900 Subject: [PATCH 20/29] add test for #437 --- tests/lib/scope-analysis.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/lib/scope-analysis.js b/tests/lib/scope-analysis.js index 56d5bb6..2186b77 100644 --- a/tests/lib/scope-analysis.js +++ b/tests/lib/scope-analysis.js @@ -209,4 +209,22 @@ const bar = 'blah' assert.deepStrictEqual(messages, []); }); + + test("https://github.com/eslint/typescript-eslint-parser/issues/437", () => { + const code = ` +interface Runnable { + run (): Result + toString (): string +} +`; + const config = { + parser: "typescript-eslint-parser", + rules: { + "no-undef": "error" + } + }; + const messages = linter.verify(code, config, { filename: "issue437.ts" }); + + assert.deepStrictEqual(messages, []); + }); }); From b8b86fd9914f370e295166602eae95b3e903b52f Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Thu, 8 Nov 2018 14:42:19 +0900 Subject: [PATCH 21/29] add test for #443 --- tests/lib/scope-analysis.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/lib/scope-analysis.js b/tests/lib/scope-analysis.js index 2186b77..5636de3 100644 --- a/tests/lib/scope-analysis.js +++ b/tests/lib/scope-analysis.js @@ -227,4 +227,20 @@ interface Runnable { assert.deepStrictEqual(messages, []); }); + + test("https://github.com/eslint/typescript-eslint-parser/issues/443", () => { + const code = ` +const Foo = 1; +type Foo = 1; +`; + const config = { + parser: "typescript-eslint-parser", + rules: { + "no-redeclare": "error" + } + }; + const messages = linter.verify(code, config, { filename: "issue443.ts" }); + + assert.deepStrictEqual(messages, []); + }); }); From 49ce2da91d22169ff03e4e4f82448d9034e5d84f Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Thu, 8 Nov 2018 14:47:29 +0900 Subject: [PATCH 22/29] add test for #459 --- tests/lib/scope-analysis.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/lib/scope-analysis.js b/tests/lib/scope-analysis.js index 5636de3..92932c4 100644 --- a/tests/lib/scope-analysis.js +++ b/tests/lib/scope-analysis.js @@ -243,4 +243,20 @@ type Foo = 1; assert.deepStrictEqual(messages, []); }); + + test("https://github.com/eslint/typescript-eslint-parser/issues/459", () => { + const code = ` +type foo = any; +function bar(foo: any) {} +`; + const config = { + parser: "typescript-eslint-parser", + rules: { + "no-shadow": "error" + } + }; + const messages = linter.verify(code, config, { filename: "issue.ts" }); + + assert.deepStrictEqual(messages, []); + }); }); From 13f50c3fbcb2c24aadf7e301a259c662609b9d0f Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Thu, 8 Nov 2018 14:49:50 +0900 Subject: [PATCH 23/29] add test for #466 --- tests/lib/scope-analysis.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/lib/scope-analysis.js b/tests/lib/scope-analysis.js index 92932c4..dd8118d 100644 --- a/tests/lib/scope-analysis.js +++ b/tests/lib/scope-analysis.js @@ -259,4 +259,20 @@ function bar(foo: any) {} assert.deepStrictEqual(messages, []); }); + + test("https://github.com/eslint/typescript-eslint-parser/issues/466", () => { + const code = ` +/*globals document, selector */ +const links = document.querySelectorAll( selector ) as NodeListOf +`; + const config = { + parser: "typescript-eslint-parser", + rules: { + "no-undef": "error" + } + }; + const messages = linter.verify(code, config, { filename: "issue.ts" }); + + assert.deepStrictEqual(messages, []); + }); }); From 180cbfefb23d61438d9fbde910e5c42ca1272765 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Thu, 8 Nov 2018 14:51:18 +0900 Subject: [PATCH 24/29] add test for #471 --- tests/lib/scope-analysis.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/lib/scope-analysis.js b/tests/lib/scope-analysis.js index dd8118d..dfd7cc9 100644 --- a/tests/lib/scope-analysis.js +++ b/tests/lib/scope-analysis.js @@ -275,4 +275,21 @@ const links = document.querySelectorAll( selector ) as NodeListOf assert.deepStrictEqual(messages, []); }); + + test("https://github.com/eslint/typescript-eslint-parser/issues/471", () => { + const code = ` +class X { + field = {} +} +`; + const config = { + parser: "typescript-eslint-parser", + rules: { + "no-undef": "error" + } + }; + const messages = linter.verify(code, config, { filename: "issue.ts" }); + + assert.deepStrictEqual(messages, []); + }); }); From 6e70971c73abf659d80865bf54738d2506475e91 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Thu, 8 Nov 2018 14:57:52 +0900 Subject: [PATCH 25/29] add test for #487 --- tests/lib/scope-analysis.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/lib/scope-analysis.js b/tests/lib/scope-analysis.js index dfd7cc9..e22efa1 100644 --- a/tests/lib/scope-analysis.js +++ b/tests/lib/scope-analysis.js @@ -292,4 +292,24 @@ class X { assert.deepStrictEqual(messages, []); }); + + test("https://github.com/eslint/typescript-eslint-parser/issues/487", () => { + const code = ` +export default class Test { + private status: string; + getStatus() { + return this.status; + } +} +`; + const config = { + parser: "typescript-eslint-parser", + rules: { + "no-restricted-globals": ["error", "status"] + } + }; + const messages = linter.verify(code, config, { filename: "issue.ts" }); + + assert.deepStrictEqual(messages, []); + }); }); From 81f4f3088456ccd361f9e7998db53b064bfec048 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Thu, 8 Nov 2018 15:11:42 +0900 Subject: [PATCH 26/29] add test for #535 --- tests/lib/scope-analysis.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/lib/scope-analysis.js b/tests/lib/scope-analysis.js index e22efa1..6b2ab0f 100644 --- a/tests/lib/scope-analysis.js +++ b/tests/lib/scope-analysis.js @@ -312,4 +312,22 @@ export default class Test { assert.deepStrictEqual(messages, []); }); + + test("https://github.com/eslint/typescript-eslint-parser/issues/535", () => { + const code = ` +function foo({ bar }: { bar: string }) { + console.log(bar); +} +`; + const config = { + parser: "typescript-eslint-parser", + rules: { + "no-dupe-args": "error", + "no-redeclare": "error" + } + }; + const messages = linter.verify(code, config, { filename: "issue.ts" }); + + assert.deepStrictEqual(messages, []); + }); }); From 95a83807a865ae9d4c557e8277aa69cf0d7c2b4e Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Thu, 8 Nov 2018 15:19:11 +0900 Subject: [PATCH 27/29] add test for #536 --- tests/lib/scope-analysis.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/lib/scope-analysis.js b/tests/lib/scope-analysis.js index 6b2ab0f..071ac26 100644 --- a/tests/lib/scope-analysis.js +++ b/tests/lib/scope-analysis.js @@ -330,4 +330,26 @@ function foo({ bar }: { bar: string }) { assert.deepStrictEqual(messages, []); }); + + test("https://github.com/eslint/typescript-eslint-parser/issues/535", () => { + const code = ` +import { + observable, +} from 'mobx'; + +export default class ListModalStore { + @observable + orderList: IObservableArray = observable([]); +} +`; + const config = { + parser: "typescript-eslint-parser", + rules: { + "no-unused-vars": "error" + } + }; + const messages = linter.verify(code, config, { filename: "issue.ts" }); + + assert.deepStrictEqual(messages, []); + }); }); From 044563be23b54a82dd8dc4f8449d325789595c72 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Thu, 8 Nov 2018 15:49:06 +0900 Subject: [PATCH 28/29] add test for #476 --- tests/lib/basics.js | 60 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/tests/lib/basics.js b/tests/lib/basics.js index 0217f66..2d6658b 100644 --- a/tests/lib/basics.js +++ b/tests/lib/basics.js @@ -12,9 +12,13 @@ // Requirements //------------------------------------------------------------------------------ -const path = require("path"), +const + assert = require("assert"), + path = require("path"), + { Linter } = require("eslint"), shelljs = require("shelljs"), - testUtils = require("../../tools/test-utils"); + testUtils = require("../../tools/test-utils"), + parser = require("../../"); //------------------------------------------------------------------------------ // Setup @@ -36,4 +40,56 @@ describe("basics", () => { const code = shelljs.cat(`${path.resolve(FIXTURES_DIR, filename)}.src.js`); test(`fixtures/${filename}.src`, testUtils.createSnapshotTestBlock(code)); }); + + test("https://github.com/eslint/typescript-eslint-parser/issues/476", () => { + const linter = new Linter(); + const code = ` +export const Price: React.SFC = function Price(props) {} +`; + const config = { + parser: "typescript-eslint-parser", + rules: { + test: "error" + } + }; + + linter.defineParser("typescript-eslint-parser", parser); + linter.defineRule("test", context => ({ + TSTypeReference(node) { + const name = context.getSourceCode().getText(node.typeName); + context.report({ + node, + message: "called on {{name}}", + data: { name } + }); + } + })); + + const messages = linter.verify(code, config, { filename: "issue.ts" }); + + assert.deepStrictEqual(messages, [ + { + column: 21, + endColumn: 42, + endLine: 2, + line: 2, + message: "called on React.SFC", + nodeType: "TSTypeReference", + ruleId: "test", + severity: 2, + source: "export const Price: React.SFC = function Price(props) {}" + }, + { + column: 31, + endColumn: 41, + endLine: 2, + line: 2, + message: "called on PriceProps", + nodeType: "TSTypeReference", + ruleId: "test", + severity: 2, + source: "export const Price: React.SFC = function Price(props) {}" + } + ]); + }); }); From 2a5669dfb4177bf999199b9c115afc4fda59ca30 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Fri, 9 Nov 2018 13:23:49 +0900 Subject: [PATCH 29/29] fix test to use `expect()` --- tests/lib/basics.js | 3 +-- tests/lib/scope-analysis.js | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/tests/lib/basics.js b/tests/lib/basics.js index 2d6658b..6542baf 100644 --- a/tests/lib/basics.js +++ b/tests/lib/basics.js @@ -13,7 +13,6 @@ //------------------------------------------------------------------------------ const - assert = require("assert"), path = require("path"), { Linter } = require("eslint"), shelljs = require("shelljs"), @@ -67,7 +66,7 @@ export const Price: React.SFC = function Price(props) {} const messages = linter.verify(code, config, { filename: "issue.ts" }); - assert.deepStrictEqual(messages, [ + expect(messages).toStrictEqual([ { column: 21, endColumn: 42, diff --git a/tests/lib/scope-analysis.js b/tests/lib/scope-analysis.js index 071ac26..6fb0f9a 100644 --- a/tests/lib/scope-analysis.js +++ b/tests/lib/scope-analysis.js @@ -4,7 +4,6 @@ */ "use strict"; -const assert = require("assert"); const fs = require("fs"); const path = require("path"); const { Linter } = require("eslint"); @@ -189,7 +188,7 @@ export type SomeThing = { }; const messages = linter.verify(code, config, { filename: "issue416.ts" }); - assert.deepStrictEqual(messages, []); + expect(messages).toStrictEqual([]); }); test("https://github.com/eslint/typescript-eslint-parser/issues/435", () => { @@ -207,7 +206,7 @@ const bar = 'blah' }; const messages = linter.verify(code, config, { filename: "issue435.ts" }); - assert.deepStrictEqual(messages, []); + expect(messages).toStrictEqual([]); }); test("https://github.com/eslint/typescript-eslint-parser/issues/437", () => { @@ -225,7 +224,7 @@ interface Runnable { }; const messages = linter.verify(code, config, { filename: "issue437.ts" }); - assert.deepStrictEqual(messages, []); + expect(messages).toStrictEqual([]); }); test("https://github.com/eslint/typescript-eslint-parser/issues/443", () => { @@ -241,7 +240,7 @@ type Foo = 1; }; const messages = linter.verify(code, config, { filename: "issue443.ts" }); - assert.deepStrictEqual(messages, []); + expect(messages).toStrictEqual([]); }); test("https://github.com/eslint/typescript-eslint-parser/issues/459", () => { @@ -257,7 +256,7 @@ function bar(foo: any) {} }; const messages = linter.verify(code, config, { filename: "issue.ts" }); - assert.deepStrictEqual(messages, []); + expect(messages).toStrictEqual([]); }); test("https://github.com/eslint/typescript-eslint-parser/issues/466", () => { @@ -273,7 +272,7 @@ const links = document.querySelectorAll( selector ) as NodeListOf }; const messages = linter.verify(code, config, { filename: "issue.ts" }); - assert.deepStrictEqual(messages, []); + expect(messages).toStrictEqual([]); }); test("https://github.com/eslint/typescript-eslint-parser/issues/471", () => { @@ -290,7 +289,7 @@ class X { }; const messages = linter.verify(code, config, { filename: "issue.ts" }); - assert.deepStrictEqual(messages, []); + expect(messages).toStrictEqual([]); }); test("https://github.com/eslint/typescript-eslint-parser/issues/487", () => { @@ -310,7 +309,7 @@ export default class Test { }; const messages = linter.verify(code, config, { filename: "issue.ts" }); - assert.deepStrictEqual(messages, []); + expect(messages).toStrictEqual([]); }); test("https://github.com/eslint/typescript-eslint-parser/issues/535", () => { @@ -328,7 +327,7 @@ function foo({ bar }: { bar: string }) { }; const messages = linter.verify(code, config, { filename: "issue.ts" }); - assert.deepStrictEqual(messages, []); + expect(messages).toStrictEqual([]); }); test("https://github.com/eslint/typescript-eslint-parser/issues/535", () => { @@ -350,6 +349,6 @@ export default class ListModalStore { }; const messages = linter.verify(code, config, { filename: "issue.ts" }); - assert.deepStrictEqual(messages, []); + expect(messages).toStrictEqual([]); }); });