/**
 * Phase 2b: Null Safety Checker
 *
 * Validates use<T> lifetime safety to prevent dangling references.
 *
 * Key Rules:
 * - use<T> cannot outlive the owned reference (own<T> or share<T>)
 * - use<T> cannot be stored in fields (temporary only)
 * - use<T> cannot be returned from functions
 * - use<T> parameters are valid only within function scope
 *
 * Error Codes: GS401-GS499
 */
import { Ownership, } from '../ir/types.js';
/**
 * Null Safety Checker
 *
 * Ensures use<T> references cannot outlive their owned counterparts.
 * Only enforced in ownership mode (not GC mode).
 */
export class NullChecker {
    diagnostics = [];
    scopeStack = [];
    memoryMode;
    constructor(memoryMode = 'ownership') {
        this.memoryMode = memoryMode;
    }
    /**
     * Analyze a module for null safety violations
     *
     * In GC mode: Analysis is skipped (use<T> lifetime managed by GC)
     * In ownership mode: use<T> lifetime violations are errors
     */
    analyze(module) {
        // Skip null safety checks in GC mode
        if (this.memoryMode === 'gc') {
            return [];
        }
        this.diagnostics = [];
        this.scopeStack = [];
        // Check classes for use<T> fields (forbidden)
        for (const decl of module.declarations) {
            if (decl.kind === 'class') {
                this.checkClassFields(decl, module.path);
            }
            else if (decl.kind === 'interface') {
                this.checkInterfaceProperties(decl, module.path);
            }
            else if (decl.kind === 'function') {
                this.checkFunction(decl, module.path);
            }
        }
        return this.diagnostics;
    }
    /**
     * Check class fields for use<T> (GS401)
     */
    checkClassFields(classDecl, modulePath) {
        for (const field of classDecl.fields) {
            if (this.hasUseOwnership(field.type)) {
                this.addError('GS401', `Class field '${field.name}' cannot use 'use<T>' ownership. Use 'own<T>' or 'share<T>' instead.`, modulePath, 0, 0);
            }
        }
    }
    /**
     * Check interface properties for use<T> (GS401)
     */
    checkInterfaceProperties(interfaceDecl, modulePath) {
        for (const prop of interfaceDecl.properties) {
            if (this.hasUseOwnership(prop.type)) {
                this.addError('GS401', `Interface property '${prop.name}' cannot use 'use<T>' ownership. Use 'own<T>' or 'share<T>' instead.`, modulePath, prop.location?.line ?? 0, prop.location?.column ?? 0);
            }
        }
    }
    /**
     * Check function for use<T> violations
     */
    checkFunction(func, modulePath) {
        // Enter function scope
        this.pushScope('function');
        // Track use<T> parameters
        for (const param of func.params) {
            if (this.hasUseOwnership(param.type)) {
                this.currentScope().useRefs.add(param.name);
            }
            else if (this.hasOwningType(param.type)) {
                this.currentScope().ownedRefs.add(param.name);
            }
        }
        // Check return type (GS402)
        if (this.hasUseOwnership(func.returnType)) {
            this.addError('GS402', `function '${func.name}' cannot return 'use<T>'. Use 'own<T>' or 'share<T>' instead.`, modulePath, func.source?.line ?? 0, func.source?.column ?? 0);
        }
        // Check function body
        // Support both AST-level (IRFunctionBody) and SSA-level (IRBlock) bodies
        if (func.body && 'instructions' in func.body) {
            // SSA-level body (IRBlock) - used in tests
            for (const instruction of func.body.instructions) {
                this.checkInstruction(instruction, modulePath);
            }
            if (func.body.terminator) {
                this.checkTerminator(func.body.terminator, modulePath, func.returnType);
            }
        }
        // TODO: Check AST-level function body statements (IRFunctionBody)
        // For now, skip null-checking of AST-level bodies
        // This will be implemented when we add proper AST-level analysis
        // Exit function scope
        this.popScope();
    }
    /**
     * Check instruction for use<T> violations
     */
    checkInstruction(instruction, modulePath) {
        switch (instruction.kind) {
            case 'assign':
                // Track variable ownership based on assignment type
                if (this.hasUseOwnership(instruction.type)) {
                    this.currentScope().useRefs.add(instruction.target.name);
                }
                else if (this.hasOwningType(instruction.type)) {
                    this.currentScope().ownedRefs.add(instruction.target.name);
                }
                // Check the value expression
                this.checkExpression(instruction.value, modulePath);
                break;
            case 'call':
                // Check callee and arguments
                this.checkExpression(instruction.callee, modulePath);
                for (const arg of instruction.args) {
                    this.checkExpression(arg, modulePath);
                }
                break;
            case 'fieldAssign':
                this.checkExpression(instruction.object, modulePath);
                this.checkExpression(instruction.value, modulePath);
                break;
            case 'expr':
                this.checkExpression(instruction.value, modulePath);
                break;
        }
    }
    /**
     * Check terminator for use<T> violations (GS403)
     */
    checkTerminator(terminator, modulePath, _returnType) {
        if (terminator.kind === 'return' && terminator.value) {
            // Check if returning a use<T> variable
            if (terminator.value.kind === 'variable') {
                const varName = terminator.value.name;
                if (this.currentScope().useRefs.has(varName)) {
                    this.addError('GS403', `Cannot return 'use<T>' variable '${varName}'. Convert to owned or shared reference first.`, modulePath, 0, 0);
                }
            }
            // Check the return value expression
            this.checkExpression(terminator.value, modulePath);
        }
    }
    /**
     * Check statement for use<T> violations
     * @deprecated This method is for AST-level IR. Use checkInstruction for SSA-level IR.
     */
    // @ts-expect-error - Deprecated method for old AST-level IR
    checkStatement(_stmt, _modulePath) {
        // This method is no longer used with IRBlock-based functions
        // All checking is now done via checkInstruction and terminator analysis
    }
    /**
     * Check expression for use<T> violations
     */
    checkExpression(expr, modulePath) {
        switch (expr.kind) {
            case 'callExpr':
                this.checkExpression(expr.callee, modulePath);
                for (const arg of expr.args) {
                    this.checkExpression(arg, modulePath);
                }
                break;
            case 'member':
                this.checkExpression(expr.object, modulePath);
                break;
            case 'index':
                this.checkExpression(expr.object, modulePath);
                this.checkExpression(expr.index, modulePath);
                break;
            case 'binary':
                this.checkExpression(expr.left, modulePath);
                this.checkExpression(expr.right, modulePath);
                break;
            case 'unary':
                this.checkExpression(expr.operand, modulePath);
                break;
            case 'array':
                for (const element of expr.elements) {
                    this.checkExpression(element, modulePath);
                }
                break;
            case 'object':
                for (const prop of expr.properties) {
                    this.checkExpression(prop.value, modulePath);
                }
                break;
            case 'move':
            case 'borrow':
                this.checkExpression(expr.source, modulePath);
                break;
            // Base cases: literal, variable, new
            case 'literal':
            case 'variable':
            case 'new':
                // No nested expressions to check
                break;
        }
    }
    /**
     * Check return expression for use<T> variables (GS403)
     */
    // @ts-expect-error - Temporarily unused, will be used when SSA-level analysis is implemented
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    checkReturnExpression(_expr, _modulePath, _location) {
        // Check if expression is a use<T> variable reference
        if (_expr.kind === 'variable') {
            const varName = _expr.name;
            if (this.isUseRef(varName)) {
                this.addError('GS403', `Cannot return 'use<T>' reference '${varName}'. Use 'own<T>' or 'share<T>' instead.`, _modulePath, _location?.line ?? _expr.source?.line ?? 0, _location?.column ?? _expr.source?.column ?? 0);
            }
        }
    }
    /**
     * Check if a type has use<T> ownership
     */
    hasUseOwnership(type) {
        if (type.kind === 'class' || type.kind === 'interface') {
            return type.ownership === Ownership.Use;
        }
        if (type.kind === 'array') {
            return type.ownership === Ownership.Use || this.hasUseOwnership(type.element);
        }
        if (type.kind === 'map') {
            return (type.ownership === Ownership.Use ||
                this.hasUseOwnership(type.key) ||
                this.hasUseOwnership(type.value));
        }
        if (type.kind === 'nullable') {
            return this.hasUseOwnership(type.inner);
        }
        return false;
    }
    /**
     * Check if a type has owning semantics (own<T> or share<T>)
     */
    hasOwningType(type) {
        if (type.kind === 'class' || type.kind === 'interface') {
            return type.ownership === Ownership.Own || type.ownership === Ownership.Share;
        }
        if (type.kind === 'array') {
            return (type.ownership === Ownership.Own ||
                type.ownership === Ownership.Share ||
                this.hasOwningType(type.element));
        }
        if (type.kind === 'map') {
            return (type.ownership === Ownership.Own ||
                type.ownership === Ownership.Share ||
                this.hasOwningType(type.key) ||
                this.hasOwningType(type.value));
        }
        if (type.kind === 'nullable') {
            return this.hasOwningType(type.inner);
        }
        return false;
    }
    /**
     * Check if a variable is a use<T> reference
     */
    isUseRef(varName) {
        for (let i = this.scopeStack.length - 1; i >= 0; i--) {
            if (this.scopeStack[i].useRefs.has(varName)) {
                return true;
            }
        }
        return false;
    }
    /**
     * Push a new lifetime scope
     */
    pushScope(kind) {
        this.scopeStack.push({
            kind,
            useRefs: new Set(),
            ownedRefs: new Set(),
        });
    }
    /**
     * Pop the current lifetime scope
     */
    popScope() {
        this.scopeStack.pop();
    }
    /**
     * Get current scope
     */
    currentScope() {
        return this.scopeStack[this.scopeStack.length - 1];
    }
    /**
     * Add an error diagnostic
     */
    addError(code, message, file, line, column) {
        this.diagnostics.push({
            code,
            message,
            severity: 'error',
            location: { file, line, column },
        });
    }
}
/**
 * Analyze a module for null safety violations
 *
 * @param module - The IR module to analyze
 * @param memoryMode - Memory management mode ('gc' or 'ownership')
 */
export function analyzeNullSafety(module, memoryMode = 'ownership') {
    const checker = new NullChecker(memoryMode);
    return checker.analyze(module);
}
//# sourceMappingURL=null-checker.js.map