Merge pull request #632 from Microsoft/getOccurrencesSuperThanksForAsking
Get occurrences for 'super' keywords
This commit is contained in:
commit
e1f8aa7b8f
6 changed files with 250 additions and 34 deletions
|
@ -411,7 +411,7 @@ module ts {
|
|||
while (true) {
|
||||
node = node.parent;
|
||||
if (!node) {
|
||||
return node;
|
||||
return undefined;
|
||||
}
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ArrowFunction:
|
||||
|
@ -434,6 +434,23 @@ module ts {
|
|||
}
|
||||
}
|
||||
|
||||
export function getSuperContainer(node: Node): Node {
|
||||
while (true) {
|
||||
node = node.parent;
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Property:
|
||||
case SyntaxKind.Method:
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function hasRestParameters(s: SignatureDeclaration): boolean {
|
||||
return s.parameters.length > 0 && (s.parameters[s.parameters.length - 1].flags & NodeFlags.Rest) !== 0;
|
||||
}
|
||||
|
|
|
@ -2134,7 +2134,6 @@ module ts {
|
|||
return result;
|
||||
}
|
||||
|
||||
/// Find references
|
||||
function getOccurrencesAtPosition(filename: string, position: number): ReferenceEntry[] {
|
||||
synchronizeHostData();
|
||||
|
||||
|
@ -2146,7 +2145,7 @@ module ts {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword ||
|
||||
if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword || node.kind === SyntaxKind.SuperKeyword ||
|
||||
isLiteralNameOfPropertyDeclarationOrIndexAccess(node) || isNameOfExternalModuleImportOrDeclaration(node)) {
|
||||
return getReferencesForNode(node, [sourceFile]);
|
||||
}
|
||||
|
@ -2378,7 +2377,9 @@ module ts {
|
|||
}
|
||||
|
||||
if (node.kind !== SyntaxKind.Identifier &&
|
||||
node.kind !== SyntaxKind.ThisKeyword &&
|
||||
// TODO (drosen): This should be enabled in a later release - currently breaks rename.
|
||||
//node.kind !== SyntaxKind.ThisKeyword &&
|
||||
//node.kind !== SyntaxKind.SuperKeyword &&
|
||||
!isLiteralNameOfPropertyDeclarationOrIndexAccess(node) &&
|
||||
!isNameOfExternalModuleImportOrDeclaration(node)) {
|
||||
return undefined;
|
||||
|
@ -2406,6 +2407,10 @@ module ts {
|
|||
return getReferencesForThisKeyword(node, sourceFiles);
|
||||
}
|
||||
|
||||
if (node.kind === SyntaxKind.SuperKeyword) {
|
||||
return getReferencesForSuperKeyword(node);
|
||||
}
|
||||
|
||||
var symbol = typeInfoResolver.getSymbolInfo(node);
|
||||
|
||||
// Could not find a symbol e.g. unknown identifier
|
||||
|
@ -2627,32 +2632,75 @@ module ts {
|
|||
}
|
||||
}
|
||||
|
||||
function getReferencesForThisKeyword(thisKeyword: Node, sourceFiles: SourceFile[]) {
|
||||
// Get the owner" of the 'this' keyword.
|
||||
var thisContainer = getThisContainer(thisKeyword, /* includeArrowFunctions */ false);
|
||||
|
||||
var searchSpaceNode: Node;
|
||||
|
||||
// Whether 'this' occurs in a static context within a class;
|
||||
function getReferencesForSuperKeyword(superKeyword: Node): ReferenceEntry[]{
|
||||
var searchSpaceNode = getSuperContainer(superKeyword);
|
||||
if (!searchSpaceNode) {
|
||||
return undefined;
|
||||
}
|
||||
// Whether 'super' occurs in a static context within a class.
|
||||
var staticFlag = NodeFlags.Static;
|
||||
|
||||
switch (thisContainer.kind) {
|
||||
switch (searchSpaceNode.kind) {
|
||||
case SyntaxKind.Property:
|
||||
case SyntaxKind.Method:
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
searchSpaceNode = thisContainer.parent; // should be the owning class
|
||||
staticFlag &= thisContainer.flags
|
||||
staticFlag &= searchSpaceNode.flags;
|
||||
searchSpaceNode = searchSpaceNode.parent; // re-assign to be the owning class
|
||||
break;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var result: ReferenceEntry[] = [];
|
||||
|
||||
var sourceFile = searchSpaceNode.getSourceFile();
|
||||
var possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "super", searchSpaceNode.getStart(), searchSpaceNode.getEnd());
|
||||
forEach(possiblePositions, position => {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
var node = getNodeAtPosition(sourceFile, position);
|
||||
|
||||
if (!node || node.kind !== SyntaxKind.SuperKeyword) {
|
||||
return;
|
||||
}
|
||||
|
||||
var container = getSuperContainer(node);
|
||||
|
||||
// If we have a 'super' container, we must have an enclosing class.
|
||||
// Now make sure the owning class is the same as the search-space
|
||||
// and has the same static qualifier as the original 'super's owner.
|
||||
if (container && (NodeFlags.Static & container.flags) === staticFlag && container.parent.symbol === searchSpaceNode.symbol) {
|
||||
result.push(getReferenceEntryFromNode(node));
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getReferencesForThisKeyword(thisOrSuperKeyword: Node, sourceFiles: SourceFile[]): ReferenceEntry[] {
|
||||
var searchSpaceNode = getThisContainer(thisOrSuperKeyword, /* includeArrowFunctions */ false);
|
||||
|
||||
// Whether 'this' occurs in a static context within a class.
|
||||
var staticFlag = NodeFlags.Static;
|
||||
|
||||
switch (searchSpaceNode.kind) {
|
||||
case SyntaxKind.Property:
|
||||
case SyntaxKind.Method:
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
staticFlag &= searchSpaceNode.flags
|
||||
searchSpaceNode = searchSpaceNode.parent; // re-assign to be the owning class
|
||||
break;
|
||||
case SyntaxKind.SourceFile:
|
||||
if (isExternalModule(<SourceFile>thisContainer)) {
|
||||
if (isExternalModule(<SourceFile>searchSpaceNode)) {
|
||||
return undefined;
|
||||
}
|
||||
// Fall through
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
searchSpaceNode = thisContainer;
|
||||
break;
|
||||
default:
|
||||
return undefined;
|
||||
|
@ -2683,31 +2731,24 @@ module ts {
|
|||
return;
|
||||
}
|
||||
|
||||
// Get the owner of the 'this' keyword.
|
||||
// This *should* be a node that occurs somewhere within searchSpaceNode.
|
||||
var container = getThisContainer(node, /* includeArrowFunctions */ false);
|
||||
|
||||
switch (container.kind) {
|
||||
case SyntaxKind.Property:
|
||||
case SyntaxKind.Method:
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
// Make sure the container belongs to the same class
|
||||
// and has the appropriate static modifier from the original container.
|
||||
if (searchSpaceNode.symbol === container.parent.symbol && (container.flags & NodeFlags.Static) === staticFlag) {
|
||||
result.push(getReferenceEntryFromNode(node));
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
switch (searchSpaceNode.kind) {
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
if (searchSpaceNode.symbol === container.symbol) {
|
||||
result.push(getReferenceEntryFromNode(node));
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
// Make sure the container belongs to the same class
|
||||
// and has the appropriate static modifier from the original container.
|
||||
if (container.parent && searchSpaceNode.symbol === container.parent.symbol && (container.flags & NodeFlags.Static) === staticFlag) {
|
||||
result.push(getReferenceEntryFromNode(node));
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.SourceFile:
|
||||
// Add all 'this' keywords that belong to the top-level scope.
|
||||
if (searchSpaceNode.kind === SyntaxKind.SourceFile && !isExternalModule(<SourceFile>searchSpaceNode)) {
|
||||
if (container.kind === SyntaxKind.SourceFile && !isExternalModule(<SourceFile>container)) {
|
||||
result.push(getReferenceEntryFromNode(node));
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -12,4 +12,7 @@
|
|||
|
||||
goTo.file("file1.ts");
|
||||
goTo.marker();
|
||||
verify.referencesCountIs(8);
|
||||
|
||||
// TODO (drosen): The CURRENT behavior is that findAllRefs doesn't work on 'this' or 'super' keywords.
|
||||
// This should change down the line.
|
||||
verify.referencesCountIs(0);
|
64
tests/cases/fourslash/getOccurrencesSuper.ts
Normal file
64
tests/cases/fourslash/getOccurrencesSuper.ts
Normal file
|
@ -0,0 +1,64 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////class SuperType {
|
||||
//// superMethod() {
|
||||
//// }
|
||||
////
|
||||
//// static superStaticMethod() {
|
||||
//// return 10;
|
||||
//// }
|
||||
////}
|
||||
////
|
||||
////class SubType extends SuperType {
|
||||
//// public prop1 = [|s/**/uper|].superMethod;
|
||||
//// private prop2 = [|super|].superMethod;
|
||||
////
|
||||
//// constructor() {
|
||||
//// [|super|]();
|
||||
//// }
|
||||
////
|
||||
//// public method1() {
|
||||
//// return [|super|].superMethod();
|
||||
//// }
|
||||
////
|
||||
//// private method2() {
|
||||
//// return [|super|].superMethod();
|
||||
//// }
|
||||
////
|
||||
//// public method3() {
|
||||
//// var x = () => [|super|].superMethod();
|
||||
////
|
||||
//// // Bad but still gets highlighted
|
||||
//// function f() {
|
||||
//// [|super|].superMethod();
|
||||
//// }
|
||||
//// }
|
||||
////
|
||||
//// // Bad but still gets highlighted.
|
||||
//// public static statProp1 = super.superStaticMethod;
|
||||
////
|
||||
//// public static staticMethod1() {
|
||||
//// return super.superStaticMethod();
|
||||
//// }
|
||||
////
|
||||
//// private static staticMethod2() {
|
||||
//// return super.superStaticMethod();
|
||||
//// }
|
||||
////
|
||||
//// // Are not actually 'super' keywords.
|
||||
//// super = 10;
|
||||
//// static super = 20;
|
||||
////}
|
||||
|
||||
test.ranges().forEach(r => {
|
||||
goTo.position(r.start);
|
||||
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
||||
});
|
||||
|
||||
goTo.marker();
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
64
tests/cases/fourslash/getOccurrencesSuper2.ts
Normal file
64
tests/cases/fourslash/getOccurrencesSuper2.ts
Normal file
|
@ -0,0 +1,64 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////class SuperType {
|
||||
//// superMethod() {
|
||||
//// }
|
||||
////
|
||||
//// static superStaticMethod() {
|
||||
//// return 10;
|
||||
//// }
|
||||
////}
|
||||
////
|
||||
////class SubType extends SuperType {
|
||||
//// public prop1 = super.superMethod;
|
||||
//// private prop2 = super.superMethod;
|
||||
////
|
||||
//// constructor() {
|
||||
//// super();
|
||||
//// }
|
||||
////
|
||||
//// public method1() {
|
||||
//// return super.superMethod();
|
||||
//// }
|
||||
////
|
||||
//// private method2() {
|
||||
//// return super.superMethod();
|
||||
//// }
|
||||
////
|
||||
//// public method3() {
|
||||
//// var x = () => super.superMethod();
|
||||
////
|
||||
//// // Bad but still gets highlighted
|
||||
//// function f() {
|
||||
//// super.superMethod();
|
||||
//// }
|
||||
//// }
|
||||
////
|
||||
//// // Bad but still gets highlighted.
|
||||
//// public static statProp1 = [|super|].superStaticMethod;
|
||||
////
|
||||
//// public static staticMethod1() {
|
||||
//// return [|super|].superStaticMethod();
|
||||
//// }
|
||||
////
|
||||
//// private static staticMethod2() {
|
||||
//// return [|supe/**/r|].superStaticMethod();
|
||||
//// }
|
||||
////
|
||||
//// // Are not actually 'super' keywords.
|
||||
//// super = 10;
|
||||
//// static super = 20;
|
||||
////}
|
||||
|
||||
test.ranges().forEach(r => {
|
||||
goTo.position(r.start);
|
||||
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
||||
});
|
||||
|
||||
goTo.marker();
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
27
tests/cases/fourslash/getOccurrencesSuperNegatives.ts
Normal file
27
tests/cases/fourslash/getOccurrencesSuperNegatives.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////function f(x = [|super|]) {
|
||||
//// [|super|];
|
||||
////}
|
||||
////
|
||||
////module M {
|
||||
//// [|super|];
|
||||
//// function f(x = [|super|]) {
|
||||
//// [|super|];
|
||||
//// }
|
||||
////
|
||||
//// class A {
|
||||
//// }
|
||||
////
|
||||
//// class B extends A {
|
||||
//// constructor() {
|
||||
//// super();
|
||||
//// }
|
||||
//// }
|
||||
////}
|
||||
|
||||
test.ranges().forEach(r => {
|
||||
goTo.position(r.start);
|
||||
|
||||
verify.occurrencesAtPositionCount(0);
|
||||
});
|
Loading…
Reference in a new issue