text: "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  exports.analyzeScope = void 0;
  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) {
          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 = variable === null || variable === void 0 ? void 0 : variable.defs;
              const existed = defs === null || defs === void 0 ? void 0 : defs.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, _b;
      const options = {
          ignoreEval: true,
          optimistic: false,
          directive: false,
          nodejsScope: parserOptions.sourceType === 'script' &&
              ((_a = parserOptions.ecmaFeatures) === null || _a === void 0 ? void 0 : _a.globalReturn) === true,
          impliedStrict: false,
          sourceType: parserOptions.sourceType,
          ecmaVersion: (_b = parserOptions.ecmaVersion) !== null && _b !== void 0 ? _b : 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