• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/* @internal */
2namespace ts.GoToDefinition {
3    export function getDefinitionAtPosition(program: Program, sourceFile: SourceFile, position: number, searchOtherFilesOnly?: boolean, stopAtAlias?: boolean): readonly DefinitionInfo[] | undefined {
4        const resolvedRef = getReferenceAtPosition(sourceFile, position, program);
5        const fileReferenceDefinition = resolvedRef && [getDefinitionInfoForFileReference(resolvedRef.reference.fileName, resolvedRef.fileName, resolvedRef.unverified)] || emptyArray;
6        if (resolvedRef?.file) {
7            // If `file` is missing, do a symbol-based lookup as well
8            return fileReferenceDefinition;
9        }
10
11        const node = getTouchingPropertyName(sourceFile, position);
12        if (node === sourceFile) {
13            return undefined;
14        }
15
16        const { parent } = node;
17        const typeChecker = program.getTypeChecker();
18
19        if (node.kind === SyntaxKind.OverrideKeyword || (isIdentifier(node) && isJSDocOverrideTag(parent) && parent.tagName === node)) {
20            return getDefinitionFromOverriddenMember(typeChecker, node) || emptyArray;
21        }
22
23        // Labels
24        if (isJumpStatementTarget(node)) {
25            const label = getTargetLabel(node.parent, node.text);
26            return label ? [createDefinitionInfoFromName(typeChecker, label, ScriptElementKind.label, node.text, /*containerName*/ undefined!)] : undefined; // TODO: GH#18217
27        }
28
29        if (node.kind === SyntaxKind.ReturnKeyword) {
30            const functionDeclaration = findAncestor(node.parent, n =>
31                isClassStaticBlockDeclaration(n) ? "quit" : isFunctionLikeDeclaration(n)) as FunctionLikeDeclaration | undefined;
32            return functionDeclaration ? [createDefinitionFromSignatureDeclaration(typeChecker, functionDeclaration)] : undefined;
33        }
34
35        if (isStaticModifier(node) && isClassStaticBlockDeclaration(node.parent)) {
36            const classDecl = node.parent.parent;
37            const { symbol, failedAliasResolution } = getSymbol(classDecl, typeChecker, stopAtAlias);
38
39            const staticBlocks = filter(classDecl.members, isClassStaticBlockDeclaration);
40            const containerName = symbol ? typeChecker.symbolToString(symbol, classDecl) : "";
41            const sourceFile = node.getSourceFile();
42            return map(staticBlocks, staticBlock => {
43                let { pos } = moveRangePastModifiers(staticBlock);
44                pos = skipTrivia(sourceFile.text, pos);
45                return createDefinitionInfoFromName(typeChecker, staticBlock, ScriptElementKind.constructorImplementationElement, "static {}", containerName, /*unverified*/ false, failedAliasResolution, { start: pos, length: "static".length });
46            });
47        }
48
49        let { symbol, failedAliasResolution } = getSymbol(node, typeChecker, stopAtAlias);
50        let fallbackNode = node;
51
52        if (searchOtherFilesOnly && failedAliasResolution) {
53            // We couldn't resolve the specific import, try on the module specifier.
54            const importDeclaration = forEach([node, ...symbol?.declarations || emptyArray], n => findAncestor(n, isAnyImportOrBareOrAccessedRequire));
55            const moduleSpecifier = importDeclaration && tryGetModuleSpecifierFromDeclaration(importDeclaration);
56            if (moduleSpecifier) {
57                ({ symbol, failedAliasResolution } = getSymbol(moduleSpecifier, typeChecker, stopAtAlias));
58                fallbackNode = moduleSpecifier;
59            }
60        }
61
62        if (!symbol && isModuleSpecifierLike(fallbackNode)) {
63            // We couldn't resolve the module specifier as an external module, but it could
64            // be that module resolution succeeded but the target was not a module.
65            const ref = sourceFile.resolvedModules?.get(fallbackNode.text, getModeForUsageLocation(sourceFile, fallbackNode));
66            if (ref) {
67                return [{
68                    name: fallbackNode.text,
69                    fileName: ref.resolvedFileName,
70                    containerName: undefined!,
71                    containerKind: undefined!,
72                    kind: ScriptElementKind.scriptElement,
73                    textSpan: createTextSpan(0, 0),
74                    failedAliasResolution,
75                    isAmbient: isDeclarationFileName(ref.resolvedFileName),
76                    unverified: fallbackNode !== node,
77                }];
78            }
79        }
80
81        // Could not find a symbol e.g. node is string or number keyword,
82        // or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol
83        if (!symbol) {
84            return concatenate(fileReferenceDefinition, getDefinitionInfoForIndexSignatures(node, typeChecker));
85        }
86
87        if (searchOtherFilesOnly && every(symbol.declarations, d => d.getSourceFile().fileName === sourceFile.fileName)) return undefined;
88
89        if (parent.kind === SyntaxKind.CallExpression || (parent.kind === SyntaxKind.EtsComponentExpression && isCalledStructDeclaration(symbol.getDeclarations()))) {
90            const declarations = symbol.getDeclarations();
91            if (declarations?.length && declarations[0].kind === SyntaxKind.StructDeclaration) {
92                return getDefinitionFromSymbol(typeChecker, symbol, node);
93            }
94        }
95
96        const calledDeclaration = tryGetSignatureDeclaration(typeChecker, node);
97        const compilerOptions = program.getCompilerOptions();
98        // Don't go to the component constructor definition for a JSX element, just go to the component definition.
99        if (calledDeclaration && !(isJsxOpeningLikeElement(node.parent) && isConstructorLike(calledDeclaration)) && !isVirtualConstructor(typeChecker, calledDeclaration.symbol, calledDeclaration)) {
100            const sigInfo = createDefinitionFromSignatureDeclaration(typeChecker, calledDeclaration, failedAliasResolution);
101            // For a function, if this is the original function definition, return just sigInfo.
102            // If this is the original constructor definition, parent is the class.
103            if (typeChecker.getRootSymbols(symbol).some(s => symbolMatchesSignature(s, calledDeclaration))) {
104                return [sigInfo];
105            }
106            else if (isIdentifier(node)
107                && isNewExpression(parent)
108                && compilerOptions.ets?.components.some(component => component === node.escapedText.toString())
109            ) {
110                return [sigInfo];
111            }
112            else {
113                const defs = getDefinitionFromSymbol(typeChecker, symbol, node, failedAliasResolution, calledDeclaration) || emptyArray;
114                if (isIdentifier(node) && isEtsComponentExpression(parent)) {
115                    return [...defs];
116                }
117                // For a 'super()' call, put the signature first, else put the variable first.
118                return node.kind === SyntaxKind.SuperKeyword ? [sigInfo, ...defs] : [...defs, sigInfo];
119            }
120        }
121
122        // Because name in short-hand property assignment has two different meanings: property name and property value,
123        // using go-to-definition at such position should go to the variable declaration of the property value rather than
124        // go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition
125        // is performed at the location of property access, we would like to go to definition of the property in the short-hand
126        // assignment. This case and others are handled by the following code.
127        if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) {
128            const shorthandSymbol = typeChecker.getShorthandAssignmentValueSymbol(symbol.valueDeclaration);
129            const definitions = shorthandSymbol?.declarations ? shorthandSymbol.declarations.map(decl => createDefinitionInfo(decl, typeChecker, shorthandSymbol, node, /*unverified*/ false, failedAliasResolution)) : emptyArray;
130            return concatenate(definitions, getDefinitionFromObjectLiteralElement(typeChecker, node) || emptyArray);
131        }
132
133        // If the node is the name of a BindingElement within an ObjectBindingPattern instead of just returning the
134        // declaration the symbol (which is itself), we should try to get to the original type of the ObjectBindingPattern
135        // and return the property declaration for the referenced property.
136        // For example:
137        //      import('./foo').then(({ b/*goto*/ar }) => undefined); => should get use to the declaration in file "./foo"
138        //
139        //      function bar<T>(onfulfilled: (value: T) => void) { //....}
140        //      interface Test {
141        //          pr/*destination*/op1: number
142        //      }
143        //      bar<Test>(({pr/*goto*/op1})=>{});
144        if (isPropertyName(node) && isBindingElement(parent) && isObjectBindingPattern(parent.parent) &&
145            (node === (parent.propertyName || parent.name))) {
146            const name = getNameFromPropertyName(node);
147            const type = typeChecker.getTypeAtLocation(parent.parent);
148            return name === undefined ? emptyArray : flatMap(type.isUnion() ? type.types : [type], t => {
149                const prop = t.getProperty(name);
150                return prop && getDefinitionFromSymbol(typeChecker, prop, node);
151            });
152        }
153
154        return concatenate(fileReferenceDefinition, getDefinitionFromObjectLiteralElement(typeChecker, node) || getDefinitionFromSymbol(typeChecker, symbol, node, failedAliasResolution));
155    }
156
157    /**
158     * True if we should not add definitions for both the signature symbol and the definition symbol.
159     * True for `const |f = |() => 0`, false for `function |f() {} const |g = f;`.
160     * Also true for any assignment RHS.
161     */
162    function symbolMatchesSignature(s: Symbol, calledDeclaration: SignatureDeclaration) {
163        return s === calledDeclaration.symbol
164            || s === calledDeclaration.symbol.parent
165            || isAssignmentExpression(calledDeclaration.parent)
166            || (!isCallLikeExpression(calledDeclaration.parent) && s === calledDeclaration.parent.symbol);
167    }
168
169    // If the current location we want to find its definition is in an object literal, try to get the contextual type for the
170    // object literal, lookup the property symbol in the contextual type, and use this for goto-definition.
171    // For example
172    //      interface Props{
173    //          /*first*/prop1: number
174    //          prop2: boolean
175    //      }
176    //      function Foo(arg: Props) {}
177    //      Foo( { pr/*1*/op1: 10, prop2: true })
178    function getDefinitionFromObjectLiteralElement(typeChecker: TypeChecker, node: Node) {
179        const element = getContainingObjectLiteralElement(node);
180        if (element) {
181            const contextualType = element && typeChecker.getContextualType(element.parent);
182            if (contextualType) {
183                return flatMap(getPropertySymbolsFromContextualType(element, typeChecker, contextualType, /*unionSymbolOk*/ false), propertySymbol =>
184                    getDefinitionFromSymbol(typeChecker, propertySymbol, node));
185            }
186        }
187    }
188
189    function getDefinitionFromOverriddenMember(typeChecker: TypeChecker, node: Node) {
190        const classElement = findAncestor(node, isClassElement);
191        if (!(classElement && classElement.name)) return;
192
193        const baseDeclaration = findAncestor(classElement, isClassLike);
194        if (!baseDeclaration) return;
195
196        const baseTypeNode = getEffectiveBaseTypeNode(baseDeclaration);
197        if (!baseTypeNode) return;
198        const expression = skipParentheses(baseTypeNode.expression);
199        const base = isClassExpression(expression) ? expression.symbol : typeChecker.getSymbolAtLocation(expression);
200        if (!base) return;
201
202        const name = unescapeLeadingUnderscores(getTextOfPropertyName(classElement.name));
203        const symbol = hasStaticModifier(classElement)
204            ? typeChecker.getPropertyOfType(typeChecker.getTypeOfSymbol(base), name)
205            : typeChecker.getPropertyOfType(typeChecker.getDeclaredTypeOfSymbol(base), name);
206        if (!symbol) return;
207
208        return getDefinitionFromSymbol(typeChecker, symbol, node);
209    }
210
211    export function getReferenceAtPosition(sourceFile: SourceFile, position: number, program: Program): { reference: FileReference, fileName: string, unverified: boolean, file?: SourceFile } | undefined {
212        const referencePath = findReferenceInPosition(sourceFile.referencedFiles, position);
213        if (referencePath) {
214            const file = program.getSourceFileFromReference(sourceFile, referencePath);
215            return file && { reference: referencePath, fileName: file.fileName, file, unverified: false };
216        }
217
218        const typeReferenceDirective = findReferenceInPosition(sourceFile.typeReferenceDirectives, position);
219        if (typeReferenceDirective) {
220            const reference = program.getResolvedTypeReferenceDirectives().get(typeReferenceDirective.fileName, typeReferenceDirective.resolutionMode || sourceFile.impliedNodeFormat);
221            const file = reference && program.getSourceFile(reference.resolvedFileName!); // TODO:GH#18217
222            return file && { reference: typeReferenceDirective, fileName: file.fileName, file, unverified: false };
223        }
224
225        const libReferenceDirective = findReferenceInPosition(sourceFile.libReferenceDirectives, position);
226        if (libReferenceDirective) {
227            const file = program.getLibFileFromReference(libReferenceDirective);
228            return file && { reference: libReferenceDirective, fileName: file.fileName, file, unverified: false };
229        }
230
231        if (sourceFile.resolvedModules?.size()) {
232            const node = getTouchingToken(sourceFile, position);
233            if (isModuleSpecifierLike(node) && isExternalModuleNameRelative(node.text) && sourceFile.resolvedModules.has(node.text, getModeForUsageLocation(sourceFile, node))) {
234                const verifiedFileName = sourceFile.resolvedModules.get(node.text, getModeForUsageLocation(sourceFile, node))?.resolvedFileName;
235                const fileName = verifiedFileName || resolvePath(getDirectoryPath(sourceFile.fileName), node.text);
236                return {
237                    file: program.getSourceFile(fileName),
238                    fileName,
239                    reference: {
240                        pos: node.getStart(),
241                        end: node.getEnd(),
242                        fileName: node.text
243                    },
244                    unverified: !verifiedFileName,
245                };
246            }
247        }
248
249        return undefined;
250    }
251
252    /// Goto type
253    export function getTypeDefinitionAtPosition(typeChecker: TypeChecker, sourceFile: SourceFile, position: number): readonly DefinitionInfo[] | undefined {
254        const node = getTouchingPropertyName(sourceFile, position);
255        if (node === sourceFile) {
256            return undefined;
257        }
258
259        if (isImportMeta(node.parent) && node.parent.name === node) {
260            return definitionFromType(typeChecker.getTypeAtLocation(node.parent), typeChecker, node.parent, /*failedAliasResolution*/ false);
261        }
262
263        const { symbol, failedAliasResolution } = getSymbol(node, typeChecker, /*stopAtAlias*/ false);
264        if (!symbol) return undefined;
265
266        const typeAtLocation = typeChecker.getTypeOfSymbolAtLocation(symbol, node);
267        const returnType = tryGetReturnTypeOfFunction(symbol, typeAtLocation, typeChecker);
268        const fromReturnType = returnType && definitionFromType(returnType, typeChecker, node, failedAliasResolution);
269        // If a function returns 'void' or some other type with no definition, just return the function definition.
270        const typeDefinitions = fromReturnType && fromReturnType.length !== 0 ? fromReturnType : definitionFromType(typeAtLocation, typeChecker, node, failedAliasResolution);
271        return typeDefinitions.length ? typeDefinitions
272            : !(symbol.flags & SymbolFlags.Value) && symbol.flags & SymbolFlags.Type ? getDefinitionFromSymbol(typeChecker, skipAlias(symbol, typeChecker), node, failedAliasResolution)
273            : undefined;
274    }
275
276    function definitionFromType(type: Type, checker: TypeChecker, node: Node, failedAliasResolution: boolean | undefined): readonly DefinitionInfo[] {
277        return flatMap(type.isUnion() && !(type.flags & TypeFlags.Enum) ? type.types : [type], t =>
278            t.symbol && getDefinitionFromSymbol(checker, t.symbol, node, failedAliasResolution));
279    }
280
281    function tryGetReturnTypeOfFunction(symbol: Symbol, type: Type, checker: TypeChecker): Type | undefined {
282        // If the type is just a function's inferred type,
283        // go-to-type should go to the return type instead, since go-to-definition takes you to the function anyway.
284        if (type.symbol === symbol ||
285            // At `const f = () => {}`, the symbol is `f` and the type symbol is at `() => {}`
286            symbol.valueDeclaration && type.symbol && isVariableDeclaration(symbol.valueDeclaration) && symbol.valueDeclaration.initializer === type.symbol.valueDeclaration as Node) {
287            const sigs = type.getCallSignatures();
288            if (sigs.length === 1) return checker.getReturnTypeOfSignature(first(sigs));
289        }
290        return undefined;
291    }
292
293    export function getDefinitionAndBoundSpan(program: Program, sourceFile: SourceFile, position: number): DefinitionInfoAndBoundSpan | undefined {
294        const definitions = getDefinitionAtPosition(program, sourceFile, position);
295
296        if (!definitions || definitions.length === 0) {
297            return undefined;
298        }
299
300        // Check if position is on triple slash reference.
301        const comment = findReferenceInPosition(sourceFile.referencedFiles, position) ||
302            findReferenceInPosition(sourceFile.typeReferenceDirectives, position) ||
303            findReferenceInPosition(sourceFile.libReferenceDirectives, position);
304
305        if (comment) {
306            return { definitions, textSpan: createTextSpanFromRange(comment) };
307        }
308
309        const node = getTouchingPropertyName(sourceFile, position);
310        const textSpan = createTextSpan(node.getStart(), node.getWidth());
311
312        return { definitions, textSpan };
313    }
314
315    // At 'x.foo', see if the type of 'x' has an index signature, and if so find its declarations.
316    function getDefinitionInfoForIndexSignatures(node: Node, checker: TypeChecker): DefinitionInfo[] | undefined {
317        return mapDefined(checker.getIndexInfosAtLocation(node), info => info.declaration && createDefinitionFromSignatureDeclaration(checker, info.declaration));
318    }
319
320    function getSymbol(node: Node, checker: TypeChecker, stopAtAlias: boolean | undefined) {
321        const symbol = checker.getSymbolAtLocation(node);
322        // If this is an alias, and the request came at the declaration location
323        // get the aliased symbol instead. This allows for goto def on an import e.g.
324        //   import {A, B} from "mod";
325        // to jump to the implementation directly.
326        let failedAliasResolution = false;
327        if (symbol?.declarations && symbol.flags & SymbolFlags.Alias && !stopAtAlias && shouldSkipAlias(node, symbol.declarations[0])) {
328            const aliased = checker.getAliasedSymbol(symbol);
329            if (aliased.declarations) {
330                return { symbol: aliased };
331            }
332            else {
333                failedAliasResolution = true;
334            }
335        }
336        return { symbol, failedAliasResolution };
337    }
338
339    // Go to the original declaration for cases:
340    //
341    //   (1) when the aliased symbol was declared in the location(parent).
342    //   (2) when the aliased symbol is originating from an import.
343    //
344    function shouldSkipAlias(node: Node, declaration: Node): boolean {
345        if (node.kind !== SyntaxKind.Identifier) {
346            return false;
347        }
348        if (node.parent === declaration) {
349            return true;
350        }
351        if (declaration.kind === SyntaxKind.NamespaceImport) {
352            return false;
353        }
354        return true;
355    }
356
357    /**
358     * ```ts
359     * function f() {}
360     * f.foo = 0;
361     * ```
362     *
363     * Here, `f` has two declarations: the function declaration, and the identifier in the next line.
364     * The latter is a declaration for `f` because it gives `f` the `SymbolFlags.Namespace` meaning so
365     * it can contain `foo`. However, that declaration is pretty uninteresting and not intuitively a
366     * "definition" for `f`. Ideally, the question we'd like to answer is "what SymbolFlags does this
367     * declaration contribute to the symbol for `f`?" If the answer is just `Namespace` and the
368     * declaration looks like an assignment, that declaration is in no sense a definition for `f`.
369     * But that information is totally lost during binding and/or symbol merging, so we need to do
370     * our best to reconstruct it or use other heuristics. This function (and the logic around its
371     * calling) covers our tests but feels like a hack, and it would be great if someone could come
372     * up with a more precise definition of what counts as a definition.
373     */
374    function isExpandoDeclaration(node: Declaration): boolean {
375        if (!isAssignmentDeclaration(node)) return false;
376        const containingAssignment = findAncestor(node, p => {
377            if (isAssignmentExpression(p)) return true;
378            if (!isAssignmentDeclaration(p as Declaration)) return "quit";
379            return false;
380        }) as AssignmentExpression<AssignmentOperatorToken> | undefined;
381        return !!containingAssignment && getAssignmentDeclarationKind(containingAssignment) === AssignmentDeclarationKind.Property;
382    }
383
384    function getDefinitionFromSymbol(typeChecker: TypeChecker, symbol: Symbol, node: Node, failedAliasResolution?: boolean, excludeDeclaration?: Node): DefinitionInfo[] | undefined {
385        const filteredDeclarations = filter(symbol.declarations, d => d !== excludeDeclaration);
386        const withoutExpandos = filter(filteredDeclarations, d => !isExpandoDeclaration(d));
387        const results = some(withoutExpandos) ? withoutExpandos : filteredDeclarations;
388        return getConstructSignatureDefinition() || getCallSignatureDefinition() || map(results, declaration => createDefinitionInfo(declaration, typeChecker, symbol, node, /*unverified*/ false, failedAliasResolution));
389
390        function getConstructSignatureDefinition(): DefinitionInfo[] | undefined {
391            // Applicable only if we are in a new expression, or we are on a constructor declaration
392            // and in either case the symbol has a construct signature definition, i.e. class
393            if (symbol.flags & SymbolFlags.Class && !(symbol.flags & (SymbolFlags.Function | SymbolFlags.Variable)) && (isNewExpressionTarget(node) || node.kind === SyntaxKind.ConstructorKeyword)) {
394                const cls = find(filteredDeclarations, isClassLike) || Debug.fail("Expected declaration to have at least one class-like declaration");
395                return getSignatureDefinition(cls.members, /*selectConstructors*/ true);
396            }
397        }
398
399        function getCallSignatureDefinition(): DefinitionInfo[] | undefined {
400            return isCallOrNewExpressionTarget(node) || isNameOfFunctionDeclaration(node)
401                ? getSignatureDefinition(filteredDeclarations, /*selectConstructors*/ false)
402                : undefined;
403        }
404
405        function getSignatureDefinition(signatureDeclarations: readonly Declaration[] | undefined, selectConstructors: boolean): DefinitionInfo[] | undefined {
406            if (!signatureDeclarations) {
407                return undefined;
408            }
409            const declarations = signatureDeclarations.filter(selectConstructors ? isConstructorDeclaration : isFunctionLike);
410            const declarationsWithBody = declarations.filter(d => !!(d as FunctionLikeDeclaration).body);
411
412            // declarations defined on the global scope can be defined on multiple files. Get all of them.
413            return declarations.length
414                ? declarationsWithBody.length !== 0
415                    ? declarationsWithBody.map(x => createDefinitionInfo(x, typeChecker, symbol, node))
416                    : [createDefinitionInfo(last(declarations), typeChecker, symbol, node, /*unverified*/ false, failedAliasResolution)]
417                : undefined;
418        }
419    }
420
421    /** Creates a DefinitionInfo from a Declaration, using the declaration's name if possible. */
422    export function createDefinitionInfo(declaration: Declaration, checker: TypeChecker, symbol: Symbol, node: Node, unverified?: boolean, failedAliasResolution?: boolean): DefinitionInfo {
423        const symbolName = checker.symbolToString(symbol); // Do not get scoped name, just the name of the symbol
424        const symbolKind = SymbolDisplay.getSymbolKind(checker, symbol, node);
425        const containerName = symbol.parent ? checker.symbolToString(symbol.parent, node) : "";
426        return createDefinitionInfoFromName(checker, declaration, symbolKind, symbolName, containerName, unverified, failedAliasResolution);
427    }
428
429    /** Creates a DefinitionInfo directly from the name of a declaration. */
430    function createDefinitionInfoFromName(checker: TypeChecker, declaration: Declaration, symbolKind: ScriptElementKind, symbolName: string, containerName: string, unverified?: boolean, failedAliasResolution?: boolean, textSpan?: TextSpan): DefinitionInfo {
431        const sourceFile = declaration.getSourceFile();
432        if (!textSpan) {
433            const name = getNameOfDeclaration(declaration) || declaration;
434            textSpan = createTextSpanFromNode(name, sourceFile);
435        }
436        return {
437            fileName: sourceFile.fileName,
438            textSpan,
439            kind: symbolKind,
440            name: symbolName,
441            containerKind: undefined!, // TODO: GH#18217
442            containerName,
443            ...FindAllReferences.toContextSpan(
444                textSpan,
445                sourceFile,
446                FindAllReferences.getContextNode(declaration)
447            ),
448            isLocal: !isDefinitionVisible(checker, declaration),
449            isAmbient: !!(declaration.flags & NodeFlags.Ambient),
450            unverified,
451            failedAliasResolution,
452        };
453    }
454
455    function isDefinitionVisible(checker: TypeChecker, declaration: Declaration): boolean {
456        if (checker.isDeclarationVisible(declaration)) return true;
457        if (!declaration.parent) return false;
458
459        // Variable initializers are visible if variable is visible
460        if (hasInitializer(declaration.parent) && declaration.parent.initializer === declaration) return isDefinitionVisible(checker, declaration.parent as Declaration);
461
462        // Handle some exceptions here like arrow function, members of class and object literal expression which are technically not visible but we want the definition to be determined by its parent
463        switch (declaration.kind) {
464            case SyntaxKind.PropertyDeclaration:
465            case SyntaxKind.GetAccessor:
466            case SyntaxKind.SetAccessor:
467            case SyntaxKind.MethodDeclaration:
468                // Private/protected properties/methods are not visible
469                if (hasEffectiveModifier(declaration, ModifierFlags.Private)) return false;
470            // Public properties/methods are visible if its parents are visible, so:
471            // falls through
472
473            case SyntaxKind.Constructor:
474            case SyntaxKind.PropertyAssignment:
475            case SyntaxKind.ShorthandPropertyAssignment:
476            case SyntaxKind.ObjectLiteralExpression:
477            case SyntaxKind.ClassExpression:
478            case SyntaxKind.ArrowFunction:
479            case SyntaxKind.FunctionExpression:
480                return isDefinitionVisible(checker, declaration.parent as Declaration);
481            default:
482                return false;
483        }
484    }
485
486    function createDefinitionFromSignatureDeclaration(typeChecker: TypeChecker, decl: SignatureDeclaration, failedAliasResolution?: boolean): DefinitionInfo {
487        return createDefinitionInfo(decl, typeChecker, decl.symbol, decl, /*unverified*/ false, failedAliasResolution);
488    }
489
490    export function findReferenceInPosition(refs: readonly FileReference[], pos: number): FileReference | undefined {
491        return find(refs, ref => textRangeContainsPositionInclusive(ref, pos));
492    }
493
494    function getDefinitionInfoForFileReference(name: string, targetFileName: string, unverified: boolean): DefinitionInfo {
495        return {
496            fileName: targetFileName,
497            textSpan: createTextSpanFromBounds(0, 0),
498            kind: ScriptElementKind.scriptElement,
499            name,
500            containerName: undefined!,
501            containerKind: undefined!, // TODO: GH#18217
502            unverified,
503        };
504    }
505
506    /** Returns a CallLikeExpression where `node` is the target being invoked. */
507    function getAncestorCallLikeExpression(node: Node): CallLikeExpression | undefined {
508        const target = findAncestor(node, n => !isRightSideOfPropertyAccess(n));
509        const callLike = target?.parent;
510        return callLike && isCallLikeExpression(callLike) && getInvokedExpression(callLike) === target ? callLike : undefined;
511    }
512
513    function tryGetSignatureDeclaration(typeChecker: TypeChecker, node: Node): SignatureDeclaration | undefined {
514        const callLike = getAncestorCallLikeExpression(node);
515        const signature = callLike && typeChecker.getResolvedSignature(callLike);
516        // Don't go to a function type, go to the value having that type.
517        return tryCast(signature && signature.declaration, (d): d is SignatureDeclaration => isFunctionLike(d) && !isFunctionTypeNode(d));
518    }
519
520    function isConstructorLike(node: Node): boolean {
521        switch (node.kind) {
522            case SyntaxKind.Constructor:
523            case SyntaxKind.ConstructorType:
524            case SyntaxKind.ConstructSignature:
525                return true;
526            default:
527                return false;
528        }
529    }
530}
531