1/* @internal */ 2namespace ts.SymbolDisplay { 3 const symbolDisplayNodeBuilderFlags = NodeBuilderFlags.OmitParameterModifiers | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope; 4 5 // TODO(drosen): use contextual SemanticMeaning. 6 export function getSymbolKind(typeChecker: TypeChecker, symbol: Symbol, location: Node): ScriptElementKind { 7 const result = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location); 8 if (result !== ScriptElementKind.unknown) { 9 return result; 10 } 11 12 const flags = getCombinedLocalAndExportSymbolFlags(symbol); 13 if (flags & SymbolFlags.Class) { 14 return getDeclarationOfKind(symbol, SyntaxKind.ClassExpression) ? 15 ScriptElementKind.localClassElement : ScriptElementKind.classElement; 16 } 17 if (flags & SymbolFlags.Enum) return ScriptElementKind.enumElement; 18 if (flags & SymbolFlags.TypeAlias) return ScriptElementKind.typeElement; 19 if (flags & SymbolFlags.Interface) return ScriptElementKind.interfaceElement; 20 if (flags & SymbolFlags.TypeParameter) return ScriptElementKind.typeParameterElement; 21 if (flags & SymbolFlags.EnumMember) return ScriptElementKind.enumMemberElement; 22 if (flags & SymbolFlags.Alias) return ScriptElementKind.alias; 23 if (flags & SymbolFlags.Module) return ScriptElementKind.moduleElement; 24 25 return result; 26 } 27 28 function getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker: TypeChecker, symbol: Symbol, location: Node): ScriptElementKind { 29 const roots = typeChecker.getRootSymbols(symbol); 30 // If this is a method from a mapped type, leave as a method so long as it still has a call signature. 31 if (roots.length === 1 32 && first(roots).flags & SymbolFlags.Method 33 // Ensure the mapped version is still a method, as opposed to `{ [K in keyof I]: number }`. 34 && typeChecker.getTypeOfSymbolAtLocation(symbol, location).getNonNullableType().getCallSignatures().length !== 0) { 35 return ScriptElementKind.memberFunctionElement; 36 } 37 38 if (typeChecker.isUndefinedSymbol(symbol)) { 39 return ScriptElementKind.variableElement; 40 } 41 if (typeChecker.isArgumentsSymbol(symbol)) { 42 return ScriptElementKind.localVariableElement; 43 } 44 if (location.kind === SyntaxKind.ThisKeyword && isExpression(location) || isThisInTypeQuery(location)) { 45 return ScriptElementKind.parameterElement; 46 } 47 const flags = getCombinedLocalAndExportSymbolFlags(symbol); 48 if (flags & SymbolFlags.Variable) { 49 if (isFirstDeclarationOfSymbolParameter(symbol)) { 50 return ScriptElementKind.parameterElement; 51 } 52 else if (symbol.valueDeclaration && isVarConst(symbol.valueDeclaration as VariableDeclaration)) { 53 return ScriptElementKind.constElement; 54 } 55 else if (forEach(symbol.declarations, isLet)) { 56 return ScriptElementKind.letElement; 57 } 58 return isLocalVariableOrFunction(symbol) ? ScriptElementKind.localVariableElement : ScriptElementKind.variableElement; 59 } 60 if (flags & SymbolFlags.Function) return isLocalVariableOrFunction(symbol) ? ScriptElementKind.localFunctionElement : ScriptElementKind.functionElement; 61 // FIXME: getter and setter use the same symbol. And it is rare to use only setter without getter, so in most cases the symbol always has getter flag. 62 // So, even when the location is just on the declaration of setter, this function returns getter. 63 if (flags & SymbolFlags.GetAccessor) return ScriptElementKind.memberGetAccessorElement; 64 if (flags & SymbolFlags.SetAccessor) return ScriptElementKind.memberSetAccessorElement; 65 if (flags & SymbolFlags.Method) return ScriptElementKind.memberFunctionElement; 66 if (flags & SymbolFlags.Constructor) return ScriptElementKind.constructorImplementationElement; 67 if (flags & SymbolFlags.Signature) return ScriptElementKind.indexSignatureElement; 68 69 if (flags & SymbolFlags.Property) { 70 if (flags & SymbolFlags.Transient && (symbol as TransientSymbol).checkFlags & CheckFlags.Synthetic) { 71 // If union property is result of union of non method (property/accessors/variables), it is labeled as property 72 const unionPropertyKind = forEach(typeChecker.getRootSymbols(symbol), rootSymbol => { 73 const rootSymbolFlags = rootSymbol.getFlags(); 74 if (rootSymbolFlags & (SymbolFlags.PropertyOrAccessor | SymbolFlags.Variable)) { 75 return ScriptElementKind.memberVariableElement; 76 } 77 }); 78 if (!unionPropertyKind) { 79 // If this was union of all methods, 80 // make sure it has call signatures before we can label it as method 81 const typeOfUnionProperty = typeChecker.getTypeOfSymbolAtLocation(symbol, location); 82 if (typeOfUnionProperty.getCallSignatures().length) { 83 return ScriptElementKind.memberFunctionElement; 84 } 85 return ScriptElementKind.memberVariableElement; 86 } 87 return unionPropertyKind; 88 } 89 90 return ScriptElementKind.memberVariableElement; 91 } 92 93 return ScriptElementKind.unknown; 94 } 95 96 function getNormalizedSymbolModifiers(symbol: Symbol) { 97 if (symbol.declarations && symbol.declarations.length) { 98 const [declaration, ...declarations] = symbol.declarations; 99 // omit deprecated flag if some declarations are not deprecated 100 const excludeFlags = length(declarations) && isDeprecatedDeclaration(declaration) && some(declarations, d => !isDeprecatedDeclaration(d)) 101 ? ModifierFlags.Deprecated 102 : ModifierFlags.None; 103 const modifiers = getNodeModifiers(declaration, excludeFlags); 104 if (modifiers) { 105 return modifiers.split(","); 106 } 107 } 108 return []; 109 } 110 111 export function getSymbolModifiers(typeChecker: TypeChecker, symbol: Symbol): string { 112 if (!symbol) { 113 return ScriptElementKindModifier.none; 114 } 115 116 const modifiers = new Set(getNormalizedSymbolModifiers(symbol)); 117 if (symbol.flags & SymbolFlags.Alias) { 118 const resolvedSymbol = typeChecker.getAliasedSymbol(symbol); 119 if (resolvedSymbol !== symbol) { 120 forEach(getNormalizedSymbolModifiers(resolvedSymbol), modifier => { 121 modifiers.add(modifier); 122 }); 123 } 124 } 125 if (symbol.flags & SymbolFlags.Optional) { 126 modifiers.add(ScriptElementKindModifier.optionalModifier); 127 } 128 return modifiers.size > 0 ? arrayFrom(modifiers.values()).join(",") : ScriptElementKindModifier.none; 129 } 130 131 interface SymbolDisplayPartsDocumentationAndSymbolKind { 132 displayParts: SymbolDisplayPart[]; 133 documentation: SymbolDisplayPart[]; 134 symbolKind: ScriptElementKind; 135 tags: JSDocTagInfo[] | undefined; 136 } 137 138 // TODO(drosen): Currently completion entry details passes the SemanticMeaning.All instead of using semanticMeaning of location 139 export function getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker: TypeChecker, symbol: Symbol, sourceFile: SourceFile, enclosingDeclaration: Node | undefined, 140 location: Node, semanticMeaning = getMeaningFromLocation(location), alias?: Symbol): SymbolDisplayPartsDocumentationAndSymbolKind { 141 const displayParts: SymbolDisplayPart[] = []; 142 let documentation: SymbolDisplayPart[] = []; 143 let tags: JSDocTagInfo[] = []; 144 const symbolFlags = getCombinedLocalAndExportSymbolFlags(symbol); 145 let symbolKind = semanticMeaning & SemanticMeaning.Value ? getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location) : ScriptElementKind.unknown; 146 let hasAddedSymbolInfo = false; 147 const isThisExpression = location.kind === SyntaxKind.ThisKeyword && isInExpressionContext(location) || isThisInTypeQuery(location); 148 let type: Type | undefined; 149 let printer: Printer; 150 let documentationFromAlias: SymbolDisplayPart[] | undefined; 151 let tagsFromAlias: JSDocTagInfo[] | undefined; 152 let hasMultipleSignatures = false; 153 154 if (location.kind === SyntaxKind.ThisKeyword && !isThisExpression) { 155 return { displayParts: [keywordPart(SyntaxKind.ThisKeyword)], documentation: [], symbolKind: ScriptElementKind.primitiveType, tags: undefined }; 156 } 157 158 // Class at constructor site need to be shown as constructor apart from property,method, vars 159 if (symbolKind !== ScriptElementKind.unknown || symbolFlags & SymbolFlags.Class || symbolFlags & SymbolFlags.Alias) { 160 // If symbol is accessor, they are allowed only if location is at declaration identifier of the accessor 161 if (symbolKind === ScriptElementKind.memberGetAccessorElement || symbolKind === ScriptElementKind.memberSetAccessorElement) { 162 const declaration = find(symbol.declarations as ((GetAccessorDeclaration | SetAccessorDeclaration | PropertyDeclaration)[]), declaration => declaration.name === location); 163 if (declaration) { 164 switch(declaration.kind){ 165 case SyntaxKind.GetAccessor: 166 symbolKind = ScriptElementKind.memberGetAccessorElement; 167 break; 168 case SyntaxKind.SetAccessor: 169 symbolKind = ScriptElementKind.memberSetAccessorElement; 170 break; 171 case SyntaxKind.PropertyDeclaration: 172 symbolKind = ScriptElementKind.memberAccessorVariableElement; 173 break; 174 default: 175 Debug.assertNever(declaration); 176 } 177 } 178 else { 179 symbolKind = ScriptElementKind.memberVariableElement; 180 } 181 } 182 183 let signature: Signature | undefined; 184 type = isThisExpression ? typeChecker.getTypeAtLocation(location) : typeChecker.getTypeOfSymbolAtLocation(symbol, location); 185 186 if (location.parent && location.parent.kind === SyntaxKind.PropertyAccessExpression) { 187 const right = (location.parent as PropertyAccessExpression).name; 188 // Either the location is on the right of a property access, or on the left and the right is missing 189 if (right === location || (right && right.getFullWidth() === 0)) { 190 location = location.parent; 191 } 192 } 193 194 // try get the call/construct signature from the type if it matches 195 let callExpressionLike: CallExpression | NewExpression | JsxOpeningLikeElement | TaggedTemplateExpression | undefined; 196 if (isCallOrNewExpression(location)) { 197 callExpressionLike = location; 198 } 199 else if (isCallExpressionTarget(location) || isNewExpressionTarget(location)) { 200 callExpressionLike = location.parent as CallExpression | NewExpression; 201 } 202 else if (location.parent && (isJsxOpeningLikeElement(location.parent) || isTaggedTemplateExpression(location.parent)) && isFunctionLike(symbol.valueDeclaration)) { 203 callExpressionLike = location.parent; 204 } 205 206 if (callExpressionLike) { 207 signature = typeChecker.getResolvedSignature(callExpressionLike); // TODO: GH#18217 208 209 const useConstructSignatures = callExpressionLike.kind === SyntaxKind.NewExpression || (isCallExpression(callExpressionLike) && callExpressionLike.expression.kind === SyntaxKind.SuperKeyword); 210 211 const allSignatures = useConstructSignatures ? type.getConstructSignatures() : type.getCallSignatures(); 212 213 if (signature && !contains(allSignatures, signature.target) && !contains(allSignatures, signature)) { 214 // Get the first signature if there is one -- allSignatures may contain 215 // either the original signature or its target, so check for either 216 signature = allSignatures.length ? allSignatures[0] : undefined; 217 } 218 219 if (signature) { 220 if (useConstructSignatures && (symbolFlags & SymbolFlags.Class)) { 221 // Constructor 222 symbolKind = ScriptElementKind.constructorImplementationElement; 223 addPrefixForAnyFunctionOrVar(type.symbol, symbolKind); 224 } 225 else if (symbolFlags & SymbolFlags.Alias) { 226 symbolKind = ScriptElementKind.alias; 227 pushSymbolKind(symbolKind); 228 displayParts.push(spacePart()); 229 if (useConstructSignatures) { 230 if (signature.flags & SignatureFlags.Abstract) { 231 displayParts.push(keywordPart(SyntaxKind.AbstractKeyword)); 232 displayParts.push(spacePart()); 233 } 234 displayParts.push(keywordPart(SyntaxKind.NewKeyword)); 235 displayParts.push(spacePart()); 236 } 237 addFullSymbolName(symbol); 238 } 239 else { 240 addPrefixForAnyFunctionOrVar(symbol, symbolKind); 241 } 242 243 switch (symbolKind) { 244 case ScriptElementKind.jsxAttribute: 245 case ScriptElementKind.memberVariableElement: 246 case ScriptElementKind.variableElement: 247 case ScriptElementKind.constElement: 248 case ScriptElementKind.letElement: 249 case ScriptElementKind.parameterElement: 250 case ScriptElementKind.localVariableElement: 251 // If it is call or construct signature of lambda's write type name 252 displayParts.push(punctuationPart(SyntaxKind.ColonToken)); 253 displayParts.push(spacePart()); 254 if (!(getObjectFlags(type) & ObjectFlags.Anonymous) && type.symbol) { 255 addRange(displayParts, symbolToDisplayParts(typeChecker, type.symbol, enclosingDeclaration, /*meaning*/ undefined, SymbolFormatFlags.AllowAnyNodeKind | SymbolFormatFlags.WriteTypeParametersOrArguments)); 256 displayParts.push(lineBreakPart()); 257 } 258 if (useConstructSignatures) { 259 if (signature.flags & SignatureFlags.Abstract) { 260 displayParts.push(keywordPart(SyntaxKind.AbstractKeyword)); 261 displayParts.push(spacePart()); 262 } 263 displayParts.push(keywordPart(SyntaxKind.NewKeyword)); 264 displayParts.push(spacePart()); 265 } 266 addSignatureDisplayParts(signature, allSignatures, TypeFormatFlags.WriteArrowStyleSignature); 267 break; 268 269 default: 270 // Just signature 271 addSignatureDisplayParts(signature, allSignatures); 272 } 273 hasAddedSymbolInfo = true; 274 hasMultipleSignatures = allSignatures.length > 1; 275 } 276 } 277 else if ((isNameOfFunctionDeclaration(location) && !(symbolFlags & SymbolFlags.Accessor)) || // name of function declaration 278 (location.kind === SyntaxKind.ConstructorKeyword && location.parent.kind === SyntaxKind.Constructor)) { // At constructor keyword of constructor declaration 279 // get the signature from the declaration and write it 280 const functionDeclaration = location.parent as SignatureDeclaration; 281 // Use function declaration to write the signatures only if the symbol corresponding to this declaration 282 const locationIsSymbolDeclaration = symbol.declarations && find(symbol.declarations, declaration => 283 declaration === (location.kind === SyntaxKind.ConstructorKeyword ? functionDeclaration.parent : functionDeclaration)); 284 285 if (locationIsSymbolDeclaration) { 286 const allSignatures = functionDeclaration.kind === SyntaxKind.Constructor ? type.getNonNullableType().getConstructSignatures() : type.getNonNullableType().getCallSignatures(); 287 if (!typeChecker.isImplementationOfOverload(functionDeclaration)) { 288 signature = typeChecker.getSignatureFromDeclaration(functionDeclaration); // TODO: GH#18217 289 } 290 else { 291 signature = allSignatures[0]; 292 } 293 294 if (functionDeclaration.kind === SyntaxKind.Constructor) { 295 // show (constructor) Type(...) signature 296 symbolKind = ScriptElementKind.constructorImplementationElement; 297 addPrefixForAnyFunctionOrVar(type.symbol, symbolKind); 298 } 299 else { 300 // (function/method) symbol(..signature) 301 addPrefixForAnyFunctionOrVar(functionDeclaration.kind === SyntaxKind.CallSignature && 302 !(type.symbol.flags & SymbolFlags.TypeLiteral || type.symbol.flags & SymbolFlags.ObjectLiteral) ? type.symbol : symbol, symbolKind); 303 } 304 if (signature) { 305 addSignatureDisplayParts(signature, allSignatures); 306 } 307 hasAddedSymbolInfo = true; 308 hasMultipleSignatures = allSignatures.length > 1; 309 } 310 } 311 } 312 if (symbolFlags & SymbolFlags.Class && !hasAddedSymbolInfo && !isThisExpression) { 313 addAliasPrefixIfNecessary(); 314 if (getDeclarationOfKind(symbol, SyntaxKind.ClassExpression)) { 315 // Special case for class expressions because we would like to indicate that 316 // the class name is local to the class body (similar to function expression) 317 // (local class) class <className> 318 pushSymbolKind(ScriptElementKind.localClassElement); 319 } 320 else if (getDeclarationOfKind(symbol, SyntaxKind.StructDeclaration)) { 321 // struct declaration 322 displayParts.push(keywordPart(SyntaxKind.StructKeyword)); 323 } 324 else { 325 // Class declaration has name which is not local. 326 displayParts.push(keywordPart(SyntaxKind.ClassKeyword)); 327 } 328 displayParts.push(spacePart()); 329 addFullSymbolName(symbol); 330 writeTypeParametersOfSymbol(symbol, sourceFile); 331 } 332 if ((symbolFlags & SymbolFlags.Interface) && (semanticMeaning & SemanticMeaning.Type)) { 333 prefixNextMeaning(); 334 displayParts.push(keywordPart(SyntaxKind.InterfaceKeyword)); 335 displayParts.push(spacePart()); 336 addFullSymbolName(symbol); 337 writeTypeParametersOfSymbol(symbol, sourceFile); 338 } 339 if ((symbolFlags & SymbolFlags.TypeAlias) && (semanticMeaning & SemanticMeaning.Type)) { 340 prefixNextMeaning(); 341 displayParts.push(keywordPart(SyntaxKind.TypeKeyword)); 342 displayParts.push(spacePart()); 343 addFullSymbolName(symbol); 344 writeTypeParametersOfSymbol(symbol, sourceFile); 345 displayParts.push(spacePart()); 346 displayParts.push(operatorPart(SyntaxKind.EqualsToken)); 347 displayParts.push(spacePart()); 348 addRange(displayParts, typeToDisplayParts(typeChecker, isConstTypeReference(location.parent) ? typeChecker.getTypeAtLocation(location.parent) : typeChecker.getDeclaredTypeOfSymbol(symbol), enclosingDeclaration, TypeFormatFlags.InTypeAlias)); 349 } 350 if (symbolFlags & SymbolFlags.Enum) { 351 prefixNextMeaning(); 352 if (some(symbol.declarations, d => isEnumDeclaration(d) && isEnumConst(d))) { 353 displayParts.push(keywordPart(SyntaxKind.ConstKeyword)); 354 displayParts.push(spacePart()); 355 } 356 displayParts.push(keywordPart(SyntaxKind.EnumKeyword)); 357 displayParts.push(spacePart()); 358 addFullSymbolName(symbol); 359 } 360 if (symbolFlags & SymbolFlags.Module && !isThisExpression) { 361 prefixNextMeaning(); 362 const declaration = getDeclarationOfKind<ModuleDeclaration>(symbol, SyntaxKind.ModuleDeclaration); 363 const isNamespace = declaration && declaration.name && declaration.name.kind === SyntaxKind.Identifier; 364 displayParts.push(keywordPart(isNamespace ? SyntaxKind.NamespaceKeyword : SyntaxKind.ModuleKeyword)); 365 displayParts.push(spacePart()); 366 addFullSymbolName(symbol); 367 } 368 if ((symbolFlags & SymbolFlags.TypeParameter) && (semanticMeaning & SemanticMeaning.Type)) { 369 prefixNextMeaning(); 370 displayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); 371 displayParts.push(textPart("type parameter")); 372 displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); 373 displayParts.push(spacePart()); 374 addFullSymbolName(symbol); 375 if (symbol.parent) { 376 // Class/Interface type parameter 377 addInPrefix(); 378 addFullSymbolName(symbol.parent, enclosingDeclaration); 379 writeTypeParametersOfSymbol(symbol.parent, enclosingDeclaration); 380 } 381 else { 382 // Method/function type parameter 383 const decl = getDeclarationOfKind(symbol, SyntaxKind.TypeParameter); 384 if (decl === undefined) return Debug.fail(); 385 const declaration = decl.parent; 386 387 if (declaration) { 388 if (isFunctionLikeKind(declaration.kind)) { 389 addInPrefix(); 390 const signature = typeChecker.getSignatureFromDeclaration(declaration as SignatureDeclaration)!; // TODO: GH#18217 391 if (declaration.kind === SyntaxKind.ConstructSignature) { 392 displayParts.push(keywordPart(SyntaxKind.NewKeyword)); 393 displayParts.push(spacePart()); 394 } 395 else if (declaration.kind !== SyntaxKind.CallSignature && (declaration as SignatureDeclaration).name) { 396 addFullSymbolName(declaration.symbol); 397 } 398 addRange(displayParts, signatureToDisplayParts(typeChecker, signature, sourceFile, TypeFormatFlags.WriteTypeArgumentsOfSignature)); 399 } 400 else if (declaration.kind === SyntaxKind.TypeAliasDeclaration) { 401 // Type alias type parameter 402 // For example 403 // type list<T> = T[]; // Both T will go through same code path 404 addInPrefix(); 405 displayParts.push(keywordPart(SyntaxKind.TypeKeyword)); 406 displayParts.push(spacePart()); 407 addFullSymbolName(declaration.symbol); 408 writeTypeParametersOfSymbol(declaration.symbol, sourceFile); 409 } 410 } 411 } 412 } 413 if (symbolFlags & SymbolFlags.EnumMember) { 414 symbolKind = ScriptElementKind.enumMemberElement; 415 addPrefixForAnyFunctionOrVar(symbol, "enum member"); 416 const declaration = symbol.declarations?.[0]; 417 if (declaration?.kind === SyntaxKind.EnumMember) { 418 const constantValue = typeChecker.getConstantValue(declaration as EnumMember); 419 if (constantValue !== undefined) { 420 displayParts.push(spacePart()); 421 displayParts.push(operatorPart(SyntaxKind.EqualsToken)); 422 displayParts.push(spacePart()); 423 displayParts.push(displayPart(getTextOfConstantValue(constantValue), 424 typeof constantValue === "number" ? SymbolDisplayPartKind.numericLiteral : SymbolDisplayPartKind.stringLiteral)); 425 } 426 } 427 } 428 // don't use symbolFlags since getAliasedSymbol requires the flag on the symbol itself 429 if (symbol.flags & SymbolFlags.Alias) { 430 prefixNextMeaning(); 431 if (!hasAddedSymbolInfo) { 432 const resolvedSymbol = typeChecker.getAliasedSymbol(symbol); 433 if (resolvedSymbol !== symbol && resolvedSymbol.declarations && resolvedSymbol.declarations.length > 0) { 434 const resolvedNode = resolvedSymbol.declarations[0]; 435 const declarationName = getNameOfDeclaration(resolvedNode); 436 if (declarationName) { 437 const isExternalModuleDeclaration = 438 isModuleWithStringLiteralName(resolvedNode) && 439 hasSyntacticModifier(resolvedNode, ModifierFlags.Ambient); 440 const shouldUseAliasName = symbol.name !== "default" && !isExternalModuleDeclaration; 441 const resolvedInfo = getSymbolDisplayPartsDocumentationAndSymbolKind( 442 typeChecker, 443 resolvedSymbol, 444 getSourceFileOfNode(resolvedNode), 445 resolvedNode, 446 declarationName, 447 semanticMeaning, 448 shouldUseAliasName ? symbol : resolvedSymbol); 449 displayParts.push(...resolvedInfo.displayParts); 450 displayParts.push(lineBreakPart()); 451 documentationFromAlias = resolvedInfo.documentation; 452 tagsFromAlias = resolvedInfo.tags; 453 } 454 else { 455 documentationFromAlias = resolvedSymbol.getContextualDocumentationComment(resolvedNode, typeChecker); 456 tagsFromAlias = resolvedSymbol.getJsDocTags(typeChecker); 457 } 458 } 459 } 460 461 if (symbol.declarations) { 462 switch (symbol.declarations[0].kind) { 463 case SyntaxKind.NamespaceExportDeclaration: 464 displayParts.push(keywordPart(SyntaxKind.ExportKeyword)); 465 displayParts.push(spacePart()); 466 displayParts.push(keywordPart(SyntaxKind.NamespaceKeyword)); 467 break; 468 case SyntaxKind.ExportAssignment: 469 displayParts.push(keywordPart(SyntaxKind.ExportKeyword)); 470 displayParts.push(spacePart()); 471 displayParts.push(keywordPart((symbol.declarations[0] as ExportAssignment).isExportEquals ? SyntaxKind.EqualsToken : SyntaxKind.DefaultKeyword)); 472 break; 473 case SyntaxKind.ExportSpecifier: 474 displayParts.push(keywordPart(SyntaxKind.ExportKeyword)); 475 break; 476 default: 477 displayParts.push(keywordPart(SyntaxKind.ImportKeyword)); 478 } 479 } 480 displayParts.push(spacePart()); 481 addFullSymbolName(symbol); 482 forEach(symbol.declarations, declaration => { 483 if (declaration.kind === SyntaxKind.ImportEqualsDeclaration) { 484 const importEqualsDeclaration = declaration as ImportEqualsDeclaration; 485 if (isExternalModuleImportEqualsDeclaration(importEqualsDeclaration)) { 486 displayParts.push(spacePart()); 487 displayParts.push(operatorPart(SyntaxKind.EqualsToken)); 488 displayParts.push(spacePart()); 489 displayParts.push(keywordPart(SyntaxKind.RequireKeyword)); 490 displayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); 491 displayParts.push(displayPart(getTextOfNode(getExternalModuleImportEqualsDeclarationExpression(importEqualsDeclaration)), SymbolDisplayPartKind.stringLiteral)); 492 displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); 493 } 494 else { 495 const internalAliasSymbol = typeChecker.getSymbolAtLocation(importEqualsDeclaration.moduleReference); 496 if (internalAliasSymbol) { 497 displayParts.push(spacePart()); 498 displayParts.push(operatorPart(SyntaxKind.EqualsToken)); 499 displayParts.push(spacePart()); 500 addFullSymbolName(internalAliasSymbol, enclosingDeclaration); 501 } 502 } 503 return true; 504 } 505 }); 506 } 507 if (!hasAddedSymbolInfo) { 508 if (symbolKind !== ScriptElementKind.unknown) { 509 if (type) { 510 if (isThisExpression) { 511 prefixNextMeaning(); 512 displayParts.push(keywordPart(SyntaxKind.ThisKeyword)); 513 } 514 else { 515 addPrefixForAnyFunctionOrVar(symbol, symbolKind); 516 } 517 // For properties, variables and local vars: show the type 518 if (symbolKind === ScriptElementKind.memberVariableElement || 519 symbolKind === ScriptElementKind.memberAccessorVariableElement || 520 symbolKind === ScriptElementKind.memberGetAccessorElement || 521 symbolKind === ScriptElementKind.memberSetAccessorElement || 522 symbolKind === ScriptElementKind.jsxAttribute || 523 symbolFlags & SymbolFlags.Variable || 524 symbolKind === ScriptElementKind.localVariableElement || 525 symbolKind === ScriptElementKind.indexSignatureElement || 526 isThisExpression) { 527 displayParts.push(punctuationPart(SyntaxKind.ColonToken)); 528 displayParts.push(spacePart()); 529 // If the type is type parameter, format it specially 530 if (type.symbol && type.symbol.flags & SymbolFlags.TypeParameter && symbolKind !== ScriptElementKind.indexSignatureElement) { 531 const typeParameterParts = mapToDisplayParts(writer => { 532 const param = typeChecker.typeParameterToDeclaration(type as TypeParameter, enclosingDeclaration, symbolDisplayNodeBuilderFlags)!; 533 getPrinter().writeNode(EmitHint.Unspecified, param, getSourceFileOfNode(getParseTreeNode(enclosingDeclaration)), writer); 534 }); 535 addRange(displayParts, typeParameterParts); 536 } 537 else { 538 addRange(displayParts, typeToDisplayParts(typeChecker, type, enclosingDeclaration)); 539 } 540 if ((symbol as TransientSymbol).target && ((symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration) { 541 const labelDecl = ((symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration!; 542 Debug.assertNode(labelDecl.name, isIdentifier); 543 displayParts.push(spacePart()); 544 displayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); 545 displayParts.push(textPart(idText(labelDecl.name))); 546 displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); 547 } 548 } 549 else if (symbolFlags & SymbolFlags.Function || 550 symbolFlags & SymbolFlags.Method || 551 symbolFlags & SymbolFlags.Constructor || 552 symbolFlags & SymbolFlags.Signature || 553 symbolFlags & SymbolFlags.Accessor || 554 symbolKind === ScriptElementKind.memberFunctionElement) { 555 const allSignatures = type.getNonNullableType().getCallSignatures(); 556 if (allSignatures.length) { 557 addSignatureDisplayParts(allSignatures[0], allSignatures); 558 hasMultipleSignatures = allSignatures.length > 1; 559 } 560 } 561 } 562 } 563 else { 564 symbolKind = getSymbolKind(typeChecker, symbol, location); 565 } 566 } 567 568 if (documentation.length === 0 && !hasMultipleSignatures) { 569 documentation = symbol.getContextualDocumentationComment(enclosingDeclaration, typeChecker); 570 } 571 572 if (documentation.length === 0 && symbolFlags & SymbolFlags.Property) { 573 // For some special property access expressions like `exports.foo = foo` or `module.exports.foo = foo` 574 // there documentation comments might be attached to the right hand side symbol of their declarations. 575 // The pattern of such special property access is that the parent symbol is the symbol of the file. 576 if (symbol.parent && symbol.declarations && forEach(symbol.parent.declarations, declaration => declaration.kind === SyntaxKind.SourceFile)) { 577 for (const declaration of symbol.declarations) { 578 if (!declaration.parent || declaration.parent.kind !== SyntaxKind.BinaryExpression) { 579 continue; 580 } 581 582 const rhsSymbol = typeChecker.getSymbolAtLocation((declaration.parent as BinaryExpression).right); 583 if (!rhsSymbol) { 584 continue; 585 } 586 587 documentation = rhsSymbol.getDocumentationComment(typeChecker); 588 tags = rhsSymbol.getJsDocTags(typeChecker); 589 if (documentation.length > 0) { 590 break; 591 } 592 } 593 } 594 } 595 596 if (documentation.length === 0 && isIdentifier(location) && symbol.valueDeclaration && isBindingElement(symbol.valueDeclaration)) { 597 const declaration = symbol.valueDeclaration; 598 const parent = declaration.parent; 599 if (isIdentifier(declaration.name) && isObjectBindingPattern(parent)) { 600 const name = getTextOfIdentifierOrLiteral(declaration.name); 601 const objectType = typeChecker.getTypeAtLocation(parent); 602 documentation = firstDefined(objectType.isUnion() ? objectType.types : [objectType], t => { 603 const prop = t.getProperty(name); 604 return prop ? prop.getDocumentationComment(typeChecker) : undefined; 605 }) || emptyArray; 606 } 607 } 608 609 if (tags.length === 0 && !hasMultipleSignatures) { 610 tags = symbol.getContextualJsDocTags(enclosingDeclaration, typeChecker); 611 } 612 613 if (documentation.length === 0 && documentationFromAlias) { 614 documentation = documentationFromAlias; 615 } 616 617 if (tags.length === 0 && tagsFromAlias) { 618 tags = tagsFromAlias; 619 } 620 621 return { displayParts, documentation, symbolKind, tags: tags.length === 0 ? undefined : tags }; 622 623 function getPrinter() { 624 if (!printer) { 625 printer = createPrinter({ removeComments: true }); 626 } 627 return printer; 628 } 629 630 function prefixNextMeaning() { 631 if (displayParts.length) { 632 displayParts.push(lineBreakPart()); 633 } 634 addAliasPrefixIfNecessary(); 635 } 636 637 function addAliasPrefixIfNecessary() { 638 if (alias) { 639 pushSymbolKind(ScriptElementKind.alias); 640 displayParts.push(spacePart()); 641 } 642 } 643 644 function addInPrefix() { 645 displayParts.push(spacePart()); 646 displayParts.push(keywordPart(SyntaxKind.InKeyword)); 647 displayParts.push(spacePart()); 648 } 649 650 function addFullSymbolName(symbolToDisplay: Symbol, enclosingDeclaration?: Node) { 651 let indexInfos; 652 653 if (alias && symbolToDisplay === symbol) { 654 symbolToDisplay = alias; 655 } 656 if (symbolKind === ScriptElementKind.indexSignatureElement) { 657 indexInfos = typeChecker.getIndexInfosOfIndexSymbol(symbolToDisplay); 658 } 659 660 let fullSymbolDisplayParts: SymbolDisplayPart[] = []; 661 if (symbolToDisplay.flags & SymbolFlags.Signature && indexInfos) { 662 if (symbolToDisplay.parent) { 663 fullSymbolDisplayParts = symbolToDisplayParts(typeChecker, symbolToDisplay.parent); 664 } 665 fullSymbolDisplayParts.push(punctuationPart(SyntaxKind.OpenBracketToken)); 666 //Needed to handle more than one type of index 667 indexInfos.forEach((info, i) => { 668 //Needed to handle template literals 669 fullSymbolDisplayParts.push(...typeToDisplayParts(typeChecker, info.keyType)); 670 if (i !== indexInfos.length - 1) { 671 fullSymbolDisplayParts.push(spacePart()); 672 fullSymbolDisplayParts.push(punctuationPart(SyntaxKind.BarToken)); 673 fullSymbolDisplayParts.push(spacePart()); 674 } 675 }); 676 fullSymbolDisplayParts.push(punctuationPart(SyntaxKind.CloseBracketToken)); 677 } 678 else { 679 fullSymbolDisplayParts = symbolToDisplayParts(typeChecker, symbolToDisplay, enclosingDeclaration || sourceFile, /*meaning*/ undefined, 680 SymbolFormatFlags.WriteTypeParametersOrArguments | SymbolFormatFlags.UseOnlyExternalAliasing | SymbolFormatFlags.AllowAnyNodeKind); 681 } 682 addRange(displayParts, fullSymbolDisplayParts); 683 if (symbol.flags & SymbolFlags.Optional) { 684 displayParts.push(punctuationPart(SyntaxKind.QuestionToken)); 685 } 686 } 687 688 function addPrefixForAnyFunctionOrVar(symbol: Symbol, symbolKind: string) { 689 prefixNextMeaning(); 690 if (symbolKind) { 691 pushSymbolKind(symbolKind); 692 if (symbol && !some(symbol.declarations, d => isArrowFunction(d) || (isFunctionExpression(d) || isClassExpression(d)) && !d.name)) { 693 displayParts.push(spacePart()); 694 addFullSymbolName(symbol); 695 } 696 } 697 } 698 699 function pushSymbolKind(symbolKind: string) { 700 switch (symbolKind) { 701 case ScriptElementKind.variableElement: 702 case ScriptElementKind.functionElement: 703 case ScriptElementKind.letElement: 704 case ScriptElementKind.constElement: 705 case ScriptElementKind.constructorImplementationElement: 706 displayParts.push(textOrKeywordPart(symbolKind)); 707 return; 708 default: 709 displayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); 710 displayParts.push(textOrKeywordPart(symbolKind)); 711 displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); 712 return; 713 } 714 } 715 716 function addSignatureDisplayParts(signature: Signature, allSignatures: readonly Signature[], flags = TypeFormatFlags.None) { 717 addRange(displayParts, signatureToDisplayParts(typeChecker, signature, enclosingDeclaration, flags | TypeFormatFlags.WriteTypeArgumentsOfSignature)); 718 if (allSignatures.length > 1) { 719 displayParts.push(spacePart()); 720 displayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); 721 displayParts.push(operatorPart(SyntaxKind.PlusToken)); 722 displayParts.push(displayPart((allSignatures.length - 1).toString(), SymbolDisplayPartKind.numericLiteral)); 723 displayParts.push(spacePart()); 724 displayParts.push(textPart(allSignatures.length === 2 ? "overload" : "overloads")); 725 displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); 726 } 727 documentation = signature.getDocumentationComment(typeChecker); 728 tags = signature.getJsDocTags(); 729 730 if (allSignatures.length > 1 && documentation.length === 0 && tags.length === 0) { 731 documentation = allSignatures[0].getDocumentationComment(typeChecker); 732 tags = allSignatures[0].getJsDocTags().filter(tag => tag.name !== "deprecated"); // should only include @deprecated JSDoc tag on the first overload (#49368) 733 } 734 735 } 736 737 function writeTypeParametersOfSymbol(symbol: Symbol, enclosingDeclaration: Node | undefined) { 738 const typeParameterParts = mapToDisplayParts(writer => { 739 const params = typeChecker.symbolToTypeParameterDeclarations(symbol, enclosingDeclaration, symbolDisplayNodeBuilderFlags); 740 getPrinter().writeList(ListFormat.TypeParameters, params, getSourceFileOfNode(getParseTreeNode(enclosingDeclaration)), writer); 741 }); 742 addRange(displayParts, typeParameterParts); 743 } 744 } 745 746 function isLocalVariableOrFunction(symbol: Symbol) { 747 if (symbol.parent) { 748 return false; // This is exported symbol 749 } 750 751 return forEach(symbol.declarations, declaration => { 752 // Function expressions are local 753 if (declaration.kind === SyntaxKind.FunctionExpression) { 754 return true; 755 } 756 757 if (declaration.kind !== SyntaxKind.VariableDeclaration && declaration.kind !== SyntaxKind.FunctionDeclaration) { 758 return false; 759 } 760 761 // If the parent is not sourceFile or module block it is local variable 762 for (let parent = declaration.parent; !isFunctionBlock(parent); parent = parent.parent) { 763 // Reached source file or module block 764 if (parent.kind === SyntaxKind.SourceFile || parent.kind === SyntaxKind.ModuleBlock) { 765 return false; 766 } 767 } 768 769 // parent is in function block 770 return true; 771 }); 772 } 773} 774