Skip to content

Commit 779db6e

Browse files
committed
Support find all refs on union properties
1 parent c439ae4 commit 779db6e

6 files changed

+168
-79
lines changed

src/compiler/checker.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ module ts {
8888
symbolToString: symbolToString,
8989
symbolToDisplayParts: symbolToDisplayParts,
9090
getAugmentedPropertiesOfApparentType: getAugmentedPropertiesOfApparentType,
91-
getRootSymbol: getRootSymbol,
91+
getRootSymbols: getRootSymbols,
9292
getContextualType: getContextualType,
9393
getFullyQualifiedName: getFullyQualifiedName,
9494
getResolvedSignature: getResolvedSignature,
@@ -7863,8 +7863,22 @@ module ts {
78637863
}
78647864
}
78657865

7866-
function getRootSymbol(symbol: Symbol) {
7867-
return ((symbol.flags & SymbolFlags.Transient) && getSymbolLinks(symbol).target) || symbol;
7866+
function getRootSymbols(symbol: Symbol): Symbol[] {
7867+
if (symbol.flags & SymbolFlags.UnionProperty) {
7868+
var symbols: Symbol[] = [];
7869+
var name = symbol.name;
7870+
forEach(getSymbolLinks(symbol).unionType.types, t => {
7871+
symbols.push(getPropertyOfType(t, name));
7872+
});
7873+
return symbols;
7874+
}
7875+
else if (symbol.flags & SymbolFlags.Transient) {
7876+
var target = getSymbolLinks(symbol).target;
7877+
if (target) {
7878+
return [target];
7879+
}
7880+
}
7881+
return [symbol];
78687882
}
78697883

78707884
// Emitter support

src/compiler/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ module ts {
653653
symbolToDisplayParts(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): SymbolDisplayPart[];
654654
getFullyQualifiedName(symbol: Symbol): string;
655655
getAugmentedPropertiesOfApparentType(type: Type): Symbol[];
656-
getRootSymbol(symbol: Symbol): Symbol;
656+
getRootSymbols(symbol: Symbol): Symbol[];
657657
getContextualType(node: Node): Type;
658658
getResolvedSignature(node: CallExpression, candidatesOutArray?: Signature[]): Signature;
659659

src/services/services.ts

Lines changed: 66 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -2284,16 +2284,8 @@ module ts {
22842284
}
22852285
}
22862286

2287-
function getConcreteSymbol(symbol: Symbol): Symbol {
2288-
if (symbol.flags & SymbolFlags.UnionProperty) {
2289-
var types = typeInfoResolver.getUnionTypesOfUnionProperty(symbol);
2290-
symbol = typeInfoResolver.getPropertyOfType(types[0], symbol.name);
2291-
}
2292-
return typeInfoResolver.getRootSymbol(symbol);
2293-
}
2294-
22952287
function getSymbolKind(symbol: Symbol): string {
2296-
var flags = getConcreteSymbol(symbol).getFlags();
2288+
var flags = typeInfoResolver.getRootSymbols(symbol)[0].getFlags();
22972289

22982290
if (flags & SymbolFlags.Module) return ScriptElementKind.moduleElement;
22992291
if (flags & SymbolFlags.Class) return ScriptElementKind.classElement;
@@ -2352,7 +2344,7 @@ module ts {
23522344
}
23532345

23542346
function getSymbolModifiers(symbol: Symbol): string {
2355-
symbol = getConcreteSymbol(symbol);
2347+
symbol = typeInfoResolver.getRootSymbols(symbol)[0];
23562348
return symbol && symbol.declarations && symbol.declarations.length > 0
23572349
? getNodeModifiers(symbol.declarations[0])
23582350
: ScriptElementKindModifier.none;
@@ -3016,18 +3008,28 @@ module ts {
30163008
return [getReferenceEntryFromNode(node)];
30173009
}
30183010

3011+
var declarations = symbol.getDeclarations();
3012+
3013+
// Handel union properties
3014+
if (symbol.flags & SymbolFlags.UnionProperty) {
3015+
declarations = [];
3016+
forEach(typeInfoResolver.getUnionTypesOfUnionProperty(symbol), t => {
3017+
declarations.push.apply(declarations, t.getProperty(symbol.name).declarations);
3018+
});
3019+
}
3020+
30193021
// the symbol was an internal symbol and does not have a declaration e.g.undefined symbol
3020-
if (!symbol.getDeclarations()) {
3022+
if (!declarations || !declarations.length) {
30213023
return undefined;
30223024
}
30233025

30243026
var result: ReferenceEntry[];
30253027

30263028
// Compute the meaning from the location and the symbol it references
3027-
var searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), symbol.getDeclarations());
3029+
var searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), declarations);
30283030

