Fix generic symbol display information

This commit is contained in:
Sheetal Nandi
2014-10-06 12:29:49 -07:00
parent 61994a7e62
commit be051f02b5
9 changed files with 109 additions and 61 deletions

View File

@@ -103,6 +103,7 @@ module ts {
getSignatureFromDeclaration: getSignatureFromDeclaration,
writeSignature: writeSignature,
writeTypeParameter: writeTypeParameter,
writeTypeParametersOfSymbol: writeTypeParametersOfSymbol,
isImplementationOfOverload: isImplementationOfOverload
};
@@ -966,18 +967,26 @@ module ts {
// Enclosing declaration is optional when we don't want to get qualified name in the enclosing declaration scope
// Meaning needs to be specified if the enclosing declaration is given
function writeSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags): void {
var previouslyWrittenSymbol: Symbol;
function writeSymbolName(symbol: Symbol): void {
if (previouslyWrittenSymbol) {
// Write type arguments of instantiated class/interface here
if (flags & SymbolFormatFlags.WriteTypeParametersOrArguments) {
if (symbol.flags & SymbolFlags.Instantiated) {
writeTypeArguments(getTypeParametersOfClassOrInterface(previouslyWrittenSymbol),
(<TransientSymbol>symbol).mapper, writer, enclosingDeclaration);
}
else {
writeTypeParametersOfSymbol(previouslyWrittenSymbol, writer, enclosingDeclaration);
}
}
writePunctuation(writer, SyntaxKind.DotToken);
}
previouslyWrittenSymbol = symbol;
if (symbol.declarations && symbol.declarations.length > 0) {
var declaration = symbol.declarations[0];
if (declaration.name) {
writer.writeSymbol(identifierToString(declaration.name), symbol);
if (flags & SymbolFormatFlags.WriteTypeParametersOfClassOrInterface) {
var rootSymbol = getRootSymbol(symbol);
if (rootSymbol.flags & SymbolFlags.Class || rootSymbol.flags & SymbolFlags.Interface) {
writeTypeParameters(getTypeParametersOfClassOrInterface(symbol), writer, enclosingDeclaration);
}
}
return;
}
}
@@ -985,16 +994,14 @@ module ts {
writer.writeSymbol(symbol.name, symbol);
}
// Let the writer know we just wrote out a symbol. The declarationemitter writer uses
// this to determine if an import it has previously seen (and not writter out) needs
// Let the writer know we just wrote out a symbol. The declaration emitter writer uses
// this to determine if an import it has previously seen (and not writer out) needs
// to be written to the file once the walk of the tree is complete.
//
// NOTE(cyrusn): This approach feels somewhat unfortunate. A simple pass over the tree
// up front (for example, during checking) could determien if we need to emit the imports
// up front (for example, during checking) could determine if we need to emit the imports
// and we could then access that data during declaration emit.
writer.trackSymbol(symbol, enclosingDeclaration, meaning);
var needsDot = false;
function walkSymbol(symbol: Symbol, meaning: SymbolFlags): void {
if (symbol) {
var accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning);
@@ -1010,17 +1017,12 @@ module ts {
if (accessibleSymbolChain) {
for (var i = 0, n = accessibleSymbolChain.length; i < n; i++) {
if (needsDot) {
writePunctuation(writer, SyntaxKind.DotToken);
}
writeSymbolName(accessibleSymbolChain[i]);
needsDot = true;
}
}
else {
// If we didn't find accessible symbol chain for this symbol, break if this is external module
if (!needsDot && ts.forEach(symbol.declarations, declaration => hasExternalModuleSymbol(declaration))) {
if (!previouslyWrittenSymbol && ts.forEach(symbol.declarations, declaration => hasExternalModuleSymbol(declaration))) {
return;
}
@@ -1029,12 +1031,7 @@ module ts {
return;
}
if (needsDot) {
writePunctuation(writer, SyntaxKind.DotToken);
}
writeSymbolName(symbol);
needsDot = true;
}
}
}
@@ -1303,8 +1300,35 @@ module ts {
}
}
function writeTypeArguments(typeParameters: TypeParameter[], mapper: TypeMapper, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
if (typeParameters && typeParameters.length) {
writePunctuation(writer, SyntaxKind.LessThanToken);
for (var i = 0; i < typeParameters.length; i++) {
if (i > 0) {
writePunctuation(writer, SyntaxKind.CommaToken);
writeSpace(writer);
}
writeType(mapper(typeParameters[i]), writer, enclosingDeclaration, TypeFormatFlags.WriteArrowStyleSignature);
}
writePunctuation(writer, SyntaxKind.GreaterThanToken);
}
}
function writeTypeParametersOfSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaraiton?: Node, flags?: TypeFormatFlags) {
var rootSymbol = getRootSymbol(symbol);
if (rootSymbol.flags & SymbolFlags.Class || rootSymbol.flags & SymbolFlags.Interface) {
writeTypeParameters(getTypeParametersOfClassOrInterface(symbol), writer, enclosingDeclaraiton, flags);
}
}
function writeSignature(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
writeTypeParameters(signature.typeParameters, writer, enclosingDeclaration, flags, typeStack);
if (signature.target && (flags & TypeFormatFlags.WriteTypeArgumentsOfSignature)) {
// Instantiated signature, write type arguments instead
writeTypeArguments(signature.target.typeParameters, signature.mapper, writer, enclosingDeclaration);
}
else {
writeTypeParameters(signature.typeParameters, writer, enclosingDeclaration, flags, typeStack);
}
writePunctuation(writer, SyntaxKind.OpenParenToken);
for (var i = 0; i < signature.parameters.length; i++) {
if (i > 0) {

View File

@@ -654,6 +654,7 @@ module ts {
getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature;
writeSignature(signatures: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
writeTypeParameter(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
writeTypeParametersOfSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaraiton?: Node, flags?: TypeFormatFlags): void;
isImplementationOfOverload(node: FunctionDeclaration): boolean;
// Returns the constant value of this enum member, or 'undefined' if the enum member has a
@@ -684,11 +685,15 @@ module ts {
NoTruncation = 0x00000004, // Don't truncate typeToString result
WriteArrowStyleSignature= 0x00000008, // Write arrow style signature
WriteOwnNameForAnyLike = 0x00000010, // Write symbol's own name instead of 'any' for any like types (eg. unknown, __resolving__ etc)
WriteTypeArgumentsOfSignature = 0x00000020, // Write the type arguments instead of type parameters of the signature
}
export enum SymbolFormatFlags {
None = 0x00000000,
WriteTypeParametersOfClassOrInterface = 0x00000001, // Write c<T> instead of just writing symbol name c of generic class
None = 0x00000000,
WriteTypeParametersOrArguments = 0x00000001, // Write symbols's type argument if it is instantiated symbol
// eg. class C<T> { p: T } <-- Show p as C<T>.p here
// var a: C<number>;
// var p = a.p; <--- Here p is property of C<number> so show it as C<number>.p instead of just C.p
}
export enum SymbolAccessibility {

View File

@@ -2726,7 +2726,7 @@ module ts {
var useConstructSignatures = callExpression.kind === SyntaxKind.NewExpression || callExpression.func.kind === SyntaxKind.SuperKeyword;
var allSignatures = useConstructSignatures ? type.getConstructSignatures() : type.getCallSignatures();
if (contains(allSignatures, signature)) {
if (contains(allSignatures, signature.target || signature)) {
// Write it as method/function/constructor as: (constructor) a(....)
if (symbolKind === ScriptElementKind.memberVariableElement || symbolFlags & SymbolFlags.Variable || symbolFlags & SymbolFlags.Class) {
if (useConstructSignatures) {
@@ -2793,13 +2793,15 @@ module ts {
if (symbolFlags & SymbolFlags.Class && !hasAddedSymbolInfo) {
displayParts.push(keywordPart(SyntaxKind.ClassKeyword));
displayParts.push(spacePart());
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface));
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments));
writeTypeParametersOfSymbol(symbol, sourceFile);
}
if (symbolFlags & SymbolFlags.Interface) {
addNewLineIfDisplayPartsExist();
displayParts.push(keywordPart(SyntaxKind.InterfaceKeyword));
displayParts.push(spacePart());
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface));
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments));
writeTypeParametersOfSymbol(symbol, sourceFile);
}
if (symbolFlags & SymbolFlags.Enum) {
addNewLineIfDisplayPartsExist();
@@ -2825,7 +2827,8 @@ module ts {
displayParts.push(spacePart());
if (symbol.parent) {
// Class/Interface type parameter
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol.parent, enclosingDeclaration, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface))
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol.parent, enclosingDeclaration, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments))
writeTypeParametersOfSymbol(symbol.parent, enclosingDeclaration);
}
else {
// Method/function type parameter
@@ -2835,9 +2838,9 @@ module ts {
displayParts.push(keywordPart(SyntaxKind.NewKeyword));
displayParts.push(spacePart());
} else if (signatureDeclaration.kind !== SyntaxKind.CallSignature) {
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, signatureDeclaration.symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface))
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, signatureDeclaration.symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments))
}
displayParts.push.apply(displayParts, signatureToDisplayParts(typeResolver, signature, sourceFile, TypeFormatFlags.NoTruncation));
displayParts.push.apply(displayParts, signatureToDisplayParts(typeResolver, signature, sourceFile, TypeFormatFlags.NoTruncation | TypeFormatFlags.WriteTypeArgumentsOfSignature));
}
}
if (symbolFlags & SymbolFlags.EnumMember) {
@@ -2910,12 +2913,12 @@ module ts {
displayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
displayParts.push(spacePart());
// Write type parameters of class/Interface if it is property/method of the generic class/interface
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface));
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments));
}
}
function addSignatureDisplayParts(signature: Signature, allSignatures: Signature[]) {
displayParts.push.apply(displayParts, signatureToDisplayParts(typeResolver, signature, enclosingDeclaration, TypeFormatFlags.NoTruncation));
displayParts.push.apply(displayParts, signatureToDisplayParts(typeResolver, signature, enclosingDeclaration, TypeFormatFlags.NoTruncation | TypeFormatFlags.WriteTypeArgumentsOfSignature));
if (allSignatures.length > 1) {
displayParts.push(spacePart());
displayParts.push(punctuationPart(SyntaxKind.OpenParenToken));
@@ -2931,6 +2934,13 @@ module ts {
}
documentation = signature.getDocumentationComment();
}
function writeTypeParametersOfSymbol(symbol: Symbol, enclosingDeclaration: Node) {
var typeParameterParts = mapToDisplayParts(writer => {
typeResolver.writeTypeParametersOfSymbol(symbol, writer, enclosingDeclaration, TypeFormatFlags.NoTruncation);
});
displayParts.push.apply(displayParts, typeParameterParts);
}
}
function getQuickInfoAtPosition(fileName: string, position: number): QuickInfo {

View File

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

View File

@@ -21,4 +21,4 @@ goTo.marker();
verify.completionListContains('watch', '(property) iBaseScope.watch: () => void');
verify.completionListContains('moveUp', '(property) iMover.moveUp: () => void');
debugger;
verify.completionListContains('family', '(property) iScope<TModel>.family: number');
verify.completionListContains('family', '(property) iScope<number>.family: number');

View File

@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts'/>
////class C<T> {
//// foo(x: T) { }
////}
////var x = new /*1*/C<any>();
////var y = C.proto/*2*/type;
goTo.marker('1');
verify.quickInfoIs('(constructor) C<any>(): C<any>');
goTo.marker('2');
verify.quickInfoIs('(property) C<T>.prototype: C<any>');

View File

@@ -6,7 +6,7 @@
////interface IList</*2*/T> {
//// getItem(i: number): /*3*/T;
////}
////class List</*4*/T extends IList<number>> implements IList<T> {
////class List2</*4*/T extends IList<number>> implements IList<T> {
//// private __it/*6*/em: /*5*/T[];
//// public get/*7*/Item(i: number) {
//// return this.__item[i];
@@ -19,6 +19,11 @@
////function foo4</*12*/S extends string>(test: S): S;
////function foo4(test: any): any;
////function foo4</*13*/T extends Date>(test: any): any { return null; }
////var x: List2<IList<number>>;
////var y = x./*14*/getItem(10);
////var x2: IList<IList<number>>;
////var x3: IList<number>;
////var y2 = x./*15*/method(x2, [x3, x3]);
goTo.marker("1");
verify.quickInfoIs("class Container<T>", undefined);
@@ -27,22 +32,26 @@ verify.quickInfoIs("(type parameter) T in IList<T>", undefined);
goTo.marker("3");
verify.quickInfoIs("(type parameter) T in IList<T>", undefined);
goTo.marker("4");
verify.quickInfoIs("(type parameter) T in List<T extends IList<number>>", undefined);
verify.quickInfoIs("(type parameter) T in List2<T extends IList<number>>", undefined);
goTo.marker("5");
verify.quickInfoIs("(type parameter) T in List<T extends IList<number>>", undefined);
verify.quickInfoIs("(type parameter) T in List2<T extends IList<number>>", undefined);
goTo.marker("6");
verify.quickInfoIs("(property) List<T extends IList<number>>.__item: T[]", undefined);
verify.quickInfoIs("(property) List2<T extends IList<number>>.__item: T[]", undefined);
goTo.marker("7");
verify.quickInfoIs("(method) List<T extends IList<number>>.getItem(i: number): T", undefined);
verify.quickInfoIs("(method) List2<T extends IList<number>>.getItem(i: number): T", undefined);
goTo.marker("8");
verify.quickInfoIs("(method) List<T extends IList<number>>.method<S extends IList<T>>(s: S, p: T[]): S", undefined);
verify.quickInfoIs("(method) List2<T extends IList<number>>.method<S extends IList<T>>(s: S, p: T[]): S", undefined);
goTo.marker("9");
verify.quickInfoIs("(type parameter) S in List<T extends IList<number>>.method<S extends IList<T>>(s: S, p: T[]): S", undefined);
verify.quickInfoIs("(type parameter) S in List2<T extends IList<number>>.method<S extends IList<T>>(s: S, p: T[]): S", undefined);
goTo.marker("10");
verify.quickInfoIs("(type parameter) T in List<T extends IList<number>>", undefined);
verify.quickInfoIs("(type parameter) T in List2<T extends IList<number>>", undefined);
goTo.marker("11");
verify.quickInfoIs("(type parameter) T in foo4<T extends Date>(test: T): T", undefined);
goTo.marker("12");
verify.quickInfoIs("(type parameter) S in foo4<S extends string>(test: S): S", undefined);
goTo.marker("13");
verify.quickInfoIs("(type parameter) T in foo4<T extends Date>(test: any): any", undefined);
verify.quickInfoIs("(type parameter) T in foo4<T extends Date>(test: any): any", undefined);
goTo.marker("14");
verify.quickInfoIs("(method) List2<IList<number>>.getItem(i: number): IList<number>", undefined);
goTo.marker("15");
verify.quickInfoIs("(method) List2<IList<number>>.method<IList<IList<number>>>(s: IList<IList<number>>, p: IList<number>[]): IList<IList<number>>", undefined);

View File

@@ -15,14 +15,14 @@
diagnostics.setEditValidation(IncrementalEditValidation.None);
goTo.marker('1');
verify.quickInfoIs("(property) B<T>.bar: string", undefined);
verify.quickInfoIs("(property) B<string>.bar: string", undefined);
edit.deleteAtCaret(1);
edit.insert('z');
verify.quickInfoIs("any");
verify.numberOfErrorsInCurrentFile(1);
edit.backspace(1);
edit.insert('a');
verify.quickInfoIs("(property) B<T>.bar: string", undefined);
verify.quickInfoIs("(property) B<string>.bar: string", undefined);
goTo.marker('2');
verify.quickInfoIs("(var) r4: string", undefined);
verify.numberOfErrorsInCurrentFile(0);

View File

@@ -1,13 +0,0 @@
/// <reference path='fourslash.ts'/>
////class C<T> {
//// foo(x: T) { }
////}
////var x = new C/*1*/<any>();
////var y = C.proto/*2*/type;
goTo.marker('1');
verify.quickInfoIs('(): C<any>');
goTo.marker('2');
verify.quickInfoIs('C<any>');