Also check inheritance for union and intersection types

This commit is contained in:
Richard Knoll 2016-08-29 18:09:16 -07:00
parent 051c7b0217
commit 115141cb50
4 changed files with 80 additions and 67 deletions

View File

@ -1844,6 +1844,13 @@ namespace ts {
owners: string[];
}
// Internal interface used for tracking state in find all references when checking
// the inheritance hierarchy of property access expressions
interface SymbolInheritanceState {
symbol: Symbol;
cachedInheritanceResults: Map<boolean>;
}
export interface DisplayPartsSymbolWriter extends SymbolWriter {
displayParts(): SymbolDisplayPart[];
}
@ -6546,13 +6553,17 @@ namespace ts {
// symbol of the local type of the symbol the property is being accessed on. This is because our search
// symbol may have a different parent symbol if the local type's symbol does not declare the property
// being accessed (i.e. it is declared in some parent class or interface)
let parentSymbol: Symbol = undefined;
let inheritanceCache: Map<boolean> = undefined;
if (implementations && searchLocation.parent && searchLocation.parent.kind === SyntaxKind.PropertyAccessExpression && searchLocation === (<PropertyAccessExpression>searchLocation.parent).name) {
let parentSymbols: SymbolInheritanceState[] = undefined;
if (implementations && isRightSideOfPropertyAccess(searchLocation)) {
const localParentType = typeChecker.getTypeAtLocation((<PropertyAccessExpression>searchLocation.parent).expression);
if (localParentType && localParentType.symbol && localParentType.symbol.getFlags() & (SymbolFlags.Interface | SymbolFlags.Class) && localParentType.symbol.parent !== searchSymbol.parent) {
parentSymbol = localParentType.symbol;
inheritanceCache = createMap<boolean>();
if (localParentType) {
if (localParentType.symbol && isClassOrInterfaceReference(localParentType.symbol) && localParentType.symbol.parent !== searchSymbol.parent) {
parentSymbols = [createSymbolInheritanceState(localParentType.symbol)];
}
else if (localParentType.getFlags() & TypeFlags.UnionOrIntersection) {
parentSymbols = map(getSymbolsForComponentTypes(<UnionOrIntersectionType>localParentType), createSymbolInheritanceState);
}
}
}
@ -6596,7 +6607,7 @@ namespace ts {
if (referenceSymbol) {
const referenceSymbolDeclaration = referenceSymbol.valueDeclaration;
const shorthandValueSymbol = typeChecker.getShorthandAssignmentValueSymbol(referenceSymbolDeclaration);
const relatedSymbol = getRelatedSymbol(searchSymbols, referenceSymbol, referenceLocation, parentSymbol, inheritanceCache);
const relatedSymbol = getRelatedSymbol(searchSymbols, referenceSymbol, referenceLocation, parentSymbols);
if (relatedSymbol) {
const referenceEntry = implementations ? getImplementationReferenceEntryForNode(referenceLocation) : getReferenceEntryFromNode(referenceLocation);
@ -6691,6 +6702,18 @@ namespace ts {
}
}
function getSymbolsForComponentTypes(type: UnionOrIntersectionType, result: Symbol[] = []): Symbol[] {
for (const componentType of type.types) {
if (componentType.symbol && componentType.symbol.getFlags() & (SymbolFlags.Class | SymbolFlags.Interface)) {
result.push(componentType.symbol);
}
if (componentType.getFlags() & TypeFlags.UnionOrIntersection) {
getSymbolsForComponentTypes(<UnionOrIntersectionType>componentType, result);
}
}
return result;
}
function getContainingTypeReference(node: Node): Node {
if (node) {
if (node.kind === SyntaxKind.TypeReference) {
@ -6786,7 +6809,7 @@ namespace ts {
return inherits;
}
function searchTypeReference(typeReference: ExpressionWithTypeArguments, cachedResults: Map<boolean>) {
function searchTypeReference(typeReference: ExpressionWithTypeArguments, cachedResults: Map<boolean>): boolean {
if (typeReference) {
const type = typeChecker.getTypeAtLocation(typeReference);
if (type && type.symbol) {
@ -6797,6 +6820,13 @@ namespace ts {
}
}
function createSymbolInheritanceState(symbol: Symbol): SymbolInheritanceState {
return {
symbol,
cachedInheritanceResults: createMap<boolean>()
};
}
function getReferencesForSuperKeyword(superKeyword: Node): ReferencedSymbol[] {
let searchSpaceNode = getSuperContainer(superKeyword, /*stopOnFunctions*/ false);
if (!searchSpaceNode) {
@ -7144,7 +7174,7 @@ namespace ts {
}
}
function getRelatedSymbol(searchSymbols: Symbol[], referenceSymbol: Symbol, referenceLocation: Node, parentSymbol: Symbol, inheritanceCache: Map<boolean>): Symbol {
function getRelatedSymbol(searchSymbols: Symbol[], referenceSymbol: Symbol, referenceLocation: Node, parentSymbols: SymbolInheritanceState[]): Symbol {
if (searchSymbols.indexOf(referenceSymbol) >= 0) {
return referenceSymbol;
}
@ -7153,7 +7183,7 @@ namespace ts {
// symbols but by looking up for related symbol of this alias so it can handle multiple level of indirectness.
const aliasSymbol = getAliasSymbolForPropertyNameSymbol(referenceSymbol, referenceLocation);
if (aliasSymbol) {
return getRelatedSymbol(searchSymbols, aliasSymbol, referenceLocation, parentSymbol, inheritanceCache);
return getRelatedSymbol(searchSymbols, aliasSymbol, referenceLocation, parentSymbols);
}
// If the reference location is in an object literal, try to get the contextual type for the
@ -7198,7 +7228,13 @@ namespace ts {
// Finally, try all properties with the same name in any type the containing type extended or implemented, and
// see if any is in the list. If we were passed a parent symbol, only include types that are subtypes of the
// parent symbol
if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface) && (!parentSymbol || inheritsFrom(rootSymbol.parent, parentSymbol, inheritanceCache))) {
if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
if (parentSymbols) {
if (!forEach(parentSymbols, ({symbol, cachedInheritanceResults}) => inheritsFrom(rootSymbol.parent, symbol, cachedInheritanceResults))) {
return undefined;
}
}
const result: Symbol[] = [];
getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result, /*previousIterationSymbolsCache*/ createMap<Symbol>());
return forEach(result, s => searchSymbols.indexOf(s) >= 0 ? s : undefined);

View File

@ -1,9 +1,12 @@
/// <reference path='fourslash.ts'/>
// Should handle intersection types
// Should handle union and intersection types
//// interface Foo {
//// interface BaseFoo {
//// hello(): void;
//// }
////
//// interface Foo extends BaseFoo {
//// aloha(): void;
//// }
////
@ -13,12 +16,16 @@
//// }
////
//// class FooImpl implements Foo {
//// hello() {/**FooImpl*/}
//// [|hello() {/**FooImpl*/}|]
//// aloha() {}
//// }
////
//// class BaseFooImpl implements BaseFoo {
//// hello() {/**BaseFooImpl*/} // Should not show up
//// }
////
//// class BarImpl implements Bar {
//// hello() {/**BarImpl*/}
//// [|hello() {/**BarImpl*/}|]
//// goodbye() {}
//// }
////
@ -28,9 +35,15 @@
//// goodbye() {}
//// }
////
//// function someFunction(x: Foo & Bar) {
//// x.he/*function_call*/llo();
//// function someFunction(x: Foo | Bar) {
//// x.he/*function_call0*/llo();
//// }
////
//// function anotherFunction(x: Foo & Bar) {
//// x.he/*function_call1*/llo();
//// }
goTo.marker("function_call");
verify.allRangesAppearInImplementationList();
for (var i = 0; i < 2; i++) {
goTo.marker("function_call" + i);
verify.allRangesAppearInImplementationList();
}

View File

@ -1,36 +1,12 @@
/// <reference path='fourslash.ts'/>
// Should handle union types
//// interface Foo {
//// hello(): void;
//// aloha(): void;
//// }
////
//// interface Bar {
//// hello(): void;
//// goodbye(): void;
//// }
////
//// class FooImpl implements Foo {
//// [|hello() {/**FooImpl*/}|]
//// aloha() {}
//// }
////
//// class BarImpl implements Bar {
//// [|hello() {/**BarImpl*/}|]
//// goodbye() {}
//// }
////
//// class FooAndBarImpl implements Foo, Bar {
//// [|hello() {/**FooAndBarImpl*/}|]
//// aloha() {}
//// goodbye() {}
//// }
////
//// function someFunction(x: Foo | Bar) {
//// x.he/*function_call*/llo();
//// }
goTo.marker("function_call");
verify.allRangesAppearInImplementationList();
/// <reference path='fourslash.ts'/>
// Should handle members of object literals in type assertion expressions
//// interface Foo {
//// hel/*reference*/lo(): void;
//// }
////
//// var x = <Foo> { [|hello: () => {}|] };
//// var y = <Foo> (((({ [|hello: () => {}|] }))));
goTo.marker("reference");
verify.allRangesAppearInImplementationList();

View File

@ -1,12 +0,0 @@
/// <reference path='fourslash.ts'/>
// Should handle members of object literals in type assertion expressions
//// interface Foo {
//// hel/*reference*/lo(): void;
//// }
////
//// var x = <Foo> { [|hello: () => {}|] };
//// var y = <Foo> (((({ [|hello: () => {}|] }))));
goTo.marker("reference");
verify.allRangesAppearInImplementationList();