30293031
// Get the text to search for, we need to normalize it as external module names will have quote
3030-
var symbolName = getNormalizedSymbolName(symbol);
3032+
var symbolName = getNormalizedSymbolName(symbol.name, declarations);
30313033

30323034
// Get syntactic diagnostics
30333035
var scope = getSymbolScope(symbol);
@@ -3049,15 +3051,15 @@ module ts {
30493051

30503052
return result;
30513053

3052-
function getNormalizedSymbolName(symbol: Symbol): string {
3054+
function getNormalizedSymbolName(symbolName: string, declarations: Declaration[]): string {
30533055
// Special case for function expressions, whose names are solely local to their bodies.
3054-
var functionExpression = getDeclarationOfKind(symbol, SyntaxKind.FunctionExpression);
3056+
var functionExpression = forEach(declarations, d => d.kind === SyntaxKind.FunctionExpression ? d : undefined);
30553057

30563058
if (functionExpression && functionExpression.name) {
30573059
var name = functionExpression.name.text;
30583060
}
30593061
else {
3060-
var name = symbol.name;
3062+
var name = symbolName;
30613063
}
30623064

30633065
var length = name.length;
@@ -3084,22 +3086,24 @@ module ts {
30843086
var scope: Node = undefined;
30853087

30863088
var declarations = symbol.getDeclarations();
3087-
for (var i = 0, n = declarations.length; i < n; i++) {
3088-
var container = getContainerNode(declarations[i]);
3089+
if (declarations) {
3090+
for (var i = 0, n = declarations.length; i < n; i++) {
3091+
var container = getContainerNode(declarations[i]);
30893092

3090-
if (scope && scope !== container) {
3091-
// Different declarations have different containers, bail out
3092-
return undefined;
3093-
}
3093+
if (scope && scope !== container) {
3094+
// Different declarations have different containers, bail out
3095+
return undefined;
3096+
}
30943097

3095-
if (container.kind === SyntaxKind.SourceFile && !isExternalModule(<SourceFile>container)) {
3096-
// This is a global variable and not an external module, any declaration defined
3097-
// within this scope is visible outside the file
3098-
return undefined;
3099-
}
3098+
if (container.kind === SyntaxKind.SourceFile && !isExternalModule(<SourceFile>container)) {
3099+
// This is a global variable and not an external module, any declaration defined
3100+
// within this scope is visible outside the file
3101+
return undefined;
3102+
}
31003103

3101-
// The search scope is the container node
3102-
scope = container;
3104+
// The search scope is the container node
3105+
scope = container;
3106+
}
31033107
}
31043108

31053109
return scope;
@@ -3216,14 +3220,7 @@ module ts {
32163220
}
32173221

32183222
var referenceSymbol = typeInfoResolver.getSymbolInfo(referenceLocation);
3219-
3220-
// Could not find a symbol e.g. node is string or number keyword,
3221-
// or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol
3222-
if (!referenceSymbol || !(referenceSymbol.getDeclarations())) {
3223-
return;
3224-
}
3225-
3226-
if (isRelatableToSearchSet(searchSymbols, referenceSymbol, referenceLocation)) {
3223+
if (referenceSymbol && isRelatableToSearchSet(searchSymbols, referenceSymbol, referenceLocation)) {
32273224
result.push(getReferenceEntryFromNode(referenceLocation));
32283225
}
32293226
});
@@ -3359,24 +3356,26 @@ module ts {
33593356
// The search set contains at least the current symbol
33603357
var result = [symbol];
33613358

3362-
// If the symbol is an instantiation from a another symbol (e.g. widened symbol) , add the root the list
3363-
var rootSymbol = typeInfoResolver.getRootSymbol(symbol);
3364-
if (rootSymbol && rootSymbol !== symbol) {
3365-
result.push(rootSymbol);
3366-
}
3367-
33683359
// If the location is in a context sensitive location (i.e. in an object literal) try
33693360
// to get a contextual type for it, and add the property symbol from the contextual
33703361
// type to the search set
33713362
if (isNameOfPropertyAssignment(location)) {
33723363
var symbolFromContextualType = getPropertySymbolFromContextualType(location);
3373-
if (symbolFromContextualType) result.push(typeInfoResolver.getRootSymbol(symbolFromContextualType));
3364+
if (symbolFromContextualType) result.push.apply(result, typeInfoResolver.getRootSymbols(symbolFromContextualType));
33743365
}
33753366

3376-
// Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions
3377-
if (symbol.parent && symbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
3378-
getPropertySymbolsFromBaseTypes(symbol.parent, symbol.getName(), result);
3379-
}
3367+
// If this is a union property, add all the symbols from all its source symbols in all unioned types.
3368+
// If the symbol is an instantiation from a another symbol (e.g. widened symbol) , add the root the list
3369+
forEach(typeInfoResolver.getRootSymbols(symbol), rootSymbol => {
3370+
if (rootSymbol !== symbol) {
3371+
result.push(rootSymbol);
3372+
}
3373+
3374+
// Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions
3375+
if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
3376+
getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result);
3377+
}
3378+
});
33803379

33813380
return result;
33823381
}
@@ -3411,33 +3410,34 @@ module ts {
34113410
}
34123411

34133412
function isRelatableToSearchSet(searchSymbols: Symbol[], referenceSymbol: Symbol, referenceLocation: Node): boolean {
3414-
// Unwrap symbols to get to the root (e.g. transient symbols as a result of widening)
3415-
var referenceSymbolTarget = typeInfoResolver.getRootSymbol(referenceSymbol);
3416-
3417-
// if it is in the list, then we are done
3418-
if (searchSymbols.indexOf(referenceSymbolTarget) >= 0) {
3419-
return true;
3420-
}
3421-
34223413
// If the reference location is in an object literal, try to get the contextual type for the
34233414
// object literal, lookup the property symbol in the contextual type, and use this symbol to
34243415
// compare to our searchSymbol
34253416
if (isNameOfPropertyAssignment(referenceLocation)) {
34263417
var symbolFromContextualType = getPropertySymbolFromContextualType(referenceLocation);
3427-
if (symbolFromContextualType && searchSymbols.indexOf(typeInfoResolver.getRootSymbol(symbolFromContextualType)) >= 0) {
3428-
return true;
3418+
if (symbolFromContextualType) {
3419+
return forEach(typeInfoResolver.getRootSymbols(symbolFromContextualType), s => searchSymbols.indexOf(s) >= 0);
34293420
}
34303421
}
34313422

3432-
// Finally, try all properties with the same name in any type the containing type extend or implemented, and
3433-
// see if any is in the list
3434-
if (referenceSymbol.parent && referenceSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
3435-
var result: Symbol[] = [];
3436-
getPropertySymbolsFromBaseTypes(referenceSymbol.parent, referenceSymbol.getName(), result);
3437-
return forEach(result, s => searchSymbols.indexOf(s) >= 0);
3438-
}
3423+
// Unwrap symbols to get to the root (e.g. transient symbols as a result of widening)
3424+
// Or a union property, use its underlying unioned symbols
3425+
return forEach(typeInfoResolver.getRootSymbols(referenceSymbol), rootSymbol => {
3426+
// if it is in the list, then we are done
3427+
if (searchSymbols.indexOf(rootSymbol) >= 0) {
3428+
return true;
3429+
}
34393430

3440-
return false;
3431+
// Finally, try all properties with the same name in any type the containing type extended or implemented, and
3432+
// see if any is in the list
3433+
if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
3434+
var result: Symbol[] = [];
3435+
getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result);
3436+
return forEach(result, s => searchSymbols.indexOf(s) >= 0);
3437+
}
3438+
3439+
return false;
3440+
});
34413441
}
34423442

34433443
function getPropertySymbolFromContextualType(node: Node): Symbol {

tests/cases/fourslash/referencesForContextuallyTypedObjectLiteralProperties.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
/// <reference path='fourslash.ts'/>
2-
1+
/// <reference path='fourslash.ts'/>
2+
33
////interface IFoo { /*1*/xy: number; }
44
////
55
////// Assignment
@@ -23,10 +23,10 @@
2323
////var w: IFoo = { /*4*/xy: undefined };
2424
////
2525
////// Untped -- should not be included
26-
////var u = { xy: 0 };
27-
28-
29-
test.markers().forEach((m) => {
30-
goTo.position(m.position, m.fileName);
31-
verify.referencesCountIs(9);
32-
});
26+
////var u = { xy: 0 };
27+
28+
29+
test.markers().forEach((m) => {
30+
goTo.position(m.position, m.fileName);
31+
verify.referencesCountIs(9);
32+
});
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
////interface A {
4+
//// a: number;
5+
//// common: string;
6+
////}
7+
////
8+
////interface B {
9+
//// b: number;
10+
//// common: number;
11+
////}
12+
////
13+
////// Assignment
14+
////var v1: A | B = { a: 0, /*1*/common: "" };
15+
////var v2: A | B = { b: 0, /*2*/common: 3 };
16+
////
17+
////// Function call
18+
////function consumer(f: A | B) { }
19+
////consumer({ a: 0, b: 0, /*3*/common: 1 });
20+
////
21+
////// Type cast
22+
////var c = <A | B> { /*4*/common: 0, b: 0 };
23+
////
24+
////// Array literal
25+
////var ar: Array<A|B> = [{ a: 0, /*5*/common: "" }, { b: 0, /*6*/common: 0 }];
26+
////
27+
////// Nested object literal
28+
////var ob: { aorb: A|B } = { aorb: { b: 0, /*7*/common: 0 } };
29+
////
30+
////// Widened type
31+
////var w: A|B = { a:0, /*8*/common: undefined };
32+
////
33+
////// Untped -- should not be included
34+
////var u1 = { a: 0, b: 0, common: "" };
35+
////var u2 = { b: 0, common: 0 };
36+
37+
test.markers().forEach((m) => {
38+
goTo.position(m.position, m.fileName);
39+
verify.referencesCountIs(10); // 8 contextually typed common, and 2 in definition (A.common, B.common)
40+
});
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
////interface One {
4+
//// common: { /*1*/a: number; };
5+
////}
6+
////
7+
////interface Base {
8+
//// /*2*/a: string;
9+
//// b: string;
10+
////}
11+
////
12+
////interface HasAOrB extends Base {
13+
//// /*3*/a: string;
14+
//// b: string;
15+
////}
16+
////
17+
////interface Two {
18+
//// common: HasAOrB;
19+
////}
20+
////
21+
////var x : One | Two;
22+
////
23+
////x.common./*4*/a;
24+
25+
goTo.marker("1");
26+
verify.referencesCountIs(2); // One.common.a, x.common.a
27+
28+
goTo.marker("2");
29+
verify.referencesCountIs(3); // Base.a, HasAOrB.a, x.common.a
30+
31+
goTo.marker("3");
32+
verify.referencesCountIs(3); // Base.a, HasAOrB.a, x.common.a
33+
34+
goTo.marker("4");
35+
verify.referencesCountIs(4); // One.common.a, Base.a, HasAOrB.a, x.common.a

0 commit comments

Comments
 (0)