@anysphere/scip-typescript
Version:
SCIP indexer for TypeScript and JavaScript
740 lines (739 loc) • 32 kB
JavaScript
"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],
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;
}
visitSymbolOccurrence(node, sym) {
const range = Range_1.Range.fromNode(node).toLsif();
let role = 0;
const isDefinitionNode = isDefinition(node);
if (isDefinitionNode) {
role |= scip.scip.SymbolRole.Definition;
}
const 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);
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({
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;
if (isVisited.has(declaration)) {
return;
}
isVisited.add(declaration);
if (ts.isClassLike(declaration) ||
ts.isInterfaceDeclaration(declaration)) {
onAncestor(declaration);
}
if (ts.isObjectLiteralExpression(declaration)) {
const tpe = this.inferredTypeOfObjectLiteral(declaration.parent, declaration);
for (const symbolDeclaration of ((_a = tpe.symbol) === null || _a === void 0 ? void 0 : _a.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);
}
// Returns the "inferred" type of the provided object literal, where
// "inferred" is loosely defined as the type that is expected in the position
// where the object literal appears. For example, the object literal in
// `const x: SomeInterface = {y: 42}` has the inferred type `SomeInterface`
// even if `this.checker.getTypeAtLocation({y: 42})` does not return
// `SomeInterface`. The object literal could satisfy many types, but in this
// particular location must only satisfy `SomeInterface`.
inferredTypeOfObjectLiteral(node, literal) {
if (ts.isIfStatement(node) ||
ts.isForStatement(node) ||
ts.isForInStatement(node) ||
ts.isForOfStatement(node) ||
ts.isWhileStatement(node) ||
ts.isDoStatement(node) ||
ts.isReturnStatement(node) ||
ts.isBlock(node)) {
return this.inferredTypeOfObjectLiteral(node.parent, literal);
}
if (ts.isVariableDeclaration(node)) {
// Example, return `SomeInterface` from `const x: SomeInterface = {y: 42}`.
return this.checker.getTypeAtLocation(node.name);
}
if (ts.isFunctionLike(node)) {
const functionType = this.checker.getTypeAtLocation(node);
const callSignatures = functionType.getCallSignatures();
if (callSignatures.length > 0) {
return callSignatures[0].getReturnType();
}
}
if (ts.isCallOrNewExpression(node)) {
// Example: return the type of the second parameter of `someMethod` from
// the expression `someMethod(someParameter, {y: 42})`.
const signature = this.checker.getResolvedSignature(node);
for (const [index, argument] of (node.arguments || []).entries()) {
if (argument === literal) {
const parameterSymbol = signature === null || signature === void 0 ? void 0 : signature.getParameters()[index];
if (parameterSymbol) {
return this.checker.getTypeOfSymbolAtLocation(parameterSymbol, node);
}
}
}
}
return this.checker.getTypeAtLocation(literal);
}
}
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));
}