Merge pull request #632 from Microsoft/getOccurrencesSuperThanksForAsking

Get occurrences for 'super' keywords
This commit is contained in:
Daniel Rosenwasser 2014-09-09 14:25:27 -07:00
commit e1f8aa7b8f
6 changed files with 250 additions and 34 deletions

View file

@ -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;
}

View file

@ -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;

View file

@ -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);

View 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);
});

View 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);
});

View 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);
});