Skip to content

Commit dd5d216

Browse files
Merge pull request #600 from Microsoft/getOccurrencesThis
Get occurrences for 'this' keywords
2 parents 492e1c5 + 024ca6d commit dd5d216

11 files changed

+1247
-82
lines changed

src/compiler/checker.ts

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3449,29 +3449,6 @@ module ts {
34493449
return getTypeOfSymbol(getExportSymbolOfValueSymbolIfExported(symbol));
34503450
}
34513451

3452-
function getThisContainer(node: Node): Node {
3453-
while (true) {
3454-
node = node.parent;
3455-
if (!node) {
3456-
return node;
3457-
}
3458-
switch (node.kind) {
3459-
case SyntaxKind.FunctionDeclaration:
3460-
case SyntaxKind.FunctionExpression:
3461-
case SyntaxKind.ModuleDeclaration:
3462-
case SyntaxKind.Property:
3463-
case SyntaxKind.Method:
3464-
case SyntaxKind.Constructor:
3465-
case SyntaxKind.GetAccessor:
3466-
case SyntaxKind.SetAccessor:
3467-
case SyntaxKind.EnumDeclaration:
3468-
case SyntaxKind.SourceFile:
3469-
case SyntaxKind.ArrowFunction:
3470-
return node;
3471-
}
3472-
}
3473-
}
3474-
34753452
function captureLexicalThis(node: Node, container: Node): void {
34763453
var classNode = container.parent && container.parent.kind === SyntaxKind.ClassDeclaration ? container.parent : undefined;
34773454
getNodeLinks(node).flags |= NodeCheckFlags.LexicalThis;
@@ -3484,11 +3461,14 @@ module ts {
34843461
}
34853462

34863463
function checkThisExpression(node: Node): Type {
3487-
var container = getThisContainer(node);
3464+
// Stop at the first arrow function so that we can
3465+
// tell whether 'this' needs to be captured.
3466+
var container = getThisContainer(node, /* includeArrowFunctions */ true);
34883467
var needToCaptureLexicalThis = false;
3489-
// skip arrow functions
3490-
while (container.kind === SyntaxKind.ArrowFunction) {
3491-
container = getThisContainer(container);
3468+
3469+
// Now skip arrow functions to get the "real" owner of 'this'.
3470+
if (container.kind === SyntaxKind.ArrowFunction) {
3471+
container = getThisContainer(container, /* includeArrowFunctions */ false);
34923472
needToCaptureLexicalThis = true;
34933473
}
34943474

src/compiler/parser.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,33 @@ module ts {
407407
}
408408
}
409409

410+
export function getThisContainer(node: Node, includeArrowFunctions: boolean): Node {
411+
while (true) {
412+
node = node.parent;
413+
if (!node) {
414+
return node;
415+
}
416+
switch (node.kind) {
417+
case SyntaxKind.ArrowFunction:
418+
if (!includeArrowFunctions) {
419+
continue;
420+
}
421+
// Fall through
422+
case SyntaxKind.FunctionDeclaration:
423+
case SyntaxKind.FunctionExpression:
424+
case SyntaxKind.ModuleDeclaration:
425+
case SyntaxKind.Property:
426+
case SyntaxKind.Method:
427+
case SyntaxKind.Constructor:
428+
case SyntaxKind.GetAccessor:
429+
case SyntaxKind.SetAccessor:
430+
case SyntaxKind.EnumDeclaration:
431+
case SyntaxKind.SourceFile:
432+
return node;
433+
}
434+
}
435+
}
436+
410437
export function hasRestParameters(s: SignatureDeclaration): boolean {
411438
return s.parameters.length > 0 && (s.parameters[s.parameters.length - 1].flags & NodeFlags.Rest) !== 0;
412439
}

src/services/services.ts

Lines changed: 132 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2146,7 +2146,8 @@ module ts {
21462146
return undefined;
21472147
}
21482148

2149-
if (node.kind === SyntaxKind.Identifier || isLiteralNameOfPropertyDeclarationOrIndexAccess(node) || isNameOfExternalModuleImportOrDeclaration(node)) {
2149+
if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword ||
2150+
isLiteralNameOfPropertyDeclarationOrIndexAccess(node) || isNameOfExternalModuleImportOrDeclaration(node)) {
21502151
return getReferencesForNode(node, [sourceFile]);
21512152
}
21522153

@@ -2243,7 +2244,7 @@ module ts {
22432244
}
22442245

22452246
// Ordinary case: just highlight the keyword.
2246-
result.push(keywordToReferenceEntry(keywords[i]));
2247+
result.push(getReferenceEntryFromNode(keywords[i]));
22472248
}
22482249

22492250
return result;
@@ -2262,7 +2263,7 @@ module ts {
22622263
pushKeywordIf(keywords, returnStatement.getFirstToken(), SyntaxKind.ReturnKeyword);
22632264
});
22642265

