From c40e0f6738680509ce34c9672dea14a330130f6a Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 3 Dec 2014 14:00:58 -0800 Subject: [PATCH 1/3] Fixes the quickInfo when hovering over var inside the context sensitive expression Handles #1165 --- src/compiler/checker.ts | 16 ++++++++++++++-- src/compiler/types.ts | 3 +-- src/services/services.ts | 6 +++--- .../fourslash/quickInfoOnVarInArrowExpression.ts | 16 ++++++++++++++++ 4 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 tests/cases/fourslash/quickInfoOnVarInArrowExpression.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0e428b4a4e0..63ea9f1ae2b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -85,8 +85,7 @@ module ts { getDiagnostics, getDeclarationDiagnostics, getGlobalDiagnostics, - getParentOfSymbol, - getNarrowedTypeOfSymbol, + getTypeOfSymbolAtLocation, getDeclaredTypeOfSymbol, getPropertiesOfType, getPropertyOfType, @@ -4386,6 +4385,19 @@ module ts { } } + function getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type { + var containerNodes: Node[] = []; + for (var parent = node.parent; parent; parent = parent.parent) { + if (isExpression(parent) && isContextSensitiveExpression(parent)) { + containerNodes.unshift(parent) + }; + } + + ts.forEach(containerNodes, node => { getTypeOfNode(node); }); + + return getNarrowedTypeOfSymbol(symbol, node); + } + // Get the narrowed type of a given symbol at a given location function getNarrowedTypeOfSymbol(symbol: Symbol, node: Node) { var type = getTypeOfSymbol(symbol); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index edf052a8876..261a6fbfa31 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -883,8 +883,7 @@ module ts { getSymbolCount(): number; getTypeCount(): number; emitFiles(targetSourceFile?: SourceFile): EmitResult; - getParentOfSymbol(symbol: Symbol): Symbol; - getNarrowedTypeOfSymbol(symbol: Symbol, node: Node): Type; + getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type; getDeclaredTypeOfSymbol(symbol: Symbol): Type; getPropertiesOfType(type: Type): Symbol[]; getPropertyOfType(type: Type, propertyName: string): Symbol; diff --git a/src/services/services.ts b/src/services/services.ts index 0a0f98dd4b6..e7c964fb7e3 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2697,7 +2697,7 @@ module ts { // which is permissible given that it is backwards compatible; but really we should consider // passing the meaning for the node so that we don't report that a suggestion for a value is an interface. // We COULD also just do what 'getSymbolModifiers' does, which is to use the first declaration. - Debug.assert(session.typeChecker.getNarrowedTypeOfSymbol(symbol, location) !== undefined, "Could not find type for symbol"); + Debug.assert(session.typeChecker.getTypeOfSymbolAtLocation(symbol, location) !== undefined, "Could not find type for symbol"); var displayPartsDocumentationsAndSymbolKind = getSymbolDisplayPartsDocumentationAndSymbolKind(symbol, getSourceFile(filename), location, session.typeChecker, location, SemanticMeaning.All); return { name: entryName, @@ -2800,7 +2800,7 @@ module ts { if (!unionPropertyKind) { // If this was union of all methods, //make sure it has call signatures before we can label it as method - var typeOfUnionProperty = typeInfoResolver.getNarrowedTypeOfSymbol(symbol, location); + var typeOfUnionProperty = typeInfoResolver.getTypeOfSymbolAtLocation(symbol, location); if (typeOfUnionProperty.getCallSignatures().length) { return ScriptElementKind.memberFunctionElement; } @@ -2877,7 +2877,7 @@ module ts { symbolKind = ScriptElementKind.memberVariableElement; } - var type = typeResolver.getNarrowedTypeOfSymbol(symbol, location); + var type = typeResolver.getTypeOfSymbolAtLocation(symbol, location); if (type) { if (location.parent && location.parent.kind === SyntaxKind.PropertyAccessExpression) { var right = (location.parent).name; diff --git a/tests/cases/fourslash/quickInfoOnVarInArrowExpression.ts b/tests/cases/fourslash/quickInfoOnVarInArrowExpression.ts new file mode 100644 index 00000000000..54287c76952 --- /dev/null +++ b/tests/cases/fourslash/quickInfoOnVarInArrowExpression.ts @@ -0,0 +1,16 @@ +/// + +////interface IMap { +//// [key: string]: T; +////} +////var map: IMap; +////var categories: string[]; +////each(categories, category => { +//// var /*1*/changes = map[category]; +//// return each(changes, change => { +//// }); +////}); +////function each(items: T[], handler: (item: T) => void) { } + +goTo.marker('1'); +verify.quickInfoIs("(local var) changes: string[]", undefined); \ No newline at end of file From 1939c7f2cd210347281e0351892f341649598148 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 3 Dec 2014 14:47:13 -0800 Subject: [PATCH 2/3] Fix the getSymbolInfo and getTypeOfNode entry points to resolve the context sensitive information before resolving the actual node --- src/compiler/checker.ts | 29 ++++++++++++++----- src/compiler/types.ts | 4 +-- src/harness/typeWriter.ts | 2 +- src/services/services.ts | 22 +++++++------- src/services/signatureHelp.ts | 2 +- .../memberListOfVarInArrowExpression.ts | 18 ++++++++++++ 6 files changed, 55 insertions(+), 22 deletions(-) create mode 100644 tests/cases/fourslash/memberListOfVarInArrowExpression.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 63ea9f1ae2b..056d4895e8c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -93,9 +93,9 @@ module ts { getIndexTypeOfType, getReturnTypeOfSignature, getSymbolsInScope, - getSymbolInfo, + getSymbolInfoOfLocation, getShorthandAssignmentValueSymbol, - getTypeOfNode, + getTypeOfLocation, typeToString, getSymbolDisplayBuilder, symbolToString, @@ -4385,18 +4385,33 @@ module ts { } } - function getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type { + function resolveLocation(node: Node) { + // Resolve location from top down towards node if it is a context sensitive expression + // That helps in making sure not assigning types as any when resolved out of order var containerNodes: Node[] = []; for (var parent = node.parent; parent; parent = parent.parent) { - if (isExpression(parent) && isContextSensitiveExpression(parent)) { - containerNodes.unshift(parent) - }; + if (isExpression(parent) && isContextSensitiveExpression(parent)) { + containerNodes.unshift(parent); + } } ts.forEach(containerNodes, node => { getTypeOfNode(node); }); + } + function getSymbolInfoOfLocation(node: Node): Symbol { + resolveLocation(node); + return getSymbolInfo(node); + } + + function getTypeOfLocation(node: Node): Type { + resolveLocation(node); + return getTypeOfNode(node); + } + + function getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type { + resolveLocation(node); return getNarrowedTypeOfSymbol(symbol, node); - } + } // Get the narrowed type of a given symbol at a given location function getNarrowedTypeOfSymbol(symbol: Symbol, node: Node) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 261a6fbfa31..befd9c9def4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -891,9 +891,9 @@ module ts { getIndexTypeOfType(type: Type, kind: IndexKind): Type; getReturnTypeOfSignature(signature: Signature): Type; getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[]; - getSymbolInfo(node: Node): Symbol; + getSymbolInfoOfLocation(node: Node): Symbol; getShorthandAssignmentValueSymbol(location: Node): Symbol; - getTypeOfNode(node: Node): Type; + getTypeOfLocation(node: Node): Type; typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string; symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string; getSymbolDisplayBuilder(): SymbolDisplayBuilder; diff --git a/src/harness/typeWriter.ts b/src/harness/typeWriter.ts index 83aaaaf920a..11d2e62e10a 100644 --- a/src/harness/typeWriter.ts +++ b/src/harness/typeWriter.ts @@ -94,7 +94,7 @@ class TypeWriterWalker { } private getTypeOfNode(node: ts.Node): ts.Type { - var type = this.checker.getTypeOfNode(node); + var type = this.checker.getTypeOfLocation(node); ts.Debug.assert(type !== undefined, "type doesn't exist"); return type; } diff --git a/src/services/services.ts b/src/services/services.ts index e7c964fb7e3..08f3d1fa11d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2428,7 +2428,7 @@ module ts { isMemberCompletion = true; if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.PropertyAccessExpression) { - var symbol = typeInfoResolver.getSymbolInfo(node); + var symbol = typeInfoResolver.getSymbolInfoOfLocation(node); // This is an alias, follow what it aliases if (symbol && symbol.flags & SymbolFlags.Import) { @@ -2445,7 +2445,7 @@ module ts { } } - var type = typeInfoResolver.getTypeOfNode(node); + var type = typeInfoResolver.getTypeOfLocation(node); if (type) { // Filter private properties forEach(type.getApparentProperties(), symbol => { @@ -3089,7 +3089,7 @@ module ts { displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); } else { - var internalAliasSymbol = typeResolver.getSymbolInfo(importDeclaration.moduleReference); + var internalAliasSymbol = typeResolver.getSymbolInfoOfLocation(importDeclaration.moduleReference); if (internalAliasSymbol) { displayParts.push(spacePart()); displayParts.push(operatorPart(SyntaxKind.EqualsToken)); @@ -3199,7 +3199,7 @@ module ts { return undefined; } - var symbol = typeInfoResolver.getSymbolInfo(node); + var symbol = typeInfoResolver.getSymbolInfoOfLocation(node); if (!symbol) { // Try getting just type at this position and show switch (node.kind) { @@ -3209,7 +3209,7 @@ module ts { case SyntaxKind.ThisKeyword: case SyntaxKind.SuperKeyword: // For the identifiers/this/super etc get the type at position - var type = typeInfoResolver.getTypeOfNode(node); + var type = typeInfoResolver.getTypeOfLocation(node); if (type) { return { kind: ScriptElementKind.unknown, @@ -3326,7 +3326,7 @@ module ts { return undefined; } - var symbol = typeInfoResolver.getSymbolInfo(node); + var symbol = typeInfoResolver.getSymbolInfoOfLocation(node); // Could not find a symbol e.g. node is string or number keyword, // or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol @@ -3958,7 +3958,7 @@ module ts { return getReferencesForSuperKeyword(node); } - var symbol = typeInfoResolver.getSymbolInfo(node); + var symbol = typeInfoResolver.getSymbolInfoOfLocation(node); // Could not find a symbol e.g. unknown identifier if (!symbol) { @@ -4210,7 +4210,7 @@ module ts { return; } - var referenceSymbol = typeInfoResolver.getSymbolInfo(referenceLocation); + var referenceSymbol = typeInfoResolver.getSymbolInfoOfLocation(referenceLocation); if (referenceSymbol) { var referenceSymbolDeclaration = referenceSymbol.valueDeclaration; var shorthandValueSymbol = typeInfoResolver.getShorthandAssignmentValueSymbol(referenceSymbolDeclaration); @@ -4443,7 +4443,7 @@ module ts { function getPropertySymbolFromTypeReference(typeReference: TypeReferenceNode) { if (typeReference) { - var type = typeInfoResolver.getTypeOfNode(typeReference); + var type = typeInfoResolver.getTypeOfLocation(typeReference); if (type) { var propertySymbol = typeInfoResolver.getPropertyOfType(type, propertyName); if (propertySymbol) { @@ -4967,7 +4967,7 @@ module ts { // Only walk into nodes that intersect the requested span. if (node && span.intersectsWith(node.getStart(), node.getWidth())) { if (node.kind === SyntaxKind.Identifier && node.getWidth() > 0) { - var symbol = typeInfoResolver.getSymbolInfo(node); + var symbol = typeInfoResolver.getSymbolInfoOfLocation(node); if (symbol) { var type = classifySymbol(symbol, getMeaningFromLocation(node)); if (type) { @@ -5396,7 +5396,7 @@ module ts { // Can only rename an identifier. if (node && node.kind === SyntaxKind.Identifier) { - var symbol = typeInfoResolver.getSymbolInfo(node); + var symbol = typeInfoResolver.getSymbolInfoOfLocation(node); // Only allow a symbol to be renamed if it actually has at least one declaration. if (symbol && symbol.getDeclarations() && symbol.getDeclarations().length > 0) { diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 93363b4676b..a8e0ce185f6 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -457,7 +457,7 @@ module ts.SignatureHelp { var invocation = argumentListInfo.invocation; var callTarget = getInvokedExpression(invocation) - var callTargetSymbol = typeInfoResolver.getSymbolInfo(callTarget); + var callTargetSymbol = typeInfoResolver.getSymbolInfoOfLocation(callTarget); var callTargetDisplayParts = callTargetSymbol && symbolToDisplayParts(typeInfoResolver, callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined); var items: SignatureHelpItem[] = map(candidates, candidateSignature => { var signatureHelpParameters: SignatureHelpParameter[]; diff --git a/tests/cases/fourslash/memberListOfVarInArrowExpression.ts b/tests/cases/fourslash/memberListOfVarInArrowExpression.ts new file mode 100644 index 00000000000..03da06989ae --- /dev/null +++ b/tests/cases/fourslash/memberListOfVarInArrowExpression.ts @@ -0,0 +1,18 @@ +/// + +////interface IMap { +//// [key: string]: T; +////} +////var map: IMap<{ a1: string; }[]>; +////var categories: string[]; +////each(categories, category => { +//// var changes = map[category]; +//// changes[0]./*1*/a1; +//// return each(changes, change => { +//// }); +////}); +////function each(items: T[], handler: (item: T) => void) { } + +goTo.marker('1'); +verify.quickInfoIs("(property) a1: string"); +verify.memberListContains("a1", "(property) a1: string"); \ No newline at end of file From c3c44dc3c81ee12ddf9ede71f94a4b731feb8a45 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 4 Dec 2014 12:55:54 -0800 Subject: [PATCH 3/3] Some renaming and added comments as per feedback --- src/compiler/checker.ts | 20 ++++++++++++++++---- src/compiler/types.ts | 4 ++-- src/services/services.ts | 22 +++++++++++----------- src/services/signatureHelp.ts | 2 +- 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 056d4895e8c..f58d5808816 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -93,9 +93,9 @@ module ts { getIndexTypeOfType, getReturnTypeOfSignature, getSymbolsInScope, - getSymbolInfoOfLocation, + getSymbolAtLocation, getShorthandAssignmentValueSymbol, - getTypeOfLocation, + getTypeAtLocation, typeToString, getSymbolDisplayBuilder, symbolToString, @@ -4398,18 +4398,30 @@ module ts { ts.forEach(containerNodes, node => { getTypeOfNode(node); }); } - function getSymbolInfoOfLocation(node: Node): Symbol { + function getSymbolAtLocation(node: Node): Symbol { resolveLocation(node); return getSymbolInfo(node); } - function getTypeOfLocation(node: Node): Type { + function getTypeAtLocation(node: Node): Type { resolveLocation(node); return getTypeOfNode(node); } function getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type { resolveLocation(node); + // Get the narrowed type of symbol at given location instead of just getting + // the type of the symbol. + // eg. + // function foo(a: string | number) { + // if (typeof a === "string") { + // a/**/ + // } + // } + // getTypeOfSymbol for a would return type of parameter symbol string | number + // Unless we provide location /**/, checker wouldn't know how to narrow the type + // By using getNarrowedTypeOfSymbol would return string since it would be able to narrow + // it by typeguard in the if true condition return getNarrowedTypeOfSymbol(symbol, node); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index befd9c9def4..f16444ba609 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -891,9 +891,9 @@ module ts { getIndexTypeOfType(type: Type, kind: IndexKind): Type; getReturnTypeOfSignature(signature: Signature): Type; getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[]; - getSymbolInfoOfLocation(node: Node): Symbol; + getSymbolAtLocation(node: Node): Symbol; getShorthandAssignmentValueSymbol(location: Node): Symbol; - getTypeOfLocation(node: Node): Type; + getTypeAtLocation(node: Node): Type; typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string; symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string; getSymbolDisplayBuilder(): SymbolDisplayBuilder; diff --git a/src/services/services.ts b/src/services/services.ts index 08f3d1fa11d..d2222a3ae9d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2428,7 +2428,7 @@ module ts { isMemberCompletion = true; if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.PropertyAccessExpression) { - var symbol = typeInfoResolver.getSymbolInfoOfLocation(node); + var symbol = typeInfoResolver.getSymbolAtLocation(node); // This is an alias, follow what it aliases if (symbol && symbol.flags & SymbolFlags.Import) { @@ -2445,7 +2445,7 @@ module ts { } } - var type = typeInfoResolver.getTypeOfLocation(node); + var type = typeInfoResolver.getTypeAtLocation(node); if (type) { // Filter private properties forEach(type.getApparentProperties(), symbol => { @@ -3089,7 +3089,7 @@ module ts { displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); } else { - var internalAliasSymbol = typeResolver.getSymbolInfoOfLocation(importDeclaration.moduleReference); + var internalAliasSymbol = typeResolver.getSymbolAtLocation(importDeclaration.moduleReference); if (internalAliasSymbol) { displayParts.push(spacePart()); displayParts.push(operatorPart(SyntaxKind.EqualsToken)); @@ -3199,7 +3199,7 @@ module ts { return undefined; } - var symbol = typeInfoResolver.getSymbolInfoOfLocation(node); + var symbol = typeInfoResolver.getSymbolAtLocation(node); if (!symbol) { // Try getting just type at this position and show switch (node.kind) { @@ -3209,7 +3209,7 @@ module ts { case SyntaxKind.ThisKeyword: case SyntaxKind.SuperKeyword: // For the identifiers/this/super etc get the type at position - var type = typeInfoResolver.getTypeOfLocation(node); + var type = typeInfoResolver.getTypeAtLocation(node); if (type) { return { kind: ScriptElementKind.unknown, @@ -3326,7 +3326,7 @@ module ts { return undefined; } - var symbol = typeInfoResolver.getSymbolInfoOfLocation(node); + var symbol = typeInfoResolver.getSymbolAtLocation(node); // Could not find a symbol e.g. node is string or number keyword, // or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol @@ -3958,7 +3958,7 @@ module ts { return getReferencesForSuperKeyword(node); } - var symbol = typeInfoResolver.getSymbolInfoOfLocation(node); + var symbol = typeInfoResolver.getSymbolAtLocation(node); // Could not find a symbol e.g. unknown identifier if (!symbol) { @@ -4210,7 +4210,7 @@ module ts { return; } - var referenceSymbol = typeInfoResolver.getSymbolInfoOfLocation(referenceLocation); + var referenceSymbol = typeInfoResolver.getSymbolAtLocation(referenceLocation); if (referenceSymbol) { var referenceSymbolDeclaration = referenceSymbol.valueDeclaration; var shorthandValueSymbol = typeInfoResolver.getShorthandAssignmentValueSymbol(referenceSymbolDeclaration); @@ -4443,7 +4443,7 @@ module ts { function getPropertySymbolFromTypeReference(typeReference: TypeReferenceNode) { if (typeReference) { - var type = typeInfoResolver.getTypeOfLocation(typeReference); + var type = typeInfoResolver.getTypeAtLocation(typeReference); if (type) { var propertySymbol = typeInfoResolver.getPropertyOfType(type, propertyName); if (propertySymbol) { @@ -4967,7 +4967,7 @@ module ts { // Only walk into nodes that intersect the requested span. if (node && span.intersectsWith(node.getStart(), node.getWidth())) { if (node.kind === SyntaxKind.Identifier && node.getWidth() > 0) { - var symbol = typeInfoResolver.getSymbolInfoOfLocation(node); + var symbol = typeInfoResolver.getSymbolAtLocation(node); if (symbol) { var type = classifySymbol(symbol, getMeaningFromLocation(node)); if (type) { @@ -5396,7 +5396,7 @@ module ts { // Can only rename an identifier. if (node && node.kind === SyntaxKind.Identifier) { - var symbol = typeInfoResolver.getSymbolInfoOfLocation(node); + var symbol = typeInfoResolver.getSymbolAtLocation(node); // Only allow a symbol to be renamed if it actually has at least one declaration. if (symbol && symbol.getDeclarations() && symbol.getDeclarations().length > 0) { diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index a8e0ce185f6..23d5c34fefb 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -457,7 +457,7 @@ module ts.SignatureHelp { var invocation = argumentListInfo.invocation; var callTarget = getInvokedExpression(invocation) - var callTargetSymbol = typeInfoResolver.getSymbolInfoOfLocation(callTarget); + var callTargetSymbol = typeInfoResolver.getSymbolAtLocation(callTarget); var callTargetDisplayParts = callTargetSymbol && symbolToDisplayParts(typeInfoResolver, callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined); var items: SignatureHelpItem[] = map(candidates, candidateSignature => { var signatureHelpParameters: SignatureHelpParameter[];