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