"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileIndexer = void 0;
const path_1 = __importDefault(require("path"));
const ts = __importStar(require("typescript"));
const Counter_1 = require("./Counter");
const Descriptor_1 = require("./Descriptor");
const parseHumanByteSizeIntoNumber_1 = require("./parseHumanByteSizeIntoNumber");
const Range_1 = require("./Range");
const scip = __importStar(require("./scip"));
const ScipSymbol_1 = require("./ScipSymbol");
const ts_inline = __importStar(require("./TypeScriptInternal"));
class FileIndexer {
    constructor(checker, options, input, document, globalSymbolTable, globalConstructorTable, packages, sourceFile) {
        this.checker = checker;
        this.options = options;
        this.input = input;
        this.document = document;
        this.globalSymbolTable = globalSymbolTable;
        this.globalConstructorTable = globalConstructorTable;
        this.packages = packages;
        this.sourceFile = sourceFile;
        this.localCounter = new Counter_1.Counter();
        this.propertyCounters = new Map();
        this.localSymbolTable = new Map();
        this.workingDirectoryRegExp = new RegExp(options.cwd, 'g');
    }
    index() {
        // Uncomment below if you want to skip certain files for local development.
        // if (!this.sourceFile.fileName.includes('constructor')) {
        //   return
        // }
        const byteSize = Buffer.from(this.sourceFile.getText()).length;
        if (this.options.maxFileByteSizeNumber &&
            byteSize > this.options.maxFileByteSizeNumber) {
            const humanSize = (0, parseHumanByteSizeIntoNumber_1.formatByteSizeAsHumanReadable)(byteSize);
            const humanMaxSize = (0, parseHumanByteSizeIntoNumber_1.formatByteSizeAsHumanReadable)(this.options.maxFileByteSizeNumber);
            console.log(`info: skipping file '${this.sourceFile.fileName}' because it has byte size ${humanSize} that exceeds the maximum threshold ${humanMaxSize}. ` +
                'If you intended to index this file, use the flag --max-file-byte-size to configure the maximum file size threshold.');
            return;
        }
        this.emitSourceFileOccurrence();
        this.visit(this.sourceFile);
    }
    emitSourceFileOccurrence() {
        const symbol = this.scipSymbol(this.sourceFile);
        if (symbol.isEmpty()) {
            return;
        }
        this.pushOccurrence(new scip.scip.Occurrence({
            range: [0, 0, 0],
            enclosing_range: Range_1.Range.fromNode(this.sourceFile).toLsif(),
            symbol: symbol.value,
            symbol_roles: scip.scip.SymbolRole.Definition,
        }));
        const moduleName = this.sourceFile.moduleName || path_1.default.basename(this.sourceFile.fileName);
        this.document.symbols.push(new scip.scip.SymbolInformation({
            symbol: symbol.value,
            documentation: ['```ts\nmodule "' + moduleName + '"\n```'],
        }));
    }
    visit(node) {
        if (ts.isConstructorDeclaration(node) ||
            ts.isIdentifier(node) ||
            ts.isPrivateIdentifier(node) ||
            ts.isStringLiteralLike(node)) {
            const sym = this.getTSSymbolAtLocation(node);
            if (sym) {
                this.visitSymbolOccurrence(node, sym);
            }
        }
        ts.forEachChild(node, node => this.visit(node));
    }
    // Get the ts.Symbol corresponding to the current node, potentially de-aliasing
    // the direct symbol to account for imports.
    //
    // This code is directly based off src/services/goToDefinition.ts.
    getTSSymbolAtLocation(node) {
        var _a;
        const rangeNode = ts.isConstructorDeclaration(node)
            ? ((_a = node.getFirstToken()) !== null && _a !== void 0 ? _a : node)
            : node;
        const symbol = this.checker.getSymbolAtLocation(rangeNode);
        // If this is an alias, and the request came at the declaration location
        // get the aliased symbol instead. This allows for goto def on an import e.g.
        //   import {A, B} from "mod";
        // to jump to the implementation directly.
        if ((symbol === null || symbol === void 0 ? void 0 : symbol.declarations) &&
            symbol.flags & ts.SymbolFlags.Alias &&
            node.kind === ts.SyntaxKind.Identifier &&
            (node.parent === symbol.declarations[0] ||
                ts_inline.shouldSkipAlias(symbol.declarations[0]))) {
            const aliased = this.checker.getAliasedSymbol(symbol);
            if (aliased.declarations) {
                return aliased;
            }
        }
        return symbol;
    }
    hasConstructor(classDeclaration) {
        const cached = this.globalConstructorTable.get(classDeclaration);
        if (cached !== undefined) {
            return cached;
        }
        for (const member of classDeclaration.members) {
            if (ts.isConstructorDeclaration(member)) {
                this.globalConstructorTable.set(classDeclaration, true);
                return true;
            }
        }
        this.globalConstructorTable.set(classDeclaration, false);
        return false;
    }
    getDeclarationsForPropertyAssignment(node) {
        const objectElement = ts_inline.getContainingObjectLiteralElement(node);
        if (!objectElement) {
            return;
        }
        const contextualType = this.checker.getContextualType(objectElement.parent);
        if (contextualType === undefined) {
            return;
        }
        const symbol = ts_inline.getPropertySymbolFromContextualType(objectElement, contextualType);
        return symbol === null || symbol === void 0 ? void 0 : symbol.getDeclarations();
    }
    visitSymbolOccurrence(node, sym) {
        var _a, _b;
        const isConstructor = ts.isConstructorDeclaration(node);
        // For constructors, this method is passed the declaration node and not the identifier node.
        // In either case, this method needs to get the range of the "name" of the declaration, for constructors we
        // get the firstToken which contains the text "constructor".
        const range = Range_1.Range.fromNode(isConstructor ? ((_a = node.getFirstToken()) !== null && _a !== void 0 ? _a : node) : node).toLsif();
        let role = 0;
        let declarations = (_b = this.getDeclarationsForPropertyAssignment(node)) !== null && _b !== void 0 ? _b : [];
        const isDefinitionNode = declarations.length === 0 && isDefinition(node);
        if (isDefinitionNode) {
            role |= scip.scip.SymbolRole.Definition;
        }
        if (declarations.length === 0) {
            declarations = ts.isConstructorDeclaration(node)
                ? [node]
                : isDefinitionNode
                    ? // Don't emit ambiguous definition at definition-site. You can reproduce
                        // ambiguous results by triggering "Go to definition" in VS Code on `Conflict`
                        // in the example below:
                        // export const Conflict = 42
                        // export interface Conflict {}
                        //                  ^^^^^^^^ "Go to definition" shows two results: const and interface.
                        // See https://github.com/sourcegraph/scip-typescript/pull/206 for more details.
                        [node.parent]
                    : (sym === null || sym === void 0 ? void 0 : sym.declarations) || [];
        }
        for (const declaration of declarations) {
            let scipSymbol = this.scipSymbol(declaration);
            let enclosingRange;
            if (!isDefinitionNode || scipSymbol.isEmpty() || scipSymbol.isLocal()) {
                // Skip enclosing ranges for these cases
            }
            else if (ts.isVariableDeclaration(declaration) &&
                declaration.initializer &&
                ts.isFunctionLike(declaration.initializer)) {
                enclosingRange = Range_1.Range.fromNode(declaration.initializer).toLsif();
            }
            else if (ts.isFunctionDeclaration(declaration) ||
                ts.isEnumDeclaration(declaration) ||
                ts.isTypeAliasDeclaration(declaration) ||
                ts.isClassDeclaration(declaration) ||
                ts.isMethodDeclaration(declaration) ||
                ts.isInterfaceDeclaration(declaration) ||
                ts.isConstructorDeclaration(declaration)) {
                enclosingRange = Range_1.Range.fromNode(declaration).toLsif();
            }
            if (((ts.isIdentifier(node) && ts.isNewExpression(node.parent)) ||
                (ts.isPropertyAccessExpression(node.parent) &&
                    ts.isNewExpression(node.parent.parent))) &&
                ts.isClassDeclaration(declaration) &&
                this.hasConstructor(declaration)) {
                scipSymbol = ScipSymbol_1.ScipSymbol.global(scipSymbol, (0, Descriptor_1.methodDescriptor)('<constructor>'));
            }
            if (scipSymbol.isEmpty()) {
                // Skip empty symbols
                continue;
            }
            this.pushOccurrence(new scip.scip.Occurrence({
                enclosing_range: enclosingRange,
                range,
                symbol: scipSymbol.value,
                symbol_roles: role,
            }));
            if (isDefinitionNode) {
                this.addSymbolInformation(node, sym, declaration, scipSymbol);
                this.handleShorthandPropertyDefinition(declaration, range);
                this.handleObjectBindingPattern(node, range);
                // Only emit one symbol for definitions sites, see https://github.com/sourcegraph/lsif-typescript/issues/45
                break;
            }
        }
    }
    /**
     * Emits an additional definition occurrence when destructuring an object
     * pattern. For example:
     * ```
     * interface Props { property: number}
     * const props: Props[] = [{ property: 42 }]
     * props.map(({property}) => property) = {a}
     * //          ^^^^^^^^ references `Props.property` and defines a local parameter `property`
     * ```
     */
    handleObjectBindingPattern(node, range) {
        const isObjectBindingPatternProperty = ts.isIdentifier(node) &&
            ts.isBindingElement(node.parent) &&
            ts.isObjectBindingPattern(node.parent.parent);
        if (!isObjectBindingPatternProperty) {
            return;
        }
        const tpe = this.checker.getTypeAtLocation(node.parent.parent);
        const property = tpe.getProperty(node.getText());
        for (const declaration of (property === null || property === void 0 ? void 0 : property.declarations) || []) {
            const scipSymbol = this.scipSymbol(declaration);
            if (scipSymbol.isEmpty()) {
                continue;
            }
            this.pushOccurrence(new scip.scip.Occurrence({
                range,
                symbol: scipSymbol.value,
            }));
        }
    }
    /**
     * Handles the special-case around shorthand property syntax so that we emit two occurrences instead of only one.
     * Shorthand properties need two symbols because they both define a symbol and reference a symbol. For example:
     * ```
     * const a = 42
     * const b = {a}
     * //         ^ both references the local const `a` and defines a new property
     * const c = b.a
     * //          ^ reference to the property `a`, not the local const
     * ```
     */
    handleShorthandPropertyDefinition(declaration, range) {
        if (declaration.kind !== ts.SyntaxKind.ShorthandPropertyAssignment) {
            return;
        }
        const valueSymbol = this.checker.getShorthandAssignmentValueSymbol(declaration);
        if (!valueSymbol) {
            return;
        }
        for (const symbol of (valueSymbol === null || valueSymbol === void 0 ? void 0 : valueSymbol.declarations) || []) {
            const scipSymbol = this.scipSymbol(symbol);
            if (scipSymbol.isEmpty()) {
                continue;
            }
            this.pushOccurrence(new scip.scip.Occurrence({
                range,
                symbol: scipSymbol.value,
            }));
        }
    }
    hideWorkingDirectory(value) {
        return value.replace(this.workingDirectoryRegExp, '');
    }
    addSymbolInformation(node, sym, declaration, symbol) {
        const documentation = [
            '```ts\n' +
                this.hideWorkingDirectory(this.signatureForDocumentation(node, sym)) +
                '\n```',
        ];
        const docstring = sym.getDocumentationComment(this.checker);
        if (docstring.length > 0) {
            documentation.push(ts.displayPartsToString(docstring));
        }
        this.document.symbols.push(new scip.scip.SymbolInformation({
            symbol: symbol.value,
            documentation,
            relationships: this.relationships(declaration, symbol),
        }));
    }
    pushOccurrence(occurrence) {
        const lastOccurrence = this.document.occurrences.at(-1);
        if (lastOccurrence) {
            if (isEqualOccurrence(lastOccurrence, occurrence)) {
                return;
            }
        }
        this.document.occurrences.push(occurrence);
    }
    relationships(declaration, declarationSymbol) {
        const relationships = [];
        const isAddedSymbol = new Set();
        const pushImplementation = (node, isReferences) => {
            const symbol = this.scipSymbol(node);
            if (symbol.isEmpty()) {
                return;
            }
            if (symbol.value === declarationSymbol.value) {
                return;
            }
            if (isAddedSymbol.has(symbol.value)) {
                // Avoid duplicate relationships. This can happen for overloaded methods
                // that have different ts.Symbol but the same SCIP symbol.
                return;
            }
            isAddedSymbol.add(symbol.value);
            relationships.push(new scip.scip.Relationship({
                symbol: symbol.value,
                is_implementation: true,
                is_reference: isReferences,
            }));
        };
        if (ts.isClassDeclaration(declaration)) {
            this.forEachAncestor(declaration, ancestor => {
                pushImplementation(ancestor, false);
            });
        }
        else if (ts.isMethodDeclaration(declaration) ||
            ts.isMethodSignature(declaration) ||
            ts.isPropertyAssignment(declaration) ||
            ts.isPropertyDeclaration(declaration)) {
            const declarationName = declaration.name.getText();
            this.forEachAncestor(declaration.parent, ancestor => {
                var _a;
                for (const member of ancestor.members) {
                    if (declarationName === ((_a = member.name) === null || _a === void 0 ? void 0 : _a.getText())) {
                        pushImplementation(member, true);
                    }
                }
            });
        }
        return relationships;
    }
    scipSymbol(node) {
        var _a, _b, _c;
        const fromCache = this.globalSymbolTable.get(node) || this.localSymbolTable.get(node);
        if (fromCache) {
            return fromCache;
        }
        if (ts.isBlock(node)) {
            return ScipSymbol_1.ScipSymbol.empty();
        }
        if (ts.isSourceFile(node)) {
            const package_ = this.packages.symbol(node.fileName);
            if (package_.isEmpty()) {
                return this.cached(node, ScipSymbol_1.ScipSymbol.anonymousPackage());
            }
            return this.cached(node, package_);
        }
        if (ts.isPropertyAssignment(node) ||
            ts.isShorthandPropertyAssignment(node)) {
            const name = node.name.getText();
            let counter = this.propertyCounters.get(name);
            if (!counter) {
                counter = new Counter_1.Counter();
                this.propertyCounters.set(name, counter);
            }
            return this.cached(node, ScipSymbol_1.ScipSymbol.global(this.scipSymbol(node.getSourceFile()), (0, Descriptor_1.metaDescriptor)(`${node.name.getText()}${counter.next()}`)));
        }
        if (ts.isJsxAttribute(node)) {
            // NOTE(olafurpg): the logic below is a bit convoluted but I spent several
            // hours and failed to come up with a cleaner solution. JSX attributes
            // have custom typechecking rules, as documented here
            // https://www.typescriptlang.org/docs/handbook/jsx.html#type-checking The
            // only way to access the actual symbol we want to reference appears to go
            // through the JSX opening element, which is the grandparent of the JSX
            // attribute node. Through the signature of the opening element, we get
            // the permitted attributes by querying the type of the first parameter.
            const jsxElement = node.parent.parent;
            const props = (_b = (_a = this.checker
                .getResolvedSignature(jsxElement)) === null || _a === void 0 ? void 0 : _a.getParameters()) === null || _b === void 0 ? void 0 : _b[0];
            if (props) {
                try {
                    const tpe = this.checker.getTypeOfSymbolAtLocation(props, node);
                    const property = tpe.getProperty(ts_inline.getTextOfJsxAttributeName(node.name));
                    for (const decl of (property === null || property === void 0 ? void 0 : property.declarations) || []) {
                        return this.scipSymbol(decl);
                    }
                }
                catch {
                    // TODO: https://github.com/sourcegraph/lsif-typescript/issues/34
                    // continue regardless of error, the TypeScript compiler tends to
                    // trigger stack overflows in getTypeOfSymbolAtLocation and we
                    // don't know why yet.
                }
            }
        }
        const owner = this.scipSymbol(node.parent);
        if (owner.isEmpty() || owner.isLocal()) {
            return this.newLocalSymbol(node);
        }
        if (isAnonymousContainerOfSymbols(node)) {
            return this.cached(node, this.scipSymbol(node.parent));
        }
        if (ts.isImportSpecifier(node) ||
            ts.isImportClause(node) ||
            ts.isNamespaceImport(node)) {
            const tpe = this.checker.getTypeAtLocation(node);
            for (const declaration of ((_c = tpe.symbol) === null || _c === void 0 ? void 0 : _c.declarations) || []) {
                return this.scipSymbol(declaration);
            }
        }
        const desc = this.descriptor(node);
        if (desc) {
            return this.cached(node, ScipSymbol_1.ScipSymbol.global(owner, desc));
        }
        // Fallback case: generate a local symbol. It's not a bug when this case
        // happens. For example, we hit this case for block `{}` that are local
        // symbols, which are direct children of global symbols (toplevel
        // functions).
        return this.newLocalSymbol(node);
    }
    newLocalSymbol(node) {
        const symbol = ScipSymbol_1.ScipSymbol.local(this.localCounter.next());
        this.localSymbolTable.set(node, symbol);
        return symbol;
    }
    cached(node, symbol) {
        this.globalSymbolTable.set(node, symbol);
        return symbol;
    }
    descriptor(node) {
        var _a, _b;
        if (ts.isInterfaceDeclaration(node) ||
            ts.isEnumDeclaration(node) ||
            ts.isTypeAliasDeclaration(node)) {
            return (0, Descriptor_1.typeDescriptor)(node.name.getText());
        }
        if (ts.isClassLike(node)) {
            const name = (_a = node.name) === null || _a === void 0 ? void 0 : _a.getText();
            if (name) {
                return (0, Descriptor_1.typeDescriptor)(name);
            }
        }
        if (ts.isFunctionDeclaration(node) ||
            ts.isMethodSignature(node) ||
            ts.isMethodDeclaration(node)) {
            const name = (_b = node.name) === null || _b === void 0 ? void 0 : _b.getText();
            if (name) {
                return (0, Descriptor_1.methodDescriptor)(name);
            }
        }
        if (ts.isConstructorDeclaration(node)) {
            return (0, Descriptor_1.methodDescriptor)('<constructor>');
        }
        if (ts.isPropertyDeclaration(node) ||
            ts.isPropertySignature(node) ||
            ts.isEnumMember(node) ||
            ts.isVariableDeclaration(node)) {
            return (0, Descriptor_1.termDescriptor)(node.name.getText());
        }
        if (ts.isAccessor(node)) {
            const prefix = ts.isGetAccessor(node) ? '<get>' : '<set>';
            return (0, Descriptor_1.methodDescriptor)(prefix + node.name.getText());
        }
        if (ts.isModuleDeclaration(node)) {
            return (0, Descriptor_1.packageDescriptor)(node.name.getText());
        }
        if (ts.isParameter(node)) {
            return (0, Descriptor_1.parameterDescriptor)(node.name.getText());
        }
        if (ts.isTypeParameterDeclaration(node)) {
            return (0, Descriptor_1.typeParameterDescriptor)(node.name.getText());
        }
        if (ts.isTypeReferenceNode(node)) {
            return (0, Descriptor_1.metaDescriptor)(node.typeName.getText());
        }
        if (ts.isTypeLiteralNode(node)) {
            return (0, Descriptor_1.metaDescriptor)('typeLiteral' + this.localCounter.next().toString());
        }
        return undefined;
    }
    signatureForDocumentation(node, sym) {
        var _a;
        const kind = scriptElementKind(node, sym);
        const type = () => this.checker.typeToString(this.checker.getTypeAtLocation(node));
        const asSignatureDeclaration = (node, sym) => {
            var _a;
            const declaration = (_a = sym.declarations) === null || _a === void 0 ? void 0 : _a[0];
            if (!declaration) {
                return undefined;
            }
            return ts.isConstructorDeclaration(node)
                ? node
                : ts.isFunctionDeclaration(declaration)
                    ? declaration
                    : ts.isMethodDeclaration(declaration)
                        ? declaration
                        : undefined;
        };
        const signature = () => {
            const signatureDeclaration = asSignatureDeclaration(node, sym);
            if (!signatureDeclaration) {
                return undefined;
            }
            const signature = this.checker.getSignatureFromDeclaration(signatureDeclaration);
            return signature ? this.checker.signatureToString(signature) : undefined;
        };
        switch (kind) {
            case ts.ScriptElementKind.localVariableElement:
            case ts.ScriptElementKind.variableElement: {
                return 'var ' + node.getText() + ': ' + type();
            }
            case ts.ScriptElementKind.memberVariableElement: {
                return '(property) ' + node.getText() + ': ' + type();
            }
            case ts.ScriptElementKind.parameterElement: {
                return '(parameter) ' + node.getText() + ': ' + type();
            }
            case ts.ScriptElementKind.constElement: {
                return 'const ' + node.getText() + ': ' + type();
            }
            case ts.ScriptElementKind.letElement: {
                return 'let ' + node.getText() + ': ' + type();
            }
            case ts.ScriptElementKind.alias: {
                return 'type ' + node.getText();
            }
            case ts.ScriptElementKind.classElement:
            case ts.ScriptElementKind.localClassElement: {
                if (ts.isConstructorDeclaration(node)) {
                    return 'constructor' + (signature() || '');
                }
                return 'class ' + node.getText();
            }
            case ts.ScriptElementKind.interfaceElement: {
                return 'interface ' + node.getText();
            }
            case ts.ScriptElementKind.enumElement: {
                return 'enum ' + node.getText();
            }
            case ts.ScriptElementKind.enumMemberElement: {
                let suffix = '';
                const declaration = (_a = sym.declarations) === null || _a === void 0 ? void 0 : _a[0];
                if (declaration && ts.isEnumMember(declaration)) {
                    const constantValue = this.checker.getConstantValue(declaration);
                    if (constantValue) {
                        suffix = ' = ' + constantValue.toString();
                    }
                }
                return '(enum member) ' + node.getText() + suffix;
            }
            case ts.ScriptElementKind.functionElement: {
                return 'function ' + node.getText() + (signature() || type());
            }
            case ts.ScriptElementKind.memberFunctionElement: {
                return '(method) ' + node.getText() + (signature() || type());
            }
            case ts.ScriptElementKind.memberGetAccessorElement: {
                return 'get ' + node.getText() + ': ' + type();
            }
            case ts.ScriptElementKind.memberSetAccessorElement: {
                return 'set ' + node.getText() + type();
            }
            case ts.ScriptElementKind.constructorImplementationElement: {
                return '';
            }
        }
        return node.getText() + ': ' + type();
    }
    // Invokes the `onAncestor` callback for all "ancestors" of the provided node,
    // where "ancestor" is loosely defined as the superclass or superinterface of
    // that node. The callback is invoked on the `node` parameter itself if it's
    // class-like or an interface.
    forEachAncestor(node, onAncestor) {
        const isVisited = new Set();
        const loop = (declaration) => {
            var _a, _b;
            if (isVisited.has(declaration)) {
                return;
            }
            isVisited.add(declaration);
            if (ts.isClassLike(declaration) ||
                ts.isInterfaceDeclaration(declaration)) {
                onAncestor(declaration);
            }
            if (ts.isObjectLiteralExpression(declaration)) {
                const tpe = (_a = this.checker.getContextualType(declaration)) !== null && _a !== void 0 ? _a : this.checker.getTypeAtLocation(declaration);
                for (const symbolDeclaration of ((_b = tpe.symbol) === null || _b === void 0 ? void 0 : _b.declarations) || []) {
                    loop(symbolDeclaration);
                }
            }
            else if (ts.isClassLike(declaration) ||
                ts.isInterfaceDeclaration(declaration)) {
                for (const heritageClause of (declaration === null || declaration === void 0 ? void 0 : declaration.heritageClauses) || []) {
                    for (const tpe of heritageClause.types) {
                        const ancestorSymbol = this.getTSSymbolAtLocation(tpe.expression);
                        if (ancestorSymbol) {
                            for (const ancestorDecl of ancestorSymbol.declarations || []) {
                                loop(ancestorDecl);
                            }
                        }
                    }
                }
            }
        };
        loop(node);
    }
}
exports.FileIndexer = FileIndexer;
function isAnonymousContainerOfSymbols(node) {
    return (ts.isModuleBlock(node) ||
        ts.isImportDeclaration(node) ||
        (ts.isImportClause(node) && !node.name) ||
        ts.isNamedImports(node) ||
        ts.isVariableStatement(node) ||
        ts.isVariableDeclarationList(node));
}
function scriptElementKind(node, sym) {
    const flags = sym.getFlags();
    if (flags & ts.SymbolFlags.TypeAlias) {
        return ts.ScriptElementKind.alias;
    }
    if (flags & ts.SymbolFlags.Class) {
        return ts.ScriptElementKind.classElement;
    }
    if (flags & ts.SymbolFlags.Interface) {
        return ts.ScriptElementKind.interfaceElement;
    }
    if (flags & ts.SymbolFlags.Enum) {
        return ts.ScriptElementKind.enumElement;
    }
    if (flags & ts.SymbolFlags.EnumMember) {
        return ts.ScriptElementKind.enumMemberElement;
    }
    if (flags & ts.SymbolFlags.Method) {
        return ts.ScriptElementKind.memberFunctionElement;
    }
    if (flags & ts.SymbolFlags.GetAccessor) {
        return ts.ScriptElementKind.memberGetAccessorElement;
    }
    if (flags & ts.SymbolFlags.SetAccessor) {
        return ts.ScriptElementKind.memberSetAccessorElement;
    }
    if (flags & ts.SymbolFlags.Constructor) {
        return ts.ScriptElementKind.constructorImplementationElement;
    }
    if (flags & ts.SymbolFlags.Function) {
        return ts.ScriptElementKind.functionElement;
    }
    if (flags & ts.SymbolFlags.Variable) {
        if (ts_inline.isParameter(sym)) {
            return ts.ScriptElementKind.parameterElement;
        }
        if (node.flags & ts.NodeFlags.Const) {
            return ts.ScriptElementKind.constElement;
        }
        if (node.flags & ts.NodeFlags.Let) {
            return ts.ScriptElementKind.letElement;
        }
        return ts.ScriptElementKind.variableElement;
    }
    if (flags & ts.SymbolFlags.ClassMember) {
        return ts.ScriptElementKind.memberVariableElement;
    }
    return ts.ScriptElementKind.unknown;
}
function isEqualOccurrence(a, b) {
    return (a.symbol_roles === b.symbol_roles &&
        a.symbol === b.symbol &&
        isEqualArray(a.range, b.range));
}
function isEqualArray(a, b) {
    if (a.length !== b.length) {
        return false;
    }
    for (let index = 0; index < a.length; index++) {
        if (a[index] !== b[index]) {
            return false;
        }
    }
    return true;
}
function declarationName(node) {
    if (ts.isBindingElement(node) ||
        ts.isEnumDeclaration(node) ||
        ts.isEnumMember(node) ||
        ts.isVariableDeclaration(node) ||
        ts.isPropertyDeclaration(node) ||
        ts.isAccessor(node) ||
        ts.isMethodSignature(node) ||
        ts.isMethodDeclaration(node) ||
        ts.isPropertySignature(node) ||
        ts.isFunctionDeclaration(node) ||
        ts.isModuleDeclaration(node) ||
        ts.isPropertyAssignment(node) ||
        ts.isShorthandPropertyAssignment(node) ||
        ts.isParameter(node) ||
        ts.isTypeParameterDeclaration(node) ||
        ts.isTypeAliasDeclaration(node) ||
        ts.isInterfaceDeclaration(node) ||
        ts.isClassDeclaration(node)) {
        return node.name;
    }
    return undefined;
}
/**
 * For example:
 *
 * const a = 1
 *       ^ node
 *       ^ node.parent.name
 *       ^^^^^ node.parent
 *
 * function a(): void {}
 *          ^ node
 *          ^ node.parent.name
 * ^^^^^^^^^^^^^^^^^^^^^ node.parent
 */
function isDefinition(node) {
    return (declarationName(node.parent) === node || ts.isConstructorDeclaration(node));
}
