All files / transpiler/output/codegen/resolution ScopeResolver.ts

100% Statements 8/8
90.9% Branches 10/11
100% Functions 1/1
100% Lines 8/8

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61                                                                      59x 2x             57x 57x 56x     56x 2x     2x                
/**
 * ScopeResolver - Handles scope visibility and access validation
 *
 * Extracted from CodeGenerator to reduce complexity.
 * Uses CodeGenState for all state access.
 *
 * ADR-016: Validates cross-scope member access visibility rules.
 * Issue #165: Enforces that:
 * - Cannot reference own scope by name (must use this. prefix)
 * - Cannot access private members from outside the scope
 * - Exception: global.Scope.member is allowed for explicit qualification
 */
 
import CodeGenState from "../CodeGenState";
 
/**
 * Resolves scope visibility and validates cross-scope access.
 * All methods are static - uses CodeGenState for state access.
 */
export default class ScopeResolver {
  /**
   * Validate cross-scope visibility for member access.
   * Throws an error if the access violates visibility rules.
   *
   * @param scopeName - The scope being accessed
   * @param memberName - The member being accessed
   * @param isGlobalAccess - Whether this is a global.Scope.member access
   */
  static validateCrossScopeVisibility(
    scopeName: string,
    memberName: string,
    isGlobalAccess: boolean = false,
  ): void {
    // Error if referencing own scope by name (must use this. prefix)
    // Exception: global.Scope.member is allowed for explicit qualification
    if (!isGlobalAccess && CodeGenState.currentScope === scopeName) {
      throw new Error(
        `Error: Cannot reference own scope '${scopeName}' by name. ` +
          `Use 'this.${memberName}' instead of '${scopeName}.${memberName}'`,
      );
    }
 
    // Check private member access (skip for own scope - we can access our own privates)
    const isOwnScope = CodeGenState.currentScope === scopeName;
    if (!isOwnScope) {
      const visibility = CodeGenState.symbols?.scopeMemberVisibility
        .get(scopeName)
        ?.get(memberName);
      if (visibility === "private") {
        const context = CodeGenState.currentScope
          ? `from scope '${CodeGenState.currentScope}'`
          : "from outside the scope";
        throw new Error(
          `Cannot access private member '${memberName}' of scope '${scopeName}' ${context}. ` +
            `Only public members are accessible outside their scope.`,
        );
      }
    }
  }
}