Skip to content

⬅️ Back to Table of Contents

📄 ScopeBase

📊 Analysis Summary

Metric Count
🔧 Functions 15
🧱 Classes 1
📦 Imports 17
📊 Variables & Constants 1
📑 Type Aliases 2

📚 Table of Contents

🛠️ File Location:

📂 packages/scope-manager/src/scope/ScopeBase.ts

📦 Imports

Name Source
TSESTree @typescript-eslint/types
AST_NODE_TYPES @typescript-eslint/types
Definition ../definition
ReferenceImplicitGlobal ../referencer/Reference
ScopeManager ../ScopeManager
FunctionScope ./FunctionScope
GlobalScope ./GlobalScope
ModuleScope ./ModuleScope
Scope ./Scope
TSModuleScope ./TSModuleScope
assert ../assert
createIdGenerator ../ID
Reference ../referencer/Reference
ReferenceFlag ../referencer/Reference
ReferenceTypeFlag ../referencer/Reference
Variable ../variable
ScopeType ./ScopeType

Variables & Constants

Name Type Kind Value Exported
VARIABLE_SCOPE_TYPES Set<ScopeType> const new Set([ ScopeType.classFieldInitializer, ScopeType.classStaticBlock, ScopeT...

Functions

ScopeBase.isVariableScope(): this is VariableScope

Returns: this is VariableScope

Calls:

  • VARIABLE_SCOPE_TYPES.has
Code
private isVariableScope(): this is VariableScope {
    return VARIABLE_SCOPE_TYPES.has(this.type);
  }

ScopeBase.close(_scopeManager: ScopeManager): Scope | null

Parameters:

  • _scopeManager ScopeManager

Returns: Scope | null

Calls:

  • this.shouldStaticallyClose
  • assert (from ../assert)
  • this.leftToResolve.forEach
  • closeRef

Internal Comments:

// Try Resolving all references in this scope. (x3)

Code
public close(_scopeManager: ScopeManager): Scope | null {
    const closeRef = this.shouldStaticallyClose()
      ? this.#staticCloseRef
      : this.#dynamicCloseRef;

    // Try Resolving all references in this scope.
    assert(this.leftToResolve);
    this.leftToResolve.forEach(ref => closeRef(ref));
    this.leftToResolve = null;

    return this.upper;
  }

ScopeBase.shouldStaticallyClose(): boolean

Returns: boolean

Code
public shouldStaticallyClose(): boolean {
    return !this.#dynamic || this.type === 'global';
  }

ScopeBase.defineVariable(nameOrVariable: string | Variable, set: Map<string, Variable>, variables: Variable[], node: TSESTree.Identifier | null, def: Definition | null): void

To override by function scopes. References in default parameters isn't resolved to variables which are in their function body.

Raw JSDoc
/**
   * To override by function scopes.
   * References in default parameters isn't resolved to variables which are in their function body.
   */

Calls:

  • set.get
  • set.set
  • variables.push
  • variable.defs.push
  • this.addDeclaredVariablesOfNode
  • variable.identifiers.push
Code
protected defineVariable(
    nameOrVariable: string | Variable,
    set: Map<string, Variable>,
    variables: Variable[],
    node: TSESTree.Identifier | null,
    def: Definition | null,
  ): void {
    const name =
      typeof nameOrVariable === 'string' ? nameOrVariable : nameOrVariable.name;
    let variable = set.get(name);
    if (!variable) {
      variable =
        typeof nameOrVariable === 'string'
          ? new Variable(name, this as Scope)
          : nameOrVariable;
      set.set(name, variable);
      variables.push(variable);
    }

    if (def) {
      variable.defs.push(def);
      this.addDeclaredVariablesOfNode(variable, def.node);
      this.addDeclaredVariablesOfNode(variable, def.parent);
    }
    if (node) {
      variable.identifiers.push(node);
    }
  }

ScopeBase.delegateToUpperScope(ref: Reference): void

Parameters:

  • ref Reference

Returns: void

Calls:

  • (this.upper as AnyScope | undefined)?.leftToResolve?.push
  • this.through.push
Code
protected delegateToUpperScope(ref: Reference): void {
    (this.upper as AnyScope | undefined)?.leftToResolve?.push(ref);
    this.through.push(ref);
  }

ScopeBase.isValidResolution(_ref: Reference, _variable: Variable): boolean

Parameters:

  • _ref Reference
  • _variable Variable

Returns: boolean

Code
protected isValidResolution(_ref: Reference, _variable: Variable): boolean {
    return true;
  }

ScopeBase.addDeclaredVariablesOfNode(variable: Variable, node: TSESTree.Node | null | undefined): void

Parameters:

  • variable Variable
  • node TSESTree.Node | null | undefined

Returns: void

Calls:

  • this.#declaredVariables.get
  • this.#declaredVariables.set
  • variables.includes
  • variables.push
Code
private addDeclaredVariablesOfNode(
    variable: Variable,
    node: TSESTree.Node | null | undefined,
  ): void {
    if (node == null) {
      return;
    }

    let variables = this.#declaredVariables.get(node);

    if (variables == null) {
      variables = [];
      this.#declaredVariables.set(node, variables);
    }
    if (!variables.includes(variable)) {
      variables.push(variable);
    }
  }

ScopeBase.defineIdentifier(node: TSESTree.Identifier, def: Definition): void

Parameters:

  • node TSESTree.Identifier
  • def Definition

Returns: void

Calls:

  • this.defineVariable
Code
public defineIdentifier(node: TSESTree.Identifier, def: Definition): void {
    this.defineVariable(node.name, this.set, this.variables, node, def);
  }

ScopeBase.defineLiteralIdentifier(node: TSESTree.StringLiteral, def: Definition): void

Parameters:

  • node TSESTree.StringLiteral
  • def Definition

Returns: void

Calls:

  • this.defineVariable
Code
public defineLiteralIdentifier(
    node: TSESTree.StringLiteral,
    def: Definition,
  ): void {
    this.defineVariable(node.value, this.set, this.variables, null, def);
  }

ScopeBase.referenceDualValueType(node: TSESTree.Identifier): void

Parameters:

  • node TSESTree.Identifier

Returns: void

Calls:

  • this.references.push
  • this.leftToResolve?.push
Code
public referenceDualValueType(node: TSESTree.Identifier): void {
    const ref = new Reference(
      node,
      this as Scope,
      ReferenceFlag.Read,
      null,
      null,
      false,
      ReferenceTypeFlag.Type | ReferenceTypeFlag.Value,
    );

    this.references.push(ref);
    this.leftToResolve?.push(ref);
  }

ScopeBase.referenceType(node: TSESTree.Identifier): void

Parameters:

  • node TSESTree.Identifier

Returns: void

Calls:

  • this.references.push
  • this.leftToResolve?.push
Code
public referenceType(node: TSESTree.Identifier): void {
    const ref = new Reference(
      node,
      this as Scope,
      ReferenceFlag.Read,
      null,
      null,
      false,
      ReferenceTypeFlag.Type,
    );

    this.references.push(ref);
    this.leftToResolve?.push(ref);
  }

ScopeBase.referenceValue(…): void

Parameters:

  • node TSESTree.Identifier | TSESTree.JSXIdentifier
  • assign ReferenceFlag
  • writeExpr TSESTree.Expression | null
  • maybeImplicitGlobal ReferenceImplicitGlobal | null
  • init boolean

Returns: void

Calls:

  • this.references.push
  • this.leftToResolve?.push
Code
public referenceValue(
    node: TSESTree.Identifier | TSESTree.JSXIdentifier,
    assign: ReferenceFlag = ReferenceFlag.Read,
    writeExpr?: TSESTree.Expression | null,
    maybeImplicitGlobal?: ReferenceImplicitGlobal | null,
    init = false,
  ): void {
    const ref = new Reference(
      node,
      this as Scope,
      assign,
      writeExpr,
      maybeImplicitGlobal,
      init,
      ReferenceTypeFlag.Value,
    );

    this.references.push(ref);
    this.leftToResolve?.push(ref);
  }

isStrictScope(scope: Scope, block: TSESTree.Node, isMethodDefinition: boolean): boolean

Test if scope is strict

Raw JSDoc
/**
 * Test if scope is strict
 */

Internal Comments:

// When upper scope is exists and strict, inner scope is also strict.
// Search 'use strict' directive.

Code
function isStrictScope(
  scope: Scope,
  block: TSESTree.Node,
  isMethodDefinition: boolean,
): boolean {
  let body: TSESTree.BlockStatement | TSESTree.Program | null | undefined;

  // When upper scope is exists and strict, inner scope is also strict.
  if (scope.upper?.isStrict) {
    return true;
  }

  if (isMethodDefinition) {
    return true;
  }

  if (
    scope.type === ScopeType.class ||
    scope.type === ScopeType.conditionalType ||
    scope.type === ScopeType.functionType ||
    scope.type === ScopeType.mappedType ||
    scope.type === ScopeType.module ||
    scope.type === ScopeType.tsEnum ||
    scope.type === ScopeType.tsModule ||
    scope.type === ScopeType.type
  ) {
    return true;
  }

  if (scope.type === ScopeType.block || scope.type === ScopeType.switch) {
    return false;
  }

  if (scope.type === ScopeType.function) {
    const functionBody = block as FunctionScope['block'];
    switch (functionBody.type) {
      case AST_NODE_TYPES.ArrowFunctionExpression:
        if (functionBody.body.type !== AST_NODE_TYPES.BlockStatement) {
          return false;
        }
        body = functionBody.body;
        break;

      case AST_NODE_TYPES.Program:
        body = functionBody;
        break;

      default:
        body = functionBody.body;
    }

    if (!body) {
      return false;
    }
  } else if (scope.type === ScopeType.global) {
    body = block as GlobalScope['block'];
  } else {
    return false;
  }

  // Search 'use strict' directive.
  for (const stmt of body.body) {
    if (
      stmt.type !== AST_NODE_TYPES.ExpressionStatement ||
      stmt.directive == null
    ) {
      break;
    }

    if (stmt.directive === 'use strict') {
      return true;
    }
  }
  return false;
}

registerScope(scopeManager: ScopeManager, scope: Scope): void

Parameters:

  • scopeManager ScopeManager
  • scope Scope

Returns: void

Calls:

  • scopeManager.scopes.push
  • scopeManager.nodeToScope.get
  • scopes.push
  • scopeManager.nodeToScope.set
Code
function registerScope(scopeManager: ScopeManager, scope: Scope): void {
  scopeManager.scopes.push(scope);

  const scopes = scopeManager.nodeToScope.get(scope.block);

  if (scopes) {
    scopes.push(scope);
  } else {
    scopeManager.nodeToScope.set(scope.block, [scope]);
  }
}

Internal helpers

Declared inside another function in this file.

resolve(): boolean

Returns: boolean

Calls:

  • this.set.get
  • this.isValidResolution
  • variable.references.push

Internal Comments:

// make sure we don't match a type reference to a value variable (x2)

Code
(): boolean => {
      const name = ref.identifier.name;
      const variable = this.set.get(name);

      if (!variable) {
        return false;
      }

      if (!this.isValidResolution(ref, variable)) {
        return false;
      }

      // make sure we don't match a type reference to a value variable
      const isValidTypeReference =
        ref.isTypeReference && variable.isTypeVariable;
      const isValidValueReference =
        ref.isValueReference && variable.isValueVariable;
      if (!isValidTypeReference && !isValidValueReference) {
        return false;
      }

      variable.references.push(ref);
      ref.resolved = variable;

      return true;
    }

Classes

ScopeBase

Methods (12) — full entries under Functions

Method Signature
isVariableScope (): this is VariableScope
close (_scopeManager: ScopeManager): Scope \| null
shouldStaticallyClose (): boolean
defineVariable (nameOrVariable: string \| Variable, set: Map<string, Variable>, variables: Variable[], node: TSE...
delegateToUpperScope (ref: Reference): void
isValidResolution (_ref: Reference, _variable: Variable): boolean
addDeclaredVariablesOfNode (variable: Variable, node: TSESTree.Node \| null \| undefined): void
defineIdentifier (node: TSESTree.Identifier, def: Definition): void
defineLiteralIdentifier (node: TSESTree.StringLiteral, def: Definition): void
referenceDualValueType (node: TSESTree.Identifier): void
referenceType (node: TSESTree.Identifier): void
referenceValue (node: TSESTree.Identifier \| TSESTree.JSXIdentifier, assign: ReferenceFlag, writeExpr: TSESTree....

Type Aliases

VariableScope

type VariableScope = FunctionScope | GlobalScope | ModuleScope | TSModuleScope;

AnyScope

type AnyScope = ScopeBase<ScopeType, TSESTree.Node, Scope | null>;

Generated by Syntax Scribe