Merge pull request #1362 from Microsoft/contextSensitiveExpressions
Resolve the context sensitive expression containers before resolving node
This commit is contained in:
commit
e3320c2530
7 changed files with 96 additions and 24 deletions
|
@ -85,8 +85,7 @@ module ts {
|
|||
getDiagnostics,
|
||||
getDeclarationDiagnostics,
|
||||
getGlobalDiagnostics,
|
||||
getParentOfSymbol,
|
||||
getNarrowedTypeOfSymbol,
|
||||
getTypeOfSymbolAtLocation,
|
||||
getDeclaredTypeOfSymbol,
|
||||
getPropertiesOfType,
|
||||
getPropertyOfType,
|
||||
|
@ -94,9 +93,9 @@ module ts {
|
|||
getIndexTypeOfType,
|
||||
getReturnTypeOfSignature,
|
||||
getSymbolsInScope,
|
||||
getSymbolInfo,
|
||||
getSymbolAtLocation,
|
||||
getShorthandAssignmentValueSymbol,
|
||||
getTypeOfNode,
|
||||
getTypeAtLocation,
|
||||
typeToString,
|
||||
getSymbolDisplayBuilder,
|
||||
symbolToString,
|
||||
|
@ -4391,6 +4390,46 @@ module ts {
|
|||
}
|
||||
}
|
||||
|
||||
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(<Expression>parent)) {
|
||||
containerNodes.unshift(parent);
|
||||
}
|
||||
}
|
||||
|
||||
ts.forEach(containerNodes, node => { getTypeOfNode(node); });
|
||||
}
|
||||
|
||||
function getSymbolAtLocation(node: Node): Symbol {
|
||||
resolveLocation(node);
|
||||
return getSymbolInfo(node);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Get the narrowed type of a given symbol at a given location
|
||||
function getNarrowedTypeOfSymbol(symbol: Symbol, node: Node) {
|
||||
var type = getTypeOfSymbol(symbol);
|
||||
|
|
|
@ -903,8 +903,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;
|
||||
|
@ -912,9 +911,9 @@ module ts {
|
|||
getIndexTypeOfType(type: Type, kind: IndexKind): Type;
|
||||
getReturnTypeOfSignature(signature: Signature): Type;
|
||||
getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[];
|
||||
getSymbolInfo(node: Node): Symbol;
|
||||
getSymbolAtLocation(node: Node): Symbol;
|
||||
getShorthandAssignmentValueSymbol(location: Node): Symbol;
|
||||
getTypeOfNode(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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -2433,7 +2433,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.getSymbolAtLocation(node);
|
||||
|
||||
// This is an alias, follow what it aliases
|
||||
if (symbol && symbol.flags & SymbolFlags.Import) {
|
||||
|
@ -2450,7 +2450,7 @@ module ts {
|
|||
}
|
||||
}
|
||||
|
||||
var type = typeInfoResolver.getTypeOfNode(node);
|
||||
var type = typeInfoResolver.getTypeAtLocation(node);
|
||||
if (type) {
|
||||
// Filter private properties
|
||||
forEach(type.getApparentProperties(), symbol => {
|
||||
|
@ -2702,7 +2702,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,
|
||||
|
@ -2805,7 +2805,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;
|
||||
}
|
||||
|
@ -2882,7 +2882,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 = (<PropertyAccessExpression>location.parent).name;
|
||||
|
@ -3094,7 +3094,7 @@ module ts {
|
|||
displayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
|
||||
}
|
||||
else {
|
||||
var internalAliasSymbol = typeResolver.getSymbolInfo(importDeclaration.moduleReference);
|
||||
var internalAliasSymbol = typeResolver.getSymbolAtLocation(importDeclaration.moduleReference);
|
||||
if (internalAliasSymbol) {
|
||||
displayParts.push(spacePart());
|
||||
displayParts.push(operatorPart(SyntaxKind.EqualsToken));
|
||||
|
@ -3204,7 +3204,7 @@ module ts {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
var symbol = typeInfoResolver.getSymbolInfo(node);
|
||||
var symbol = typeInfoResolver.getSymbolAtLocation(node);
|
||||
if (!symbol) {
|
||||
// Try getting just type at this position and show
|
||||
switch (node.kind) {
|
||||
|
@ -3214,7 +3214,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.getTypeAtLocation(node);
|
||||
if (type) {
|
||||
return {
|
||||
kind: ScriptElementKind.unknown,
|
||||
|
@ -3331,7 +3331,7 @@ module ts {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
var symbol = typeInfoResolver.getSymbolInfo(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
|
||||
|
@ -3963,7 +3963,7 @@ module ts {
|
|||
return getReferencesForSuperKeyword(node);
|
||||
}
|
||||
|
||||
var symbol = typeInfoResolver.getSymbolInfo(node);
|
||||
var symbol = typeInfoResolver.getSymbolAtLocation(node);
|
||||
|
||||
// Could not find a symbol e.g. unknown identifier
|
||||
if (!symbol) {
|
||||
|
@ -4215,7 +4215,7 @@ module ts {
|
|||
return;
|
||||
}
|
||||
|
||||
var referenceSymbol = typeInfoResolver.getSymbolInfo(referenceLocation);
|
||||
var referenceSymbol = typeInfoResolver.getSymbolAtLocation(referenceLocation);
|
||||
if (referenceSymbol) {
|
||||
var referenceSymbolDeclaration = referenceSymbol.valueDeclaration;
|
||||
var shorthandValueSymbol = typeInfoResolver.getShorthandAssignmentValueSymbol(referenceSymbolDeclaration);
|
||||
|
@ -4457,7 +4457,7 @@ module ts {
|
|||
|
||||
function getPropertySymbolFromTypeReference(typeReference: TypeReferenceNode) {
|
||||
if (typeReference) {
|
||||
var type = typeInfoResolver.getTypeOfNode(typeReference);
|
||||
var type = typeInfoResolver.getTypeAtLocation(typeReference);
|
||||
if (type) {
|
||||
var propertySymbol = typeInfoResolver.getPropertyOfType(type, propertyName);
|
||||
if (propertySymbol) {
|
||||
|
@ -4981,7 +4981,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.getSymbolAtLocation(node);
|
||||
if (symbol) {
|
||||
var type = classifySymbol(symbol, getMeaningFromLocation(node));
|
||||
if (type) {
|
||||
|
@ -5397,7 +5397,7 @@ module ts {
|
|||
|
||||
// Can only rename an identifier.
|
||||
if (node && node.kind === SyntaxKind.Identifier) {
|
||||
var symbol = typeInfoResolver.getSymbolInfo(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) {
|
||||
|
|
|
@ -457,7 +457,7 @@ module ts.SignatureHelp {
|
|||
|
||||
var invocation = argumentListInfo.invocation;
|
||||
var callTarget = getInvokedExpression(invocation)
|
||||
var callTargetSymbol = typeInfoResolver.getSymbolInfo(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[];
|
||||
|
|
18
tests/cases/fourslash/memberListOfVarInArrowExpression.ts
Normal file
18
tests/cases/fourslash/memberListOfVarInArrowExpression.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////interface IMap<T> {
|
||||
//// [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<T>(items: T[], handler: (item: T) => void) { }
|
||||
|
||||
goTo.marker('1');
|
||||
verify.quickInfoIs("(property) a1: string");
|
||||
verify.memberListContains("a1", "(property) a1: string");
|
16
tests/cases/fourslash/quickInfoOnVarInArrowExpression.ts
Normal file
16
tests/cases/fourslash/quickInfoOnVarInArrowExpression.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////interface IMap<T> {
|
||||
//// [key: string]: T;
|
||||
////}
|
||||
////var map: IMap<string[]>;
|
||||
////var categories: string[];
|
||||
////each(categories, category => {
|
||||
//// var /*1*/changes = map[category];
|
||||
//// return each(changes, change => {
|
||||
//// });
|
||||
////});
|
||||
////function each<T>(items: T[], handler: (item: T) => void) { }
|
||||
|
||||
goTo.marker('1');
|
||||
verify.quickInfoIs("(local var) changes: string[]", undefined);
|
Loading…
Reference in a new issue