Skip to content

Commit 115141c

Browse files
committed
Also check inheritance for union and intersection types
1 parent 051c7b0 commit 115141c

4 files changed

Lines changed: 80 additions & 67 deletions

File tree

src/services/services.ts

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1844,6 +1844,13 @@ namespace ts {
18441844
owners: string[];
18451845
}
18461846

1847+
// Internal interface used for tracking state in find all references when checking
1848+
// the inheritance hierarchy of property access expressions
1849+
interface SymbolInheritanceState {
1850+
symbol: Symbol;
1851+
cachedInheritanceResults: Map<boolean>;
1852+
}
1853+
18471854
export interface DisplayPartsSymbolWriter extends SymbolWriter {
18481855
displayParts(): SymbolDisplayPart[];
18491856
}
@@ -6546,13 +6553,17 @@ namespace ts {
65466553
// symbol of the local type of the symbol the property is being accessed on. This is because our search
65476554
// symbol may have a different parent symbol if the local type's symbol does not declare the property
65486555
// being accessed (i.e. it is declared in some parent class or interface)
6549-
let parentSymbol: Symbol = undefined;
6550-
let inheritanceCache: Map<boolean> = undefined;
6551-
if (implementations && searchLocation.parent && searchLocation.parent.kind === SyntaxKind.PropertyAccessExpression && searchLocation === (<PropertyAccessExpression>searchLocation.parent).name) {
6556+
let parentSymbols: SymbolInheritanceState[] = undefined;
6557+
6558+
if (implementations && isRightSideOfPropertyAccess(searchLocation)) {
65526559
const localParentType = typeChecker.getTypeAtLocation((<PropertyAccessExpression>searchLocation.parent).expression);
6553-
if (localParentType && localParentType.symbol && localParentType.symbol.getFlags() & (SymbolFlags.Interface | SymbolFlags.Class) && localParentType.symbol.parent !== searchSymbol.parent) {
6554-
parentSymbol = localParentType.symbol;
6555-
inheritanceCache = createMap<boolean>();
6560+
if (localParentType) {
6561+
if (localParentType.symbol && isClassOrInterfaceReference(localParentType.symbol) && localParentType.symbol.parent !== searchSymbol.parent) {
6562+
parentSymbols = [createSymbolInheritanceState(localParentType.symbol)];
6563+
}
6564+
else if (localParentType.getFlags() & TypeFlags.UnionOrIntersection) {
6565+
parentSymbols = map(getSymbolsForComponentTypes(<UnionOrIntersectionType>localParentType), createSymbolInheritanceState);
6566+
}
65566567
}
65576568
}
65586569

@@ -6596,7 +6607,7 @@ namespace ts {
65966607
if (referenceSymbol) {
65976608
const referenceSymbolDeclaration = referenceSymbol.valueDeclaration;
65986609
const shorthandValueSymbol = typeChecker.getShorthandAssignmentValueSymbol(referenceSymbolDeclaration);
6599-
const relatedSymbol = getRelatedSymbol(searchSymbols, referenceSymbol, referenceLocation, parentSymbol, inheritanceCache);
6610+
const relatedSymbol = getRelatedSymbol(searchSymbols, referenceSymbol, referenceLocation, parentSymbols);
66006611

66016612
if (relatedSymbol) {
66026613
const referenceEntry = implementations ? getImplementationReferenceEntryForNode(referenceLocation) : getReferenceEntryFromNode(referenceLocation);
@@ -6691,6 +6702,18 @@ namespace ts {
66916702
}
66926703
}
66936704

6705+
function getSymbolsForComponentTypes(type: UnionOrIntersectionType, result: Symbol[] = []): Symbol[] {
6706+
for (const componentType of type.types) {
6707+
if (componentType.symbol && componentType.symbol.getFlags() & (SymbolFlags.Class | SymbolFlags.Interface)) {
6708+
result.push(componentType.symbol);
6709+
}
6710+
if (componentType.getFlags() & TypeFlags.UnionOrIntersection) {
6711+
getSymbolsForComponentTypes(<UnionOrIntersectionType>componentType, result);
6712+
}
6713+
}
6714+
return result;
6715+
}
6716+
66946717
function getContainingTypeReference(node: Node): Node {
66956718
if (node) {
66966719
if (node.kind === SyntaxKind.TypeReference) {
@@ -6786,7 +6809,7 @@ namespace ts {
67866809
return inherits;
67876810
}
67886811

6789-
function searchTypeReference(typeReference: ExpressionWithTypeArguments, cachedResults: Map<boolean>) {
6812+
function searchTypeReference(typeReference: ExpressionWithTypeArguments, cachedResults: Map<boolean>): boolean {
67906813
if (typeReference) {
67916814
const type = typeChecker.getTypeAtLocation(typeReference);
67926815
if (type && type.symbol) {
@@ -6797,6 +6820,13 @@ namespace ts {
67976820
}
67986821
}
67996822

6823+
function createSymbolInheritanceState(symbol: Symbol): SymbolInheritanceState {
6824+
return {
6825+
symbol,
6826+
cachedInheritanceResults: createMap<boolean>()
6827+
};
6828+
}
6829+
68006830
function getReferencesForSuperKeyword(superKeyword: Node): ReferencedSymbol[] {
68016831
let searchSpaceNode = getSuperContainer(superKeyword, /*stopOnFunctions*/ false);
68026832
if (!searchSpaceNode) {
@@ -7144,7 +7174,7 @@ namespace ts {
71447174
}
71457175
}
71467176

7147-
function getRelatedSymbol(searchSymbols: Symbol[], referenceSymbol: Symbol, referenceLocation: Node, parentSymbol: Symbol, inheritanceCache: Map<boolean>): Symbol {
7177+
function getRelatedSymbol(searchSymbols: Symbol[], referenceSymbol: Symbol, referenceLocation: Node, parentSymbols: SymbolInheritanceState[]): Symbol {
71487178
if (searchSymbols.indexOf(referenceSymbol) >= 0) {
71497179
return referenceSymbol;
71507180
}
@@ -7153,7 +7183,7 @@ namespace ts {
71537183
// symbols but by looking up for related symbol of this alias so it can handle multiple level of indirectness.
71547184
const aliasSymbol = getAliasSymbolForPropertyNameSymbol(referenceSymbol, referenceLocation);
71557185
if (aliasSymbol) {
7156-
return getRelatedSymbol(searchSymbols, aliasSymbol, referenceLocation, parentSymbol, inheritanceCache);
7186+
return getRelatedSymbol(searchSymbols, aliasSymbol, referenceLocation, parentSymbols);
71577187
}
71587188

71597189
// If the reference location is in an object literal, try to get the contextual type for the
@@ -7198,7 +7228,13 @@ namespace ts {
71987228
// Finally, try all properties with the same name in any type the containing type extended or implemented, and
71997229
// see if any is in the list. If we were passed a parent symbol, only include types that are subtypes of the
72007230
// parent symbol
7201-
if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface) && (!parentSymbol || inheritsFrom(rootSymbol.parent, parentSymbol, inheritanceCache))) {
7231+
if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
7232+
if (parentSymbols) {
7233+
if (!forEach(parentSymbols, ({symbol, cachedInheritanceResults}) => inheritsFrom(rootSymbol.parent, symbol, cachedInheritanceResults))) {
7234+
return undefined;
7235+
}
7236+
}
7237+
72027238
const result: Symbol[] = [];
72037239
getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result, /*previousIterationSymbolsCache*/ createMap<Symbol>());
72047240
return forEach(result, s => searchSymbols.indexOf(s) >= 0 ? s : undefined);
Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
/// <reference path='fourslash.ts'/>
22

3-
// Should handle intersection types
3+
// Should handle union and intersection types
44

5-
//// interface Foo {
5+
//// interface BaseFoo {
66
//// hello(): void;
7+
//// }
8+
////
9+
//// interface Foo extends BaseFoo {
710
//// aloha(): void;
811
//// }
912
////
@@ -13,12 +16,16 @@
1316
//// }
1417
////
1518
//// class FooImpl implements Foo {
16-
//// hello() {/**FooImpl*/}
19+
//// [|hello() {/**FooImpl*/}|]
1720
//// aloha() {}
1821
//// }
1922
////
23+
//// class BaseFooImpl implements BaseFoo {
24+
//// hello() {/**BaseFooImpl*/} // Should not show up
25+
//// }
26+
////
2027
//// class BarImpl implements Bar {
21-
//// hello() {/**BarImpl*/}
28+
//// [|hello() {/**BarImpl*/}|]
2229
//// goodbye() {}
2330
//// }
2431
////
@@ -28,9 +35,15 @@
2835
//// goodbye() {}
2936
//// }
3037
////
31-
//// function someFunction(x: Foo & Bar) {
32-
//// x.he/*function_call*/llo();
38+
//// function someFunction(x: Foo | Bar) {
39+
//// x.he/*function_call0*/llo();
40+
//// }
41+
////
42+
//// function anotherFunction(x: Foo & Bar) {
43+
//// x.he/*function_call1*/llo();
3344
//// }
3445

35-
goTo.marker("function_call");
36-
verify.allRangesAppearInImplementationList();
46+
for (var i = 0; i < 2; i++) {
47+
goTo.marker("function_call" + i);
48+
verify.allRangesAppearInImplementationList();
49+
}
Lines changed: 12 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,12 @@
1-
/// <reference path='fourslash.ts'/>
2-
3-
// Should handle union types
4-
5-
//// interface Foo {
6-
//// hello(): void;
7-
//// aloha(): void;
8-
//// }
9-
////
10-
//// interface Bar {
11-
//// hello(): void;
12-
//// goodbye(): void;
13-
//// }
14-
////
15-
//// class FooImpl implements Foo {
16-
//// [|hello() {/**FooImpl*/}|]
17-
//// aloha() {}
18-
//// }
19-
////
20-
//// class BarImpl implements Bar {
21-
//// [|hello() {/**BarImpl*/}|]
22-
//// goodbye() {}
23-
//// }
24-
////
25-
//// class FooAndBarImpl implements Foo, Bar {
26-
//// [|hello() {/**FooAndBarImpl*/}|]
27-
//// aloha() {}
28-
//// goodbye() {}
29-
//// }
30-
////
31-
//// function someFunction(x: Foo | Bar) {
32-
//// x.he/*function_call*/llo();
33-
//// }
34-
35-
goTo.marker("function_call");
36-
verify.allRangesAppearInImplementationList();
1+
/// <reference path='fourslash.ts'/>
2+
3+
// Should handle members of object literals in type assertion expressions
4+
5+
//// interface Foo {
6+
//// hel/*reference*/lo(): void;
7+
//// }
8+
////
9+
//// var x = <Foo> { [|hello: () => {}|] };
10+
//// var y = <Foo> (((({ [|hello: () => {}|] }))));
11+
goTo.marker("reference");
12+
verify.allRangesAppearInImplementationList();

tests/cases/fourslash/goToImplementationInterfaceMethod_12.ts

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)