2265-
return map(keywords, keywordToReferenceEntry);
2266+
return map(keywords, getReferenceEntryFromNode);
22662267
}
22672268

22682269
function getTryCatchFinallyOccurrences(tryStatement: TryStatement): ReferenceEntry[] {
@@ -2278,7 +2279,7 @@ module ts {
22782279
pushKeywordIf(keywords, tryStatement.finallyBlock.getFirstToken(), SyntaxKind.FinallyKeyword);
22792280
}
22802281

2281-
return map(keywords, keywordToReferenceEntry);
2282+
return map(keywords, getReferenceEntryFromNode);
22822283
}
22832284

22842285
function getSwitchCaseDefaultOccurrences(switchStatement: SwitchStatement) {
@@ -2314,7 +2315,7 @@ module ts {
23142315
});
23152316
});
23162317

2317-
return map(keywords, keywordToReferenceEntry);
2318+
return map(keywords, getReferenceEntryFromNode);
23182319
}
23192320

23202321
function getBreakStatementOccurences(breakStatement: BreakOrContinueStatement): ReferenceEntry[]{
@@ -2363,10 +2364,6 @@ module ts {
23632364

23642365
return false;
23652366
}
2366-
2367-
function keywordToReferenceEntry(keyword: Node): ReferenceEntry {
2368-
return new ReferenceEntry(filename, TypeScript.TextSpan.fromBounds(keyword.getStart(), keyword.end), /* isWriteAccess */ false);
2369-
}
23702367
}
23712368

23722369
function getReferencesAtPosition(filename: string, position: number): ReferenceEntry[] {
@@ -2381,6 +2378,7 @@ module ts {
23812378
}
23822379

23832380
if (node.kind !== SyntaxKind.Identifier &&
2381+
node.kind !== SyntaxKind.ThisKeyword &&
23842382
!isLiteralNameOfPropertyDeclarationOrIndexAccess(node) &&
23852383
!isNameOfExternalModuleImportOrDeclaration(node)) {
23862384
return undefined;
@@ -2396,21 +2394,25 @@ module ts {
23962394
var labelDefinition = getTargetLabel((<BreakOrContinueStatement>node.parent), (<Identifier>node).text);
23972395
// if we have a label definition, look within its statement for references, if not, then
23982396
// the label is undefined, just return a set of one for the current node.
2399-
return labelDefinition ? getLabelReferencesInNode(labelDefinition.parent, labelDefinition) : [getReferenceEntry(node)];
2397+
return labelDefinition ? getLabelReferencesInNode(labelDefinition.parent, labelDefinition) : [getReferenceEntryFromNode(node)];
24002398
}
24012399
else {
24022400
// it is a label definition and not a target, search within the parent labeledStatement
24032401
return getLabelReferencesInNode(node.parent, <Identifier>node);
24042402
}
24052403
}
24062404

2405+
if (node.kind === SyntaxKind.ThisKeyword) {
2406+
return getReferencesForThisKeyword(node, sourceFiles);
2407+
}
2408+
24072409
var symbol = typeInfoResolver.getSymbolInfo(node);
24082410

24092411
// Could not find a symbol e.g. unknown identifier
24102412
if (!symbol) {
24112413
// Even if we did not find a symbol, we have an identifer, so there is at least
2412-
// one reference that we know of. return than instead of undefined.
2413-
return [getReferenceEntry(node)];
2414+
// one reference that we know of. return that instead of undefined.
2415+
return [getReferenceEntryFromNode(node)];
24142416
}
24152417

24162418
// the symbol was an internal symbol and does not have a declaration e.g.undefined symbol
@@ -2554,7 +2556,7 @@ module ts {
25542556
// Only pick labels that are either the target label, or have a target that is the target label
25552557
if (node === targetLabel ||
25562558
(isJumpStatementTarget(node) && getTargetLabel(node, labelName) === targetLabel)) {
2557-
result.push(getReferenceEntry(node));
2559+
result.push(getReferenceEntryFromNode(node));
25582560
}
25592561
});
25602562
return result;
@@ -2619,7 +2621,96 @@ module ts {
26192621
}
26202622

