• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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