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