| "use strict"; |
| Object.defineProperty(exports, "__esModule", { value: true }); |
| const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); |
| const eslint_visitor_keys_1 = require("eslint-visitor-keys"); |
| const scope_manager_1 = require("./scope/scope-manager"); |
| const typescript_estree_1 = require("@typescript-eslint/typescript-estree"); |
| /** |
| * 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 function (node, definition) { |
| define.call(this, node, definition); |
| // Set `variable.eslintUsed` to tell ESLint that the variable is exported. |
| const variable = 'name' in node && |
| typeof node.name === 'string' && |
| this.set.get(node.name); |
| if (variable) { |
| variable.eslintUsed = true; |
| } |
| }; |
| } |
| class PatternVisitor extends experimental_utils_1.TSESLintScope.PatternVisitor { |
| constructor(options, rootPattern, callback) { |
| super(options, rootPattern, callback); |
| } |
| Identifier(node) { |
| super.Identifier(node); |
| if (node.decorators) { |
| this.rightHandNodes.push(...node.decorators); |
| } |
| if (node.typeAnnotation) { |
| this.rightHandNodes.push(node.typeAnnotation); |
| } |
| } |
| ArrayPattern(node) { |
| node.elements.forEach(this.visit, this); |
| if (node.decorators) { |
| this.rightHandNodes.push(...node.decorators); |
| } |
| if (node.typeAnnotation) { |
| this.rightHandNodes.push(node.typeAnnotation); |
| } |
| } |
| ObjectPattern(node) { |
| node.properties.forEach(this.visit, this); |
| if (node.decorators) { |
| this.rightHandNodes.push(...node.decorators); |
| } |
| if (node.typeAnnotation) { |
| this.rightHandNodes.push(node.typeAnnotation); |
| } |
| } |
| RestElement(node) { |
| super.RestElement(node); |
| if (node.decorators) { |
| this.rightHandNodes.push(...node.decorators); |
| } |
| if (node.typeAnnotation) { |
| this.rightHandNodes.push(node.typeAnnotation); |
| } |
| } |
| TSParameterProperty(node) { |
| this.visit(node.parameter); |
| if (node.decorators) { |
| this.rightHandNodes.push(...node.decorators); |
| } |
| } |
| } |
| class Referencer extends experimental_utils_1.TSESLintScope.Referencer { |
| constructor(options, scopeManager) { |
| super(options, scopeManager); |
| this.typeMode = false; |
| } |
| /** |
| * Override to use PatternVisitor we overrode. |
| * @param node The Identifier node to visit. |
| * @param [options] The flag to visit right-hand side nodes. |
| * @param callback The callback function for left-hand side nodes. |
| */ |
| 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 node The function node to visit. |
| */ |
| visitFunction(node) { |
| const { type, id, typeParameters, params, returnType, body } = node; |
| const scopeManager = this.scopeManager; |
| const upperScope = this.currentScope(); |
| // Process the name. |
| if (type === experimental_utils_1.AST_NODE_TYPES.FunctionDeclaration && id) { |
| upperScope.__define(id, new experimental_utils_1.TSESLintScope.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 === experimental_utils_1.AST_NODE_TYPES.TSDeclareFunction) { |
| defs.splice(i, 1); |
| identifiers.splice(i, 1); |
| break; |
| } |
| } |
| } |
| else if (type === experimental_utils_1.AST_NODE_TYPES.FunctionExpression && id) { |
| scopeManager.__nestFunctionExpressionNameScope(node); |
| } |
| // Open the function scope. |
| scopeManager.__nestFunctionScope(node, this.isInnerMethodDefinition); |
| const innerScope = this.currentScope(); |
| // Process the type parameters |
| this.visit(typeParameters); |
| // Process parameter declarations. |
| for (let i = 0; i < params.length; ++i) { |
| this.visitPattern(params[i], { processRightHandNodes: true }, (pattern, info) => { |
| if (pattern.type !== experimental_utils_1.AST_NODE_TYPES.Identifier || |
| pattern.name !== 'this') { |
| innerScope.__define(pattern, new experimental_utils_1.TSESLintScope.ParameterDefinition(pattern, node, i, info.rest)); |
| this.referencingDefaultValue(pattern, info.assignments, null, true); |
| } |
| }); |
| } |
| // Process the return type. |
| this.visit(returnType); |
| // Process the body. |
| if (body && body.type === experimental_utils_1.AST_NODE_TYPES.BlockStatement) { |
| this.visitChildren(body); |
| } |
| else { |
| this.visit(body); |
| } |
| // Close the function scope. |
| this.close(node); |
| } |
| /** |
| * Override. |
| * Visit decorators. |
| * @param node The class node to visit. |
| */ |
| visitClass(node) { |
| this.visitDecorators(node.decorators); |
| const upperTypeMode = this.typeMode; |
| this.typeMode = true; |
| if (node.superTypeParameters) { |
| this.visit(node.superTypeParameters); |
| } |
| if (node.implements) { |
| node.implements.forEach(this.visit, this); |
| } |
| this.typeMode = upperTypeMode; |
| super.visitClass(node); |
| } |
| /** |
| * Visit typeParameters. |
| * @param node The node to visit. |
| */ |
| visitTypeParameters(node) { |
| if (node.typeParameters) { |
| const upperTypeMode = this.typeMode; |
| this.typeMode = true; |
| this.visit(node.typeParameters); |
| this.typeMode = upperTypeMode; |
| } |
| } |
| /** |
| * Override. |
| */ |
| JSXOpeningElement(node) { |
| this.visit(node.name); |
| this.visitTypeParameters(node); |
| node.attributes.forEach(this.visit, this); |
| } |
| /** |
| * Override. |
| * Don't create the reference object in the type mode. |
| * @param node The Identifier node to visit. |
| */ |
| Identifier(node) { |
| this.visitDecorators(node.decorators); |
| if (!this.typeMode) { |
| super.Identifier(node); |
| } |
| this.visit(node.typeAnnotation); |
| } |
| /** |
| * Override. |
| * Visit decorators. |
| * @param node The MethodDefinition node to visit. |
| */ |
| MethodDefinition(node) { |
| this.visitDecorators(node.decorators); |
| super.MethodDefinition(node); |
| } |
| /** |
| * Don't create the reference object for the key if not computed. |
| * @param node The ClassProperty node to visit. |
| */ |
| ClassProperty(node) { |
| const upperTypeMode = this.typeMode; |
| const { computed, decorators, key, typeAnnotation, value } = node; |
| this.typeMode = false; |
| this.visitDecorators(decorators); |
| if (computed) { |
| this.visit(key); |
| } |
| this.typeMode = true; |
| this.visit(typeAnnotation); |
| this.typeMode = false; |
| this.visit(value); |
| this.typeMode = upperTypeMode; |
| } |
| /** |
| * Visit new expression. |
| * @param node The NewExpression node to visit. |
| */ |
| NewExpression(node) { |
| this.visitTypeParameters(node); |
| this.visit(node.callee); |
| node.arguments.forEach(this.visit, this); |
| } |
| /** |
| * Override. |
| * Visit call expression. |
| * @param node The CallExpression node to visit. |
| */ |
| CallExpression(node) { |
| this.visitTypeParameters(node); |
| this.visit(node.callee); |
| node.arguments.forEach(this.visit, this); |
| } |
| /** |
| * Visit optional member expression. |
| * @param node The OptionalMemberExpression node to visit. |
| */ |
| OptionalMemberExpression(node) { |
| this.visit(node.object); |
| if (node.computed) { |
| this.visit(node.property); |
| } |
| } |
| /** |
| * Visit optional call expression. |
| * @param node The OptionalMemberExpression node to visit. |
| */ |
| OptionalCallExpression(node) { |
| this.visitTypeParameters(node); |
| this.visit(node.callee); |
| node.arguments.forEach(this.visit, this); |
| } |
| /** |
| * Define the variable of this function declaration only once. |
| * Because to avoid confusion of `no-redeclare` rule by overloading. |
| * @param node The TSDeclareFunction node to visit. |
| */ |
| TSDeclareFunction(node) { |
| var _a, _b; |
| const scopeManager = this.scopeManager; |
| const upperScope = this.currentScope(); |
| const { id, typeParameters, params, returnType } = node; |
| // Ignore this if other overload have already existed. |
| if (id) { |
| const variable = upperScope.set.get(id.name); |
| const defs = (_a = variable) === null || _a === void 0 ? void 0 : _a.defs; |
| const existed = (_b = defs) === null || _b === void 0 ? void 0 : _b.some((d) => d.type === 'FunctionName'); |
| if (!existed) { |
| upperScope.__define(id, new experimental_utils_1.TSESLintScope.Definition('FunctionName', id, node, null, null, null)); |
| } |
| } |
| // Open the function scope. |
| scopeManager.__nestEmptyFunctionScope(node); |
| const innerScope = this.currentScope(); |
| // Process the type parameters |
| this.visit(typeParameters); |
| // Process parameter declarations. |
| for (let i = 0; i < params.length; ++i) { |
| this.visitPattern(params[i], { processRightHandNodes: true }, (pattern, info) => { |
| innerScope.__define(pattern, new experimental_utils_1.TSESLintScope.ParameterDefinition(pattern, node, i, info.rest)); |
| // Set `variable.eslintUsed` to tell ESLint that the variable is used. |
| const variable = innerScope.set.get(pattern.name); |
| if (variable) { |
| variable.eslintUsed = true; |
| } |
| this.referencingDefaultValue(pattern, info.assignments, null, true); |
| }); |
| } |
| // Process the return type. |
| this.visit(returnType); |
| // Close the function scope. |
| this.close(node); |
| } |
| /** |
| * Create reference objects for the references in parameters and return type. |
| * @param node The TSEmptyBodyFunctionExpression node to visit. |
| */ |
| 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; |
| } |
| /** |
| * 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 node The TSInterfaceDeclaration node to visit. |
| */ |
| TSInterfaceDeclaration(node) { |
| this.visitTypeNodes(node); |
| } |
| /** |
| * 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 node The TSClassImplements node to visit. |
| */ |
| TSClassImplements(node) { |
| this.visitTypeNodes(node); |
| } |
| /** |
| * 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 node The TSIndexSignature node to visit. |
| */ |
| TSIndexSignature(node) { |
| this.visitTypeNodes(node); |
| } |
| /** |
| * Visit type assertion. |
| * @param node The TSTypeAssertion node to visit. |
| */ |
| TSTypeAssertion(node) { |
| if (this.typeMode) { |
| this.visit(node.typeAnnotation); |
| } |
| else { |
| this.typeMode = true; |
| this.visit(node.typeAnnotation); |
| this.typeMode = false; |
| } |
| this.visit(node.expression); |
| } |
| /** |
| * Visit as expression. |
| * @param node The TSAsExpression node to visit. |
| */ |
| TSAsExpression(node) { |
| this.visit(node.expression); |
| if (this.typeMode) { |
| this.visit(node.typeAnnotation); |
| } |
| else { |
| this.typeMode = true; |
| this.visit(node.typeAnnotation); |
| this.typeMode = false; |
| } |
| } |
| /** |
| * Switch to the type mode and visit child nodes to find `typeof x` expression in type declarations. |
| * @param node The TSTypeAnnotation node to visit. |
| */ |
| TSTypeAnnotation(node) { |
| this.visitTypeNodes(node); |
| } |
| /** |
| * Switch to the type mode and visit child nodes to find `typeof x` expression in type declarations. |
| * @param node The TSTypeParameterDeclaration node to visit. |
| */ |
| TSTypeParameterDeclaration(node) { |
| this.visitTypeNodes(node); |
| } |
| /** |
| * Create reference objects for the references in `typeof` expression. |
| * @param node The TSTypeQuery node to visit. |
| */ |
| TSTypeQuery(node) { |
| if (this.typeMode) { |
| this.typeMode = false; |
| this.visitChildren(node); |
| this.typeMode = true; |
| } |
| else { |
| this.visitChildren(node); |
| } |
| } |
| /** |
| * @param node The TSTypeParameter node to visit. |
| */ |
| TSTypeParameter(node) { |
| this.visitTypeNodes(node); |
| } |
| /** |
| * @param node The TSInferType node to visit. |
| */ |
| TSInferType(node) { |
| this.visitTypeNodes(node); |
| } |
| /** |
| * @param node The TSTypeReference node to visit. |
| */ |
| TSTypeReference(node) { |
| this.visitTypeNodes(node); |
| } |
| /** |
| * @param node The TSTypeLiteral node to visit. |
| */ |
| TSTypeLiteral(node) { |
| this.visitTypeNodes(node); |
| } |
| /** |
| * @param node The TSLiteralType node to visit. |
| */ |
| TSLiteralType(node) { |
| this.visitTypeNodes(node); |
| } |
| /** |
| * @param node The TSIntersectionType node to visit. |
| */ |
| TSIntersectionType(node) { |
| this.visitTypeNodes(node); |
| } |
| /** |
| * @param node The TSConditionalType node to visit. |
| */ |
| TSConditionalType(node) { |
| this.visitTypeNodes(node); |
| } |
| /** |
| * @param node The TSIndexedAccessType node to visit. |
| */ |
| TSIndexedAccessType(node) { |
| this.visitTypeNodes(node); |
| } |
| /** |
| * @param node The TSMappedType node to visit. |
| */ |
| TSMappedType(node) { |
| this.visitTypeNodes(node); |
| } |
| /** |
| * @param node The TSOptionalType node to visit. |
| */ |
| TSOptionalType(node) { |
| this.visitTypeNodes(node); |
| } |
| /** |
| * @param node The TSParenthesizedType node to visit. |
| */ |
| TSParenthesizedType(node) { |
| this.visitTypeNodes(node); |
| } |
| /** |
| * @param node The TSRestType node to visit. |
| */ |
| TSRestType(node) { |
| this.visitTypeNodes(node); |
| } |
| /** |
| * @param node The TSTupleType node to visit. |
| */ |
| TSTupleType(node) { |
| this.visitTypeNodes(node); |
| } |
| /** |
| * Create reference objects for the object part. (This is `obj.prop`) |
| * @param node The TSQualifiedName node to visit. |
| */ |
| TSQualifiedName(node) { |
| this.visit(node.left); |
| } |
| /** |
| * Create reference objects for the references in computed keys. |
| * @param node The TSPropertySignature node to visit. |
| */ |
| 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 node The TSMethodSignature node to visit. |
| */ |
| TSMethodSignature(node) { |
| const upperTypeMode = this.typeMode; |
| const { computed, key, typeParameters, params, returnType } = node; |
| if (computed) { |
| this.typeMode = false; |
| this.visit(key); |
| this.typeMode = true; |
| } |
| else { |
| this.typeMode = true; |
| this.visit(key); |
| } |
| this.visit(typeParameters); |
| params.forEach(this.visit, this); |
| this.visit(returnType); |
| this.typeMode = upperTypeMode; |
| } |
| /** |
| * 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 node The TSEnumDeclaration node to visit. |
| */ |
| TSEnumDeclaration(node) { |
| const { id, members } = node; |
| const scopeManager = this.scopeManager; |
| const scope = this.currentScope(); |
| if (id) { |
| scope.__define(id, new experimental_utils_1.TSESLintScope.Definition('EnumName', id, node)); |
| } |
| scopeManager.__nestEnumScope(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 node The TSEnumMember node to visit. |
| */ |
| TSEnumMember(node) { |
| const { id, initializer } = node; |
| const scope = this.currentScope(); |
| scope.__define(id, new experimental_utils_1.TSESLintScope.Definition('EnumMemberName', id, node)); |
| if (initializer) { |
| scope.__referencing(id, experimental_utils_1.TSESLintScope.Reference.WRITE, initializer, null, false, true); |
| this.visit(initializer); |
| } |
| } |
| /** |
| * Create the variable object for the module name, and visit children. |
| * @param node The TSModuleDeclaration node to visit. |
| */ |
| TSModuleDeclaration(node) { |
| const scope = this.currentScope(); |
| const { id, body } = node; |
| if (node.global) { |
| this.visitGlobalAugmentation(node); |
| return; |
| } |
| if (id && id.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { |
| scope.__define(id, new experimental_utils_1.TSESLintScope.Definition('NamespaceName', id, node, null, null, null)); |
| } |
| this.visit(body); |
| } |
| TSTypeAliasDeclaration(node) { |
| this.typeMode = true; |
| this.visitChildren(node); |
| this.typeMode = false; |
| } |
| /** |
| * Process the module block. |
| * @param node The TSModuleBlock node to visit. |
| */ |
| TSModuleBlock(node) { |
| this.scopeManager.__nestBlockScope(node); |
| this.visitChildren(node); |
| this.close(node); |
| } |
| TSAbstractClassProperty(node) { |
| this.ClassProperty(node); |
| } |
| TSAbstractMethodDefinition(node) { |
| this.MethodDefinition(node); |
| } |
| /** |
| * Process import equal declaration |
| * @param node The TSImportEqualsDeclaration node to visit. |
| */ |
| TSImportEqualsDeclaration(node) { |
| const { id, moduleReference } = node; |
| if (id && id.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { |
| this.currentScope().__define(id, new experimental_utils_1.TSESLintScope.Definition('ImportBinding', id, node, null, null, null)); |
| } |
| this.visit(moduleReference); |
| } |
| /** |
| * 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 node The TSModuleDeclaration node to visit. |
| */ |
| 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; |
| // Skip TSModuleBlock to avoid to create that block scope. |
| if (node.body && node.body.type === experimental_utils_1.AST_NODE_TYPES.TSModuleBlock) { |
| node.body.body.forEach(this.visit, this); |
| } |
| scopeManager.__currentScope = currentScope; |
| globalScope.__define = originalDefine; |
| } |
| /** |
| * Process decorators. |
| * @param decorators The decorator nodes to visit. |
| */ |
| visitDecorators(decorators) { |
| if (decorators) { |
| decorators.forEach(this.visit, this); |
| } |
| } |
| /** |
| * Process all child of type nodes |
| * @param node node to be processed |
| */ |
| visitTypeNodes(node) { |
| if (this.typeMode) { |
| this.visitChildren(node); |
| } |
| else { |
| this.typeMode = true; |
| this.visitChildren(node); |
| this.typeMode = false; |
| } |
| } |
| } |
| function analyzeScope(ast, parserOptions) { |
| var _a; |
| const options = { |
| ignoreEval: true, |
| optimistic: false, |
| directive: false, |
| nodejsScope: parserOptions.sourceType === 'script' && |
| (parserOptions.ecmaFeatures && |
| parserOptions.ecmaFeatures.globalReturn) === true, |
| impliedStrict: false, |
| sourceType: parserOptions.sourceType, |
| ecmaVersion: (_a = parserOptions.ecmaVersion, (_a !== null && _a !== void 0 ? _a : 2018)), |
| childVisitorKeys: typescript_estree_1.visitorKeys, |
| fallback: eslint_visitor_keys_1.getKeys, |
| }; |
| const scopeManager = new scope_manager_1.ScopeManager(options); |
| const referencer = new Referencer(options, scopeManager); |
| referencer.visit(ast); |
| return scopeManager; |
| } |
| exports.analyzeScope = analyzeScope; |
| //# sourceMappingURL=analyze-scope.js.map |