Skip to content

Commit be051f0

Browse files
committed
Fix generic symbol display information
1 parent 61994a7 commit be051f0

9 files changed

+109
-61
lines changed

src/compiler/checker.ts

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ module ts {
103103
getSignatureFromDeclaration: getSignatureFromDeclaration,
104104
writeSignature: writeSignature,
105105
writeTypeParameter: writeTypeParameter,
106+
writeTypeParametersOfSymbol: writeTypeParametersOfSymbol,
106107
isImplementationOfOverload: isImplementationOfOverload
107108
};
108109

@@ -966,35 +967,41 @@ module ts {
966967
// Enclosing declaration is optional when we don't want to get qualified name in the enclosing declaration scope
967968
// Meaning needs to be specified if the enclosing declaration is given
968969
function writeSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags): void {
970+
var previouslyWrittenSymbol: Symbol;
969971
function writeSymbolName(symbol: Symbol): void {
972+
if (previouslyWrittenSymbol) {
973+
// Write type arguments of instantiated class/interface here
974+
if (flags & SymbolFormatFlags.WriteTypeParametersOrArguments) {
975+
if (symbol.flags & SymbolFlags.Instantiated) {
976+
writeTypeArguments(getTypeParametersOfClassOrInterface(previouslyWrittenSymbol),
977+
(<TransientSymbol>symbol).mapper, writer, enclosingDeclaration);
978+
}
979+
else {
980+
writeTypeParametersOfSymbol(previouslyWrittenSymbol, writer, enclosingDeclaration);
981+
}
982+
}
983+
writePunctuation(writer, SyntaxKind.DotToken);
984+
}
985+
previouslyWrittenSymbol = symbol;
970986
if (symbol.declarations && symbol.declarations.length > 0) {
971987
var declaration = symbol.declarations[0];
972988
if (declaration.name) {
973989
writer.writeSymbol(identifierToString(declaration.name), symbol);
974-
if (flags & SymbolFormatFlags.WriteTypeParametersOfClassOrInterface) {
975-
var rootSymbol = getRootSymbol(symbol);
976-
if (rootSymbol.flags & SymbolFlags.Class || rootSymbol.flags & SymbolFlags.Interface) {
977-
writeTypeParameters(getTypeParametersOfClassOrInterface(symbol), writer, enclosingDeclaration);
978-
}
979-
}
980-
981990
return;
982991
}
983992
}
984993

985994
writer.writeSymbol(symbol.name, symbol);
986995
}
987996

988-
// Let the writer know we just wrote out a symbol. The declarationemitter writer uses
989-
// this to determine if an import it has previously seen (and not writter out) needs
997+
// Let the writer know we just wrote out a symbol. The declaration emitter writer uses
998+
// this to determine if an import it has previously seen (and not writer out) needs
990999
// to be written to the file once the walk of the tree is complete.
9911000
//
9921001
// NOTE(cyrusn): This approach feels somewhat unfortunate. A simple pass over the tree
993-
// up front (for example, during checking) could determien if we need to emit the imports
1002+
// up front (for example, during checking) could determine if we need to emit the imports
9941003
// and we could then access that data during declaration emit.
9951004
writer.trackSymbol(symbol, enclosingDeclaration, meaning);
996-
997-
var needsDot = false;
9981005
function walkSymbol(symbol: Symbol, meaning: SymbolFlags): void {
9991006
if (symbol) {
10001007
var accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning);
@@ -1010,17 +1017,12 @@ module ts {
10101017

10111018
if (accessibleSymbolChain) {
10121019
for (var i = 0, n = accessibleSymbolChain.length; i < n; i++) {
1013-
if (needsDot) {
1014-
writePunctuation(writer, SyntaxKind.DotToken);
1015-
}
1016-
10171020
writeSymbolName(accessibleSymbolChain[i]);
1018-
needsDot = true;
10191021
}
10201022
}
10211023
else {
10221024
// If we didn't find accessible symbol chain for this symbol, break if this is external module
1023-
if (!needsDot && ts.forEach(symbol.declarations, declaration => hasExternalModuleSymbol(declaration))) {
1025+
if (!previouslyWrittenSymbol && ts.forEach(symbol.declarations, declaration => hasExternalModuleSymbol(declaration))) {
10241026
return;
10251027
}
10261028

@@ -1029,12 +1031,7 @@ module ts {
10291031
return;
10301032
}
10311033

1032-
if (needsDot) {
1033-
writePunctuation(writer, SyntaxKind.DotToken);
1034-
}
1035-
10361034
writeSymbolName(symbol);
1037-
needsDot = true;
10381035
}
10391036
}
10401037
}
@@ -1303,8 +1300,35 @@ module ts {
13031300
}
13041301
}
13051302

1303+
function writeTypeArguments(typeParameters: TypeParameter[], mapper: TypeMapper, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
1304+
if (typeParameters && typeParameters.length) {
1305+
writePunctuation(writer, SyntaxKind.LessThanToken);
1306+
for (var i = 0; i < typeParameters.length; i++) {
1307+
if (i > 0) {
1308+
writePunctuation(writer, SyntaxKind.CommaToken);
1309+
writeSpace(writer);
1310+
}
1311+
writeType(mapper(typeParameters[i]), writer, enclosingDeclaration, TypeFormatFlags.WriteArrowStyleSignature);
1312+
}
1313+
writePunctuation(writer, SyntaxKind.GreaterThanToken);
1314+
}
1315+
}
1316+
1317+
function writeTypeParametersOfSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaraiton?: Node, flags?: TypeFormatFlags) {
1318+
var rootSymbol = getRootSymbol(symbol);
1319+
if (rootSymbol.flags & SymbolFlags.Class || rootSymbol.flags & SymbolFlags.Interface) {
1320+
writeTypeParameters(getTypeParametersOfClassOrInterface(symbol), writer, enclosingDeclaraiton, flags);
1321+
}
1322+
}
1323+
13061324
function writeSignature(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
1307-
writeTypeParameters(signature.typeParameters, writer, enclosingDeclaration, flags, typeStack);
1325+
if (signature.target && (flags & TypeFormatFlags.WriteTypeArgumentsOfSignature)) {
1326+
// Instantiated signature, write type arguments instead
1327+
writeTypeArguments(signature.target.typeParameters, signature.mapper, writer, enclosingDeclaration);
1328+
}
1329+
else {
1330+
writeTypeParameters(signature.typeParameters, writer, enclosingDeclaration, flags, typeStack);
1331+
}
13081332
writePunctuation(writer, SyntaxKind.OpenParenToken);
13091333
for (var i = 0; i < signature.parameters.length; i++) {
13101334
if (i > 0) {

src/compiler/types.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,7 @@ module ts {
654654
getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature;
655655
writeSignature(signatures: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
656656
writeTypeParameter(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
657+
writeTypeParametersOfSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaraiton?: Node, flags?: TypeFormatFlags): void;
657658
isImplementationOfOverload(node: FunctionDeclaration): boolean;
658659

659660
// Returns the constant value of this enum member, or 'undefined' if the enum member has a
@@ -684,11 +685,15 @@ module ts {
684685
NoTruncation = 0x00000004, // Don't truncate typeToString result
685686
WriteArrowStyleSignature= 0x00000008, // Write arrow style signature
686687
WriteOwnNameForAnyLike = 0x00000010, // Write symbol's own name instead of 'any' for any like types (eg. unknown, __resolving__ etc)
688+
WriteTypeArgumentsOfSignature = 0x00000020, // Write the type arguments instead of type parameters of the signature
687689
}
688690

689691
export enum SymbolFormatFlags {
690-
None = 0x00000000,
691-
WriteTypeParametersOfClassOrInterface = 0x00000001, // Write c<T> instead of just writing symbol name c of generic class
692+
None = 0x00000000,
693+
WriteTypeParametersOrArguments = 0x00000001, // Write symbols's type argument if it is instantiated symbol
694+
// eg. class C<T> { p: T } <-- Show p as C<T>.p here
695+
// var a: C<number>;
696+
// var p = a.p; <--- Here p is property of C<number> so show it as C<number>.p instead of just C.p
692697
}
693698

694699
export enum SymbolAccessibility {

src/services/services.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2726,7 +2726,7 @@ module ts {
27262726
var useConstructSignatures = callExpression.kind === SyntaxKind.NewExpression || callExpression.func.kind === SyntaxKind.SuperKeyword;
27272727
var allSignatures = useConstructSignatures ? type.getConstructSignatures() : type.getCallSignatures();
27282728

2729-
if (contains(allSignatures, signature)) {
2729+
if (contains(allSignatures, signature.target || signature)) {
27302730
// Write it as method/function/constructor as: (constructor) a(....)
27312731
if (symbolKind === ScriptElementKind.memberVariableElement || symbolFlags & SymbolFlags.Variable || symbolFlags & SymbolFlags.Class) {
27322732
if (useConstructSignatures) {
@@ -2793,13 +2793,15 @@ module ts {
27932793
if (symbolFlags & SymbolFlags.Class && !hasAddedSymbolInfo) {
27942794
displayParts.push(keywordPart(SyntaxKind.ClassKeyword));
27952795
displayParts.push(spacePart());
2796-
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface));
2796+
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments));
2797+
writeTypeParametersOfSymbol(symbol, sourceFile);
27972798
}
27982799
if (symbolFlags & SymbolFlags.Interface) {
27992800
addNewLineIfDisplayPartsExist();
28002801
displayParts.push(keywordPart(SyntaxKind.InterfaceKeyword));
28012802
displayParts.push(spacePart());
2802-
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface));
2803+
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments));
2804+
writeTypeParametersOfSymbol(symbol, sourceFile);
28032805
}
28042806
if (symbolFlags & SymbolFlags.Enum) {
28052807
addNewLineIfDisplayPartsExist();
@@ -2825,7 +2827,8 @@ module ts {
28252827
displayParts.push(spacePart());
28262828
if (symbol.parent) {
28272829
// Class/Interface type parameter
2828-
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol.parent, enclosingDeclaration, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface))
2830+
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol.parent, enclosingDeclaration, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments))
2831+
writeTypeParametersOfSymbol(symbol.parent, enclosingDeclaration);
28292832
}
28302833
else {
28312834
// Method/function type parameter
@@ -2835,9 +2838,9 @@ module ts {
28352838
displayParts.push(keywordPart(SyntaxKind.NewKeyword));
28362839
displayParts.push(spacePart());
28372840
} else if (signatureDeclaration.kind !== SyntaxKind.CallSignature) {
2838-
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, signatureDeclaration.symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface))
2841+
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, signatureDeclaration.symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments))
28392842
}
2840-
displayParts.push.apply(displayParts, signatureToDisplayParts(typeResolver, signature, sourceFile, TypeFormatFlags.NoTruncation));
2843+
displayParts.push.apply(displayParts, signatureToDisplayParts(typeResolver, signature, sourceFile, TypeFormatFlags.NoTruncation | TypeFormatFlags.WriteTypeArgumentsOfSignature));
28412844
}
28422845
}
28432846
if (symbolFlags & SymbolFlags.EnumMember) {
@@ -2910,12 +2913,12 @@ module ts {
29102913
displayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
29112914
displayParts.push(spacePart());
29122915
// Write type parameters of class/Interface if it is property/method of the generic class/interface
2913-
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface));
2916+
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments));
29142917
}
29152918
}
29162919