26212623
if (isRelatableToSearchSet(searchSymbols, referenceSymbol, referenceLocation)) {
2622-
result.push(getReferenceEntry(referenceLocation));
2624+
result.push(getReferenceEntryFromNode(referenceLocation));
2625+
}
2626+
});
2627+
}
2628+
}
2629+
2630+
function getReferencesForThisKeyword(thisKeyword: Node, sourceFiles: SourceFile[]) {
2631+
// Get the owner" of the 'this' keyword.
2632+
var thisContainer = getThisContainer(thisKeyword, /* includeArrowFunctions */ false);
2633+
2634+
var searchSpaceNode: Node;
2635+
2636+
// Whether 'this' occurs in a static context within a class;
2637+
var staticFlag = NodeFlags.Static;
2638+
2639+
switch (thisContainer.kind) {
2640+
case SyntaxKind.Property:
2641+
case SyntaxKind.Method:
2642+
case SyntaxKind.Constructor:
2643+
case SyntaxKind.GetAccessor:
2644+
case SyntaxKind.SetAccessor:
2645+
searchSpaceNode = thisContainer.parent; // should be the owning class
2646+
staticFlag &= thisContainer.flags
2647+
break;
2648+
case SyntaxKind.SourceFile:
2649+
if (isExternalModule(<SourceFile>thisContainer)) {
2650+
return undefined;
2651+
}
2652+
// Fall through
2653+
case SyntaxKind.FunctionDeclaration:
2654+
case SyntaxKind.FunctionExpression:
2655+
searchSpaceNode = thisContainer;
2656+
break;
2657+
default:
2658+
return undefined;
2659+
}
2660+
2661+
var result: ReferenceEntry[] = [];
2662+
2663+
if (searchSpaceNode.kind === SyntaxKind.SourceFile) {
2664+
forEach(sourceFiles, sourceFile => {
2665+
var possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this", sourceFile.getStart(), sourceFile.getEnd());
2666+
getThisReferencesInFile(sourceFile, sourceFile, possiblePositions, result);
2667+
});
2668+
}
2669+
else {
2670+
var sourceFile = searchSpaceNode.getSourceFile();
2671+
var possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this", searchSpaceNode.getStart(), searchSpaceNode.getEnd());
2672+
getThisReferencesInFile(sourceFile, searchSpaceNode, possiblePositions, result);
2673+
}
2674+
2675+
return result;
2676+
2677+
function getThisReferencesInFile(sourceFile: SourceFile, searchSpaceNode: Node, possiblePositions: number[], result: ReferenceEntry[]): void {
2678+
forEach(possiblePositions, position => {
2679+
cancellationToken.throwIfCancellationRequested();
2680+
2681+
var node = getNodeAtPosition(sourceFile, position);
2682+
if (!node || node.kind !== SyntaxKind.ThisKeyword) {
2683+
return;
2684+
}
2685+
2686+
// Get the owner of the 'this' keyword.
2687+
// This *should* be a node that occurs somewhere within searchSpaceNode.
2688+
var container = getThisContainer(node, /* includeArrowFunctions */ false);
2689+
2690+
switch (container.kind) {
2691+
case SyntaxKind.Property:
2692+
case SyntaxKind.Method:
2693+
case SyntaxKind.Constructor:
2694+
case SyntaxKind.GetAccessor:
2695+
case SyntaxKind.SetAccessor:
2696+
// Make sure the container belongs to the same class
2697+
// and has the appropriate static modifier from the original container.
2698+
if (searchSpaceNode.symbol === container.parent.symbol && (container.flags & NodeFlags.Static) === staticFlag) {
2699+
result.push(getReferenceEntryFromNode(node));
2700+
}
2701+
break;
2702+
case SyntaxKind.FunctionDeclaration:
2703+
case SyntaxKind.FunctionExpression:
2704+
if (searchSpaceNode.symbol === container.symbol) {
2705+
result.push(getReferenceEntryFromNode(node));
2706+
}
2707+
break;
2708+
case SyntaxKind.SourceFile:
2709+
// Add all 'this' keywords that belong to the top-level scope.
2710+
if (searchSpaceNode.kind === SyntaxKind.SourceFile && !isExternalModule(<SourceFile>searchSpaceNode)) {
2711+
result.push(getReferenceEntryFromNode(node));
2712+
}
2713+
break;
26232714
}
26242715
});
26252716
}
@@ -2721,18 +2812,6 @@ module ts {
27212812
return undefined;
27222813
}
27232814

2724-
function getReferenceEntry(node: Node): ReferenceEntry {
2725-
var start = node.getStart();
2726-
var end = node.getEnd();
2727-
2728-
if (node.kind === SyntaxKind.StringLiteral) {
2729-
start += 1;
2730-
end -= 1;
2731-
}
2732-
2733-
return new ReferenceEntry(node.getSourceFile().filename, TypeScript.TextSpan.fromBounds(start, end), isWriteAccess(node));
2734-
}
2735-
27362815
function getMeaningFromDeclaration(node: Declaration): SearchMeaning {
27372816
switch (node.kind) {
27382817
case SyntaxKind.Parameter:
@@ -2869,40 +2948,38 @@ module ts {
28692948
}
28702949
return meaning;
28712950
}
2951+
}
28722952

2873-
/// A node is considedered a writeAccess iff it is a name of a declaration or a target of an assignment
2874-
function isWriteAccess(node: Node): boolean {
2875-
if (node.kind === SyntaxKind.Identifier && isDeclarationOrFunctionExpressionOrCatchVariableName(node)) {
2876-
return true;
2877-
}
2953+
function getReferenceEntryFromNode(node: Node): ReferenceEntry {
2954+
var start = node.getStart();
2955+
var end = node.getEnd();
28782956

2879-
var parent = node.parent;
2880-
if (parent) {
2881-
if (parent.kind === SyntaxKind.PostfixOperator || parent.kind === SyntaxKind.PrefixOperator) {
2882-
return true;
2883-
}
2884-
else if (parent.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>parent).left === node) {
2885-
var operator = (<BinaryExpression>parent).operator;
2886-
switch (operator) {
2887-
case SyntaxKind.AsteriskEqualsToken:
2888-
case SyntaxKind.SlashEqualsToken:
2889-
case SyntaxKind.PercentEqualsToken:
2890-
case SyntaxKind.MinusEqualsToken:
2891-
case SyntaxKind.LessThanLessThanEqualsToken:
2892-
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
2893-
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
2894-
case SyntaxKind.BarEqualsToken:
2895-
case SyntaxKind.CaretEqualsToken:
2896-
case SyntaxKind.AmpersandEqualsToken:
2897-
case SyntaxKind.PlusEqualsToken:
2898-
case SyntaxKind.EqualsToken:
2899-
return true;
2900-
}
2901-
}
2957+
if (node.kind === SyntaxKind.StringLiteral) {
2958+
start += 1;
2959+
end -= 1;
2960+
}
29022961

2903-
return false;
2962+
return new ReferenceEntry(node.getSourceFile().filename, TypeScript.TextSpan.fromBounds(start, end), isWriteAccess(node));
2963+
}
2964+
2965+
/// A node is considedered a writeAccess iff it is a name of a declaration or a target of an assignment
2966+
function isWriteAccess(node: Node): boolean {
2967+
if (node.kind === SyntaxKind.Identifier && isDeclarationOrFunctionExpressionOrCatchVariableName(node)) {
2968+
return true;
2969+
}
2970+
2971+
var parent = node.parent;
2972+
if (parent) {
2973+
if (parent.kind === SyntaxKind.PostfixOperator || parent.kind === SyntaxKind.PrefixOperator) {
2974+
return true;
2975+
}
2976+
else if (parent.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>parent).left === node) {
2977+
var operator = (<BinaryExpression>parent).operator;
2978+
return SyntaxKind.FirstAssignment <= operator && operator <= SyntaxKind.LastAssignment;
29042979
}
29052980
}
2981+
2982+
return false;
29062983
}
29072984

29082985
/// Syntactic features
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
// @Filename: file1.ts
4+
////this; this;
5+
6+
// @Filename: file2.ts
7+
////this;
8+
////this;
9+
10+
// @Filename: file3.ts
11+
//// ((x = this, y) => t/**/his)(this, this);
12+
13+
goTo.file("file1.ts");
14+
goTo.marker();
15+
verify.referencesCountIs(8);

0 commit comments

Comments
 (0)