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