29172920
function addSignatureDisplayParts(signature: Signature, allSignatures: Signature[]) {
2918-
displayParts.push.apply(displayParts, signatureToDisplayParts(typeResolver, signature, enclosingDeclaration, TypeFormatFlags.NoTruncation));
2921+
displayParts.push.apply(displayParts, signatureToDisplayParts(typeResolver, signature, enclosingDeclaration, TypeFormatFlags.NoTruncation | TypeFormatFlags.WriteTypeArgumentsOfSignature));
29192922
if (allSignatures.length > 1) {
29202923
displayParts.push(spacePart());
29212924
displayParts.push(punctuationPart(SyntaxKind.OpenParenToken));
@@ -2931,6 +2934,13 @@ module ts {
29312934
}
29322935
documentation = signature.getDocumentationComment();
29332936
}
2937+
2938+
function writeTypeParametersOfSymbol(symbol: Symbol, enclosingDeclaration: Node) {
2939+
var typeParameterParts = mapToDisplayParts(writer => {
2940+
typeResolver.writeTypeParametersOfSymbol(symbol, writer, enclosingDeclaration, TypeFormatFlags.NoTruncation);
2941+
});
2942+
displayParts.push.apply(displayParts, typeParameterParts);
2943+
}
29342944
}
29352945

