Skip to content

Commit e3320c2

Browse files
committed
Merge pull request microsoft#1362 from Microsoft/contextSensitiveExpressions
Resolve the context sensitive expression containers before resolving node
2 parents 4d0f992 + c3c44dc commit e3320c2

7 files changed

Lines changed: 96 additions & 24 deletions

File tree

src/compiler/checker.ts

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,17 @@ module ts {
8585
getDiagnostics,
8686
getDeclarationDiagnostics,
8787
getGlobalDiagnostics,
88-
getParentOfSymbol,
89-
getNarrowedTypeOfSymbol,
88+
getTypeOfSymbolAtLocation,
9089
getDeclaredTypeOfSymbol,
9190
getPropertiesOfType,
9291
getPropertyOfType,
9392
getSignaturesOfType,
9493
getIndexTypeOfType,
9594
getReturnTypeOfSignature,
9695
getSymbolsInScope,
97-
getSymbolInfo,
96+
getSymbolAtLocation,
9897
getShorthandAssignmentValueSymbol,
99-
getTypeOfNode,
98+
getTypeAtLocation,
10099
typeToString,
101100
getSymbolDisplayBuilder,
102101
symbolToString,
@@ -4391,6 +4390,46 @@ module ts {
43914390
}
43924391
}
43934392

