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