From f8122977a7cbd81ede96e249f2fd39435181ed8e Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Thu, 14 Aug 2014 13:38:58 -0700 Subject: [PATCH] include inherited properties from base classes and interfaces in getReference results --- src/services/services.ts | 61 ++++++++++++++++--- .../fourslash/referencesForExportedValues.ts | 16 +++++ .../referencesForInheritedProperties.ts | 27 ++++++++ .../referencesForInheritedProperties2.ts | 32 ++++++++++ .../cases/fourslash/referencesForOverrides.ts | 6 +- 5 files changed, 129 insertions(+), 13 deletions(-) create mode 100644 tests/cases/fourslash/referencesForExportedValues.ts create mode 100644 tests/cases/fourslash/referencesForInheritedProperties.ts create mode 100644 tests/cases/fourslash/referencesForInheritedProperties2.ts diff --git a/src/services/services.ts b/src/services/services.ts index b2980bd3ec1..bb14501048f 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2322,14 +2322,12 @@ module ts { var possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchSymbolName, container.getStart(), container.getEnd()); if (possiblePositions && possiblePositions.length > 0) { // Build the set of symbols to search for, initially it has only the current symbol - var searchSymbols = popluateSearchSymbolSet(searchSymbol, searchLocation); + var searchSymbols = populateSearchSymbolSet(searchSymbol, searchLocation); possiblePositions.forEach(position => { cancellationToken.throwIfCancellationRequested(); - // Each position we're searching for should be at the start of an identifier. var referenceLocation = getNodeAtPosition(sourceFile, position); - if (!isValidReferencePosition(referenceLocation, searchSymbol)) { return; } @@ -2342,14 +2340,14 @@ module ts { return; } - if (compareSymbolsForLexicalIdentity(searchSymbols, referenceSymbol, referenceLocation)) { + if (isRelatableToSearchSet(searchSymbols, referenceSymbol, referenceLocation)) { result.push(getReferenceEntry(referenceLocation)); } }); } } - function popluateSearchSymbolSet(symbol: Symbol, location: Node): Symbol[]{ + function populateSearchSymbolSet(symbol: Symbol, location: Node): Symbol[]{ // The search set contains at least the current symbol var result = [symbol]; @@ -2363,17 +2361,52 @@ module ts { // to get a contextual type for it, and add the property symbol from the contextual // type to the search set if (isNameOfPropertyAssignment(location)) { - var symbolFromContextualType = getSymbolFromContextualType(location); + var symbolFromContextualType = getPropertySymbolFromContextualType(location); if (symbolFromContextualType) result.push(symbolFromContextualType); } - // TODO: add base class and interface definitions and overwritten properties/methods + // Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions + if (symbol.parent && symbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { + getPropertySymbolsFromBaseTypes(symbol.parent, symbol.getName(), result); + } return result; } - function compareSymbolsForLexicalIdentity(searchSymbols: Symbol[], referenceSymbol: Symbol, referenceLocation: Node): boolean { + function getPropertySymbolsFromBaseTypes(symbol: Symbol, propertyName: string, result: Symbol[]): void { + if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { + forEach(symbol.getDeclarations(), declaration => { + if (declaration.kind === SyntaxKind.ClassDeclaration) { + getPropertySymbolFromTypeReference((declaration).baseType); + forEach((declaration).implementedTypes, getPropertySymbolFromTypeReference); + } + else if (declaration.kind === SyntaxKind.InterfaceDeclaration) { + forEach((declaration).baseTypes, getPropertySymbolFromTypeReference); + } + }); + } + return; + + function getPropertySymbolFromTypeReference(typeReference: TypeReferenceNode) { + if (typeReference) { + // TODO: move to getTypeOfNode instead + var typeReferenceSymbol = typeChecker.getSymbolInfo(typeReference.typeName); + if (typeReferenceSymbol) { + var propertySymbol = typeReferenceSymbol.members[propertyName]; + if (propertySymbol) result.push(typeReferenceSymbol.members[propertyName]); + + // Visit the typeReference as well to see if it directelly or indirectelly use that property + getPropertySymbolsFromBaseTypes(typeReferenceSymbol, propertyName, result); + } + } + } + } + + function isRelatableToSearchSet(searchSymbols: Symbol[], referenceSymbol: Symbol, referenceLocation: Node): boolean { + // Unwrap symbols to get to the root (e.g. triansient symbols as a result of widenning) var referenceSymbolTarget = typeChecker.getRootSymbol(referenceSymbol); + + // if it is in the list, then we are done if (searchSymbols.indexOf(referenceSymbolTarget) >= 0) { return true; } @@ -2382,16 +2415,24 @@ module ts { // object literal, lookup the property symbol in the contextual type, and use this symbol to // compare to our searchSymbol if (isNameOfPropertyAssignment(referenceLocation)) { - var symbolFromContextualType = getSymbolFromContextualType(referenceLocation); + var symbolFromContextualType = getPropertySymbolFromContextualType(referenceLocation); if (searchSymbols.indexOf(symbolFromContextualType) >= 0) { return true; } } + // Finally, try all properties with the same name in any type the containing type extened or implemented, and + // see if any is in the list + if (referenceSymbol.parent && referenceSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { + var result: Symbol[] = []; + getPropertySymbolsFromBaseTypes(referenceSymbol.parent, referenceSymbol.getName(), result); + return forEach(result, s => searchSymbols.indexOf(s) >= 0); + } + return false; } - function getSymbolFromContextualType(node: Node): Symbol { + function getPropertySymbolFromContextualType(node: Node): Symbol { if (isNameOfPropertyAssignment(node)) { var objectLiteral = node.parent.parent; var contextualType = typeChecker.getContextualType(objectLiteral); diff --git a/tests/cases/fourslash/referencesForExportedValues.ts b/tests/cases/fourslash/referencesForExportedValues.ts new file mode 100644 index 00000000000..dc7c52174af --- /dev/null +++ b/tests/cases/fourslash/referencesForExportedValues.ts @@ -0,0 +1,16 @@ +/// + +////module M { +//// export var /*1*/variable = 0; +//// +//// // local use +//// var x = /*2*/variable; +////} +//// +////// external use +////M./*3*/variable + +test.markers().forEach((m) => { + goTo.position(m.position, m.fileName); + verify.referencesCountIs(3); +}); \ No newline at end of file diff --git a/tests/cases/fourslash/referencesForInheritedProperties.ts b/tests/cases/fourslash/referencesForInheritedProperties.ts new file mode 100644 index 00000000000..a11d86ef5e1 --- /dev/null +++ b/tests/cases/fourslash/referencesForInheritedProperties.ts @@ -0,0 +1,27 @@ +/// + +////interface interface1 { +//// /*1*/doStuff(): void; +////} +//// +////interface interface2 extends interface1{ +//// /*2*/doStuff(): void; +////} +//// +////class class1 implements interface2 { +//// /*3*/doStuff() { +//// +//// } +////} +//// +////class class2 extends class1 { +//// +////} +//// +////var v: class2; +////v./*4*/doStuff(); + +test.markers().forEach(m=> { + goTo.position(m.position, m.fileName); + verify.referencesCountIs(4); +}); diff --git a/tests/cases/fourslash/referencesForInheritedProperties2.ts b/tests/cases/fourslash/referencesForInheritedProperties2.ts new file mode 100644 index 00000000000..2203b005284 --- /dev/null +++ b/tests/cases/fourslash/referencesForInheritedProperties2.ts @@ -0,0 +1,32 @@ +/// + +// extends statement in a diffrent declaration + +////interface interface1 { +//// /*1*/doStuff(): void; +////} +//// +////interface interface2 { +//// /*2*/doStuff(): void; +////} +//// +////interface interface2 extends interface1 { +////} +//// +////class class1 implements interface2 { +//// /*3*/doStuff() { +//// +//// } +////} +//// +////class class2 extends class1 { +//// +////} +//// +////var v: class2; +////v./*4*/doStuff(); + +test.markers().forEach(m=> { + goTo.position(m.position, m.fileName); + verify.referencesCountIs(4); +}); diff --git a/tests/cases/fourslash/referencesForOverrides.ts b/tests/cases/fourslash/referencesForOverrides.ts index 16e69be01ac..df3a6f208a0 100644 --- a/tests/cases/fourslash/referencesForOverrides.ts +++ b/tests/cases/fourslash/referencesForOverrides.ts @@ -73,12 +73,12 @@ // References to a field declared in a base class. goTo.marker("1"); -verify.referencesCountIs(2); +verify.referencesCountIs(3); // References to a field declared in a base interface. goTo.marker("2"); -verify.referencesCountIs(2); +verify.referencesCountIs(3); // References to a field declared in a chain of base class and interfaces. goTo.marker("3"); -verify.referencesCountIs(2); +verify.referencesCountIs(6);