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