4393+
function resolveLocation(node: Node) {
4394+
// Resolve location from top down towards node if it is a context sensitive expression
4395+
// That helps in making sure not assigning types as any when resolved out of order
4396+
var containerNodes: Node[] = [];
4397+
for (var parent = node.parent; parent; parent = parent.parent) {
4398+
if (isExpression(parent) && isContextSensitiveExpression(<Expression>parent)) {
4399+
containerNodes.unshift(parent);
4400+
}
4401+
}
4402+
4403+
ts.forEach(containerNodes, node => { getTypeOfNode(node); });
4404+
}
4405+
4406+
function getSymbolAtLocation(node: Node): Symbol {
4407+
resolveLocation(node);
4408+
return getSymbolInfo(node);
4409+
}
4410+
4411+
function getTypeAtLocation(node: Node): Type {
4412+
resolveLocation(node);
4413+
return getTypeOfNode(node);
4414+
}
4415+
4416+
function getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type {
4417+
resolveLocation(node);
4418+
// Get the narrowed type of symbol at given location instead of just getting
4419+
// the type of the symbol.
4420+
// eg.
4421+
// function foo(a: string | number) {
4422+
// if (typeof a === "string") {
4423+
// a/**/
4424+
// }
4425+
// }
4426+
// getTypeOfSymbol for a would return type of parameter symbol string | number
4427+
// Unless we provide location /**/, checker wouldn't know how to narrow the type
4428+
// By using getNarrowedTypeOfSymbol would return string since it would be able to narrow
4429+
// it by typeguard in the if true condition
4430+
return getNarrowedTypeOfSymbol(symbol, node);
4431+
}
4432+
43944433
// Get the narrowed type of a given symbol at a given location
43954434
function getNarrowedTypeOfSymbol(symbol: Symbol, node: Node) {
43964435
var type = getTypeOfSymbol(symbol);

src/compiler/types.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -903,18 +903,17 @@ module ts {
903903
getSymbolCount(): number;
904904
getTypeCount(): number;
905905
emitFiles(targetSourceFile?: SourceFile): EmitResult;
906-
getParentOfSymbol(symbol: Symbol): Symbol;
907-
getNarrowedTypeOfSymbol(symbol: Symbol, node: Node): Type;
906+
getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type;
908907
getDeclaredTypeOfSymbol(symbol: Symbol): Type;
909908
getPropertiesOfType(type: Type): Symbol[];
910909
getPropertyOfType(type: Type, propertyName: string): Symbol;
911910
getSignaturesOfType(type: Type, kind: SignatureKind): Signature[];
912911
getIndexTypeOfType(type: Type, kind: IndexKind): Type;
913912
getReturnTypeOfSignature(signature: Signature): Type;
914913
getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[];
915-
getSymbolInfo(node: Node): Symbol;
914+
getSymbolAtLocation(node: Node): Symbol;
916915
getShorthandAssignmentValueSymbol(location: Node): Symbol;
917-
getTypeOfNode(node: Node): Type;
916+
getTypeAtLocation(node: Node): Type;
918917
typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string;
919918
symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string;
920919
getSymbolDisplayBuilder(): SymbolDisplayBuilder;

src/harness/typeWriter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class TypeWriterWalker {
9494
}
9595

9696
private getTypeOfNode(node: ts.Node): ts.Type {
97-
var type = this.checker.getTypeOfNode(node);
97+
var type = this.checker.getTypeOfLocation(node);
9898
ts.Debug.assert(type !== undefined, "type doesn't exist");
9999
return type;
100100
}

src/services/services.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2433,7 +2433,7 @@ module ts {
24332433
isMemberCompletion = true;
24342434

24352435
if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.PropertyAccessExpression) {
2436-
var symbol = typeInfoResolver.getSymbolInfo(node);
2436+
var symbol = typeInfoResolver.getSymbolAtLocation(node);
24372437

24382438
// This is an alias, follow what it aliases
24392439
if (symbol && symbol.flags & SymbolFlags.Import) {
@@ -2450,7 +2450,7 @@ module ts {
24502450
}
24512451
}
24522452

2453-
var type = typeInfoResolver.getTypeOfNode(node);
2453+
var type = typeInfoResolver.getTypeAtLocation(node);
24542454
if (type) {
24552455
// Filter private properties
24562456
forEach(type.getApparentProperties(), symbol => {
@@ -2702,7 +2702,7 @@ module ts {
27022702
// which is permissible given that it is backwards compatible; but really we should consider
27032703
// passing the meaning for the node so that we don't report that a suggestion for a value is an interface.
27042704
// We COULD also just do what 'getSymbolModifiers' does, which is to use the first declaration.
2705-
Debug.assert(session.typeChecker.getNarrowedTypeOfSymbol(symbol, location) !== undefined, "Could not find type for symbol");
2705+
Debug.assert(session.typeChecker.getTypeOfSymbolAtLocation(symbol, location) !== undefined, "Could not find type for symbol");
27062706
var displayPartsDocumentationsAndSymbolKind = getSymbolDisplayPartsDocumentationAndSymbolKind(symbol, getSourceFile(filename), location, session.typeChecker, location, SemanticMeaning.All);
27072707
return {
27082708
name: entryName,
@@ -2805,7 +2805,7 @@ module ts {
28052805
if (!unionPropertyKind) {
28062806
// If this was union of all methods,
28072807
//make sure it has call signatures before we can label it as method
2808-
var typeOfUnionProperty = typeInfoResolver.getNarrowedTypeOfSymbol(symbol, location);
2808+
var typeOfUnionProperty = typeInfoResolver.getTypeOfSymbolAtLocation(symbol, location);
28092809
if (typeOfUnionProperty.getCallSignatures().length) {
28102810
return ScriptElementKind.memberFunctionElement;
28112811
}
@@ -2882,7 +2882,7 @@ module ts {
28822882
symbolKind = ScriptElementKind.memberVariableElement;
28832883
}
28842884

2885-
var type = typeResolver.getNarrowedTypeOfSymbol(symbol, location);
2885+
var type = typeResolver.getTypeOfSymbolAtLocation(symbol, location);
28862886
if (type) {
28872887
if (location.parent && location.parent.kind === SyntaxKind.PropertyAccessExpression) {
28882888
var right = (<PropertyAccessExpression>location.parent).name;
@@ -3094,7 +3094,7 @@ module ts {
30943094
displayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
30953095
}
30963096
else {
3097-
var internalAliasSymbol = typeResolver.getSymbolInfo(importDeclaration.moduleReference);
3097+
var internalAliasSymbol = typeResolver.getSymbolAtLocation(importDeclaration.moduleReference);
30983098
if (internalAliasSymbol) {
30993099
displayParts.push(spacePart());
31003100
displayParts.push(operatorPart(SyntaxKind.EqualsToken));
@@ -3204,7 +3204,7 @@ module ts {
32043204
return undefined;
32053205
}
32063206

3207-
var symbol = typeInfoResolver.getSymbolInfo(node);
3207+
var symbol = typeInfoResolver.getSymbolAtLocation(node);
32083208
if (!symbol) {
32093209
// Try getting just type at this position and show
32103210
switch (node.kind) {
@@ -3214,7 +3214,7 @@ module ts {
32143214
case SyntaxKind.ThisKeyword:
32153215
case SyntaxKind.SuperKeyword:
32163216
// For the identifiers/this/super etc get the type at position
3217-
var type = typeInfoResolver.getTypeOfNode(node);
3217+
var type = typeInfoResolver.getTypeAtLocation(node);
32183218
if (type) {
32193219
return {
32203220
kind: ScriptElementKind.unknown,
@@ -3331,7 +3331,7 @@ module ts {
33313331
return undefined;
33323332
}
33333333

3334-
var symbol = typeInfoResolver.getSymbolInfo(node);
3334+
var symbol = typeInfoResolver.getSymbolAtLocation(node);
33353335

33363336
// Could not find a symbol e.g. node is string or number keyword,
33373337
// or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol
@@ -3963,7 +3963,7 @@ module ts {
39633963
return getReferencesForSuperKeyword(node);
39643964
}
39653965

3966-
var symbol = typeInfoResolver.getSymbolInfo(node);
3966+
var symbol = typeInfoResolver.getSymbolAtLocation(node);
39673967

39683968
// Could not find a symbol e.g. unknown identifier
39693969
if (!symbol) {
@@ -4215,7 +4215,7 @@ module ts {
42154215
return;
42164216
}
42174217

4218-
var referenceSymbol = typeInfoResolver.getSymbolInfo(referenceLocation);
4218+
var referenceSymbol = typeInfoResolver.getSymbolAtLocation(referenceLocation);
42194219
if (referenceSymbol) {
42204220
var referenceSymbolDeclaration = referenceSymbol.valueDeclaration;
42214221
var shorthandValueSymbol = typeInfoResolver.getShorthandAssignmentValueSymbol(referenceSymbolDeclaration);
@@ -4457,7 +4457,7 @@ module ts {
44574457

44584458
function getPropertySymbolFromTypeReference(typeReference: TypeReferenceNode) {
44594459
if (typeReference) {
4460-
var type = typeInfoResolver.getTypeOfNode(typeReference);
4460+
var type = typeInfoResolver.getTypeAtLocation(typeReference);
44614461
if (type) {
44624462
var propertySymbol = typeInfoResolver.getPropertyOfType(type, propertyName);
44634463
if (propertySymbol) {
@@ -4981,7 +4981,7 @@ module ts {
49814981
// Only walk into nodes that intersect the requested span.
49824982
if (node && span.intersectsWith(node.getStart(), node.getWidth())) {
49834983
if (node.kind === SyntaxKind.Identifier && node.getWidth() > 0) {
4984-
var symbol = typeInfoResolver.getSymbolInfo(node);
4984+
var symbol = typeInfoResolver.getSymbolAtLocation(node);
49854985
if (symbol) {
49864986
var type = classifySymbol(symbol, getMeaningFromLocation(node));
49874987
if (type) {
@@ -5397,7 +5397,7 @@ module ts {
53975397

53985398
// Can only rename an identifier.
53995399
if (node && node.kind === SyntaxKind.Identifier) {
5400-
var symbol = typeInfoResolver.getSymbolInfo(node);
5400+
var symbol = typeInfoResolver.getSymbolAtLocation(node);
54015401

54025402
// Only allow a symbol to be renamed if it actually has at least one declaration.
54035403
if (symbol && symbol.getDeclarations() && symbol.getDeclarations().length > 0) {

src/services/signatureHelp.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ module ts.SignatureHelp {
457457

458458
var invocation = argumentListInfo.invocation;
459459
var callTarget = getInvokedExpression(invocation)
460-
var callTargetSymbol = typeInfoResolver.getSymbolInfo(callTarget);
460+
var callTargetSymbol = typeInfoResolver.getSymbolAtLocation(callTarget);
461461
var callTargetDisplayParts = callTargetSymbol && symbolToDisplayParts(typeInfoResolver, callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined);
462462
var items: SignatureHelpItem[] = map(candidates, candidateSignature => {
463463
var signatureHelpParameters: SignatureHelpParameter[];
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////interface IMap<T> {
4+
//// [key: string]: T;
5+
////}
6+
////var map: IMap<{ a1: string; }[]>;
7+
////var categories: string[];
8+
////each(categories, category => {
9+
//// var changes = map[category];
10+
//// changes[0]./*1*/a1;
11+
//// return each(changes, change => {
12+
//// });
13+
////});
14+
////function each<T>(items: T[], handler: (item: T) => void) { }
15+
16+
goTo.marker('1');
17+
verify.quickInfoIs("(property) a1: string");
18+
verify.memberListContains("a1", "(property) a1: string");
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////interface IMap<T> {
4+
//// [key: string]: T;
5+
////}
6+
////var map: IMap<string[]>;
7+
////var categories: string[];
8+
////each(categories, category => {
9+
//// var /*1*/changes = map[category];
10+
//// return each(changes, change => {
11+
//// });
12+
////});
13+
////function each<T>(items: T[], handler: (item: T) => void) { }
14+
15+
goTo.marker('1');
16+
verify.quickInfoIs("(local var) changes: string[]", undefined);

0 commit comments

Comments
 (0)