29362946
function getQuickInfoAtPosition(fileName: string, position: number): QuickInfo {

tests/cases/fourslash/completionListOfGnericSymbol.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
////a./**/
77

88
goTo.marker();
9-
verify.memberListContains('length', "(property) Array<T>.length: number", /*docComments*/ undefined, /*kind*/ "property");
10-
verify.memberListContains('toString', "(method) Array<T>.toString(): string", /*docComments*/ undefined, /*kind*/ "method");
9+
verify.memberListContains('length', "(property) Array<number>.length: number", /*docComments*/ undefined, /*kind*/ "property");
10+
verify.memberListContains('toString', "(method) Array<number>.toString(): string", /*docComments*/ undefined, /*kind*/ "method");
1111

tests/cases/fourslash/genericTypeWithMultipleBases1MultiFile.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ goTo.marker();
2121
verify.completionListContains('watch', '(property) iBaseScope.watch: () => void');
2222
verify.completionListContains('moveUp', '(property) iMover.moveUp: () => void');
2323
debugger;
24-
verify.completionListContains('family', '(property) iScope<TModel>.family: number');
24+
verify.completionListContains('family', '(property) iScope<number>.family: number');
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
////class C<T> {
4+
//// foo(x: T) { }
5+
////}
6+
////var x = new /*1*/C<any>();
7+
////var y = C.proto/*2*/type;
8+
9+
goTo.marker('1');
10+
verify.quickInfoIs('(constructor) C<any>(): C<any>');
11+
12+
goTo.marker('2');
13+
verify.quickInfoIs('(property) C<T>.prototype: C<any>');

tests/cases/fourslash/quickInfoGenerics.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
////interface IList</*2*/T> {
77
//// getItem(i: number): /*3*/T;
88
////}
9-
////class List</*4*/T extends IList<number>> implements IList<T> {
9+
////class List2</*4*/T extends IList<number>> implements IList<T> {
1010
//// private __it/*6*/em: /*5*/T[];
1111
//// public get/*7*/Item(i: number) {
1212
//// return this.__item[i];
@@ -19,6 +19,11 @@
1919
////function foo4</*12*/S extends string>(test: S): S;
2020
////function foo4(test: any): any;
2121
////function foo4</*13*/T extends Date>(test: any): any { return null; }
22+
////var x: List2<IList<number>>;
23+
////var y = x./*14*/getItem(10);
24+
////var x2: IList<IList<number>>;
25+
////var x3: IList<number>;
26+
////var y2 = x./*15*/method(x2, [x3, x3]);
2227

2328
goTo.marker("1");
2429
verify.quickInfoIs("class Container<T>", undefined);
@@ -27,22 +32,26 @@ verify.quickInfoIs("(type parameter) T in IList<T>", undefined);
2732
goTo.marker("3");
2833
verify.quickInfoIs("(type parameter) T in IList<T>", undefined);
2934
goTo.marker("4");
30-
verify.quickInfoIs("(type parameter) T in List<T extends IList<number>>", undefined);
35+
verify.quickInfoIs("(type parameter) T in List2<T extends IList<number>>", undefined);
3136
goTo.marker("5");
32-
verify.quickInfoIs("(type parameter) T in List<T extends IList<number>>", undefined);
37+
verify.quickInfoIs("(type parameter) T in List2<T extends IList<number>>", undefined);
3338
goTo.marker("6");
34-
verify.quickInfoIs("(property) List<T extends IList<number>>.__item: T[]", undefined);
39+
verify.quickInfoIs("(property) List2<T extends IList<number>>.__item: T[]", undefined);
3540
goTo.marker("7");
36-
verify.quickInfoIs("(method) List<T extends IList<number>>.getItem(i: number): T", undefined);
41+
verify.quickInfoIs("(method) List2<T extends IList<number>>.getItem(i: number): T", undefined);
3742
goTo.marker("8");
38-
verify.quickInfoIs("(method) List<T extends IList<number>>.method<S extends IList<T>>(s: S, p: T[]): S", undefined);
43+
verify.quickInfoIs("(method) List2<T extends IList<number>>.method<S extends IList<T>>(s: S, p: T[]): S", undefined);
3944
goTo.marker("9");
40-
verify.quickInfoIs("(type parameter) S in List<T extends IList<number>>.method<S extends IList<T>>(s: S, p: T[]): S", undefined);
45+
verify.quickInfoIs("(type parameter) S in List2<T extends IList<number>>.method<S extends IList<T>>(s: S, p: T[]): S", undefined);
4146
goTo.marker("10");
42-
verify.quickInfoIs("(type parameter) T in List<T extends IList<number>>", undefined);
47+
verify.quickInfoIs("(type parameter) T in List2<T extends IList<number>>", undefined);
4348
goTo.marker("11");
4449
verify.quickInfoIs("(type parameter) T in foo4<T extends Date>(test: T): T", undefined);
4550
goTo.marker("12");
4651
verify.quickInfoIs("(type parameter) S in foo4<S extends string>(test: S): S", undefined);
4752
goTo.marker("13");
48-
verify.quickInfoIs("(type parameter) T in foo4<T extends Date>(test: any): any", undefined);
53+
verify.quickInfoIs("(type parameter) T in foo4<T extends Date>(test: any): any", undefined);
54+
goTo.marker("14");
55+
verify.quickInfoIs("(method) List2<IList<number>>.getItem(i: number): IList<number>", undefined);
56+
goTo.marker("15");
57+
verify.quickInfoIs("(method) List2<IList<number>>.method<IList<IList<number>>>(s: IList<IList<number>>, p: IList<number>[]): IList<IList<number>>", undefined);

0 commit comments

Comments
 (0)