"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFunctionDeclarationFromNode = getFunctionDeclarationFromNode;
exports.getFunctionDeclarationFromSymbol = getFunctionDeclarationFromSymbol;
exports.isFunctionLikeDeclarationBase = isFunctionLikeDeclarationBase;
exports.extractNodesBySyntaxKind = extractNodesBySyntaxKind;
exports.extractNodes = extractNodes;
exports.isAssignmentExpression = isAssignmentExpression;
exports.isAssignmentOperator = isAssignmentOperator;
exports.doesNodeCreateNewScope = doesNodeCreateNewScope;
const typescript_1 = __importDefault(require("typescript"));
/**
 * Get a function declaration from a TS node by resolving its symbol.
 */
function getFunctionDeclarationFromNode(checker, node) {
    return getFunctionDeclarationFromSymbol(checker.getSymbolAtLocation(node));
}
/**
 * Get the first function declaration with a body from a symbol.
 */
function getFunctionDeclarationFromSymbol(symbol) {
    if (!symbol?.declarations || symbol.declarations.length === 0) {
        return null;
    }
    const decl = symbol.declarations.find((x) => isFunctionLikeDeclarationBase(x) && !!x.body);
    return decl ?? null;
}
/**
 * Check if a node is a function-like declaration (function, arrow, function expression).
 */
function isFunctionLikeDeclarationBase(node) {
    switch (node.kind) {
        case typescript_1.default.SyntaxKind.FunctionDeclaration:
        case typescript_1.default.SyntaxKind.FunctionExpression:
        case typescript_1.default.SyntaxKind.ArrowFunction:
        case typescript_1.default.SyntaxKind.MethodDeclaration:
            return true;
    }
    return false;
}
/**
 * Walk a subtree and collect all nodes matching a given SyntaxKind.
 */
function extractNodesBySyntaxKind(node, kind) {
    return extractNodes(node, n => n.kind === kind);
}
/**
 * Walk a subtree and collect all nodes matching a predicate.
 */
function extractNodes(node, predicate) {
    const nodes = [];
    const visit = (n) => {
        if (predicate(n)) {
            nodes.push(n);
        }
        typescript_1.default.forEachChild(n, visit);
    };
    visit(node);
    return nodes;
}
/**
 * Check if a binary expression is an assignment (=, +=, -=, etc.).
 */
function isAssignmentExpression(node) {
    return typescript_1.default.isBinaryExpression(node) && isAssignmentOperator(node.operatorToken.kind);
}
/**
 * Check if a SyntaxKind is an assignment operator.
 */
function isAssignmentOperator(kind) {
    switch (kind) {
        case typescript_1.default.SyntaxKind.EqualsToken:
        case typescript_1.default.SyntaxKind.PlusEqualsToken:
        case typescript_1.default.SyntaxKind.MinusEqualsToken:
        case typescript_1.default.SyntaxKind.CaretEqualsToken:
        case typescript_1.default.SyntaxKind.SlashEqualsToken:
        case typescript_1.default.SyntaxKind.AsteriskEqualsToken:
        case typescript_1.default.SyntaxKind.PercentEqualsToken:
        case typescript_1.default.SyntaxKind.AmpersandEqualsToken:
        case typescript_1.default.SyntaxKind.BarEqualsToken:
        case typescript_1.default.SyntaxKind.LessThanLessThanEqualsToken:
        case typescript_1.default.SyntaxKind.GreaterThanGreaterThanEqualsToken:
        case typescript_1.default.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
        case typescript_1.default.SyntaxKind.AsteriskAsteriskEqualsToken:
        case typescript_1.default.SyntaxKind.QuestionQuestionEqualsToken:
        case typescript_1.default.SyntaxKind.AmpersandAmpersandEqualsToken:
        case typescript_1.default.SyntaxKind.BarBarEqualsToken:
            return true;
    }
    return false;
}
/**
 * Check if a node introduces a new variable scope.
 */
function doesNodeCreateNewScope(node) {
    return (typescript_1.default.isClassDeclaration(node) ||
        typescript_1.default.isFunctionDeclaration(node) ||
        typescript_1.default.isArrowFunction(node) ||
        typescript_1.default.isMethodDeclaration(node) ||
        typescript_1.default.isFunctionExpression(node) ||
        typescript_1.default.isBlock(node) ||
        typescript_1.default.isCatchClause(node));
}
//# sourceMappingURL=utils.js.map