Skip to content

Commit 492e1c5

Browse files
Merge pull request #593 from Microsoft/getOccurrencesReturn
Get occurrences for return keywords.
2 parents 294ad06 + 7b5440b commit 492e1c5

File tree

9 files changed

+252
-57
lines changed

9 files changed

+252
-57
lines changed

src/compiler/checker.ts

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4401,37 +4401,6 @@ module ts {
44014401
return voidType;
44024402
}
44034403

4404-
// WARNING: This has the same semantics as the forEach family of functions,
4405-
// in that traversal terminates in the event that 'visitor' supplies a truthy value.
4406-
function forEachReturnStatement<T>(body: Block, visitor: (stmt: ReturnStatement) => T): T {
4407-
4408-
return traverse(body);
4409-
4410-
function traverse(node: Node): T {
4411-
switch (node.kind) {
4412-
case SyntaxKind.ReturnStatement:
4413-
return visitor(node);
4414-
case SyntaxKind.Block:
4415-
case SyntaxKind.FunctionBlock:
4416-
case SyntaxKind.IfStatement:
4417-
case SyntaxKind.DoStatement:
4418-
case SyntaxKind.WhileStatement:
4419-
case SyntaxKind.ForStatement:
4420-
case SyntaxKind.ForInStatement:
4421-
case SyntaxKind.WithStatement:
4422-
case SyntaxKind.SwitchStatement:
4423-
case SyntaxKind.CaseClause:
4424-
case SyntaxKind.DefaultClause:
4425-
case SyntaxKind.LabelledStatement:
4426-
case SyntaxKind.TryStatement:
4427-
case SyntaxKind.TryBlock:
4428-
case SyntaxKind.CatchBlock:
4429-
case SyntaxKind.FinallyBlock:
4430-
return forEachChild(node, traverse);
4431-
}
4432-
}
4433-
}
4434-
44354404
/// Returns a set of types relating to every return expression relating to a function block.
44364405
function checkAndAggregateReturnExpressionTypes(body: Block, contextualMapper?: TypeMapper): Type[] {
44374406
var aggregatedTypes: Type[] = [];
@@ -5812,17 +5781,6 @@ module ts {
58125781
// TODO: Check that target label is valid
58135782
}
58145783

5815-
function getContainingFunction(node: Node): SignatureDeclaration {
5816-
while (true) {
5817-
node = node.parent;
5818-
if (!node || node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression ||
5819-
node.kind === SyntaxKind.ArrowFunction || node.kind === SyntaxKind.Method || node.kind === SyntaxKind.Constructor ||
5820-
node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor) {
5821-
return <SignatureDeclaration>node;
5822-
}
5823-
}
5824-
}
5825-
58265784
function checkReturnStatement(node: ReturnStatement) {
58275785
if (node.expression && !(getNodeLinks(node.expression).flags & NodeCheckFlags.TypeChecked)) {
58285786
var func = getContainingFunction(node);

src/compiler/parser.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,63 @@ module ts {
350350
}
351351
}
352352

353+
// Warning: This has the same semantics as the forEach family of functions,
354+
// in that traversal terminates in the event that 'visitor' supplies a truthy value.
355+
export function forEachReturnStatement<T>(body: Block, visitor: (stmt: ReturnStatement) => T): T {
356+
357+
return traverse(body);
358+
359+
function traverse(node: Node): T {
360+
switch (node.kind) {
361+
case SyntaxKind.ReturnStatement:
362+
return visitor(node);
363+
case SyntaxKind.Block:
364+
case SyntaxKind.FunctionBlock:
365+
case SyntaxKind.IfStatement:
366+
case SyntaxKind.DoStatement:
367+
case SyntaxKind.WhileStatement:
368+
case SyntaxKind.ForStatement:
369+
case SyntaxKind.ForInStatement:
370+
case SyntaxKind.WithStatement:
371+
case SyntaxKind.SwitchStatement:
372+
case SyntaxKind.CaseClause:
373+
case SyntaxKind.DefaultClause:
374+
case SyntaxKind.LabelledStatement:
375+
case SyntaxKind.TryStatement:
376+
case SyntaxKind.TryBlock:
377+
case SyntaxKind.CatchBlock:
378+
case SyntaxKind.FinallyBlock:
379+
return forEachChild(node, traverse);
380+
}
381+
}
382+
}
383+
384+
export function isAnyFunction(node: Node): boolean {
385+
if (node) {
386+
switch (node.kind) {
387+
case SyntaxKind.FunctionExpression:
388+
case SyntaxKind.FunctionDeclaration:
389+
case SyntaxKind.ArrowFunction:
390+
case SyntaxKind.Method:
391+
case SyntaxKind.GetAccessor:
392+
case SyntaxKind.SetAccessor:
393+
case SyntaxKind.Constructor:
394+
return true;
395+
}
396+
}
397+
398+
return false;
399+
}
400+
401+
export function getContainingFunction(node: Node): SignatureDeclaration {
402+
while (true) {
403+
node = node.parent;
404+
if (!node || isAnyFunction(node)) {
405+
return <SignatureDeclaration>node;
406+
}
407+
}
408+
}
409+
353410
export function hasRestParameters(s: SignatureDeclaration): boolean {
354411
return s.parameters.length > 0 && (s.parameters[s.parameters.length - 1].flags & NodeFlags.Rest) !== 0;
355412
}

src/compiler/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ module ts {
220220
LastFutureReservedWord = YieldKeyword,
221221
FirstTypeNode = TypeReference,
222222
LastTypeNode = ArrayType,
223-
FirstPunctuation= OpenBraceToken,
223+
FirstPunctuation = OpenBraceToken,
224224
LastPunctuation = CaretEqualsToken
225225
}
226226

src/services/services.ts

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,20 +1312,6 @@ module ts {
13121312
return node.parent.kind === SyntaxKind.NewExpression && (<CallExpression>node.parent).func === node;
13131313
}
13141314

1315-
function isAnyFunction(node: Node): boolean {
1316-
switch (node.kind) {
1317-
case SyntaxKind.FunctionExpression:
1318-
case SyntaxKind.FunctionDeclaration:
1319-
case SyntaxKind.ArrowFunction:
1320-
case SyntaxKind.Method:
1321-
case SyntaxKind.GetAccessor:
1322-
case SyntaxKind.SetAccessor:
1323-
case SyntaxKind.Constructor:
1324-
return true;
1325-
}
1326-
return false;
1327-
}
1328-
13291315
function isNameOfFunctionDeclaration(node: Node): boolean {
13301316
return node.kind === SyntaxKind.Identifier &&
13311317
isAnyFunction(node.parent) && (<FunctionDeclaration>node.parent).name === node;
@@ -2171,6 +2157,11 @@ module ts {
21712157
return getIfElseOccurrences(<IfStatement>node.parent);
21722158
}
21732159
break;
2160+
case SyntaxKind.ReturnKeyword:
2161+
if (hasKind(node.parent, SyntaxKind.ReturnStatement)) {
2162+
return getReturnOccurrences(<ReturnStatement>node.parent);
2163+
}
2164+
break;
21742165
case SyntaxKind.TryKeyword:
21752166
case SyntaxKind.CatchKeyword:
21762167
case SyntaxKind.FinallyKeyword:
@@ -2258,6 +2249,22 @@ module ts {
22582249
return result;
22592250
}
22602251

2252+
function getReturnOccurrences(returnStatement: ReturnStatement): ReferenceEntry[]{
2253+
var func = <FunctionDeclaration>getContainingFunction(returnStatement);
2254+
2255+
// If we didn't find a containing function with a block body, bail out.
2256+
if (!(func && hasKind(func.body, SyntaxKind.FunctionBlock))) {
2257+
return undefined;
2258+
}
2259+
2260+
var keywords: Node[] = []
2261+
forEachReturnStatement(<Block>(<FunctionDeclaration>func).body, returnStatement => {
2262+
pushKeywordIf(keywords, returnStatement.getFirstToken(), SyntaxKind.ReturnKeyword);
2263+
});
2264+
2265+
return map(keywords, keywordToReferenceEntry);
2266+
}
2267+
22612268
function getTryCatchFinallyOccurrences(tryStatement: TryStatement): ReferenceEntry[] {
22622269
var keywords: Node[] = [];
22632270

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////function f(a: number) {
4+
//// if (a > 0) {
5+
//// [|ret/**/urn|] (function () {
6+
//// return;
7+
//// return;
8+
//// return;
9+
////
10+
//// if (false) {
11+
//// return true;
12+
//// }
13+
//// })() || true;
14+
//// }
15+
////
16+
//// var unusued = [1, 2, 3, 4].map(x => { return 4 })
17+
////
18+
//// [|return|];
19+
//// [|return|] true;
20+
////}
21+
22+
test.ranges().forEach(r => {
23+
goTo.position(r.start);
24+
25+
test.ranges().forEach(range => {
26+
verify.occurrencesAtPositionContains(range, false);
27+
});
28+
});
29+
30+
goTo.marker();
31+
test.ranges().forEach(range => {
32+
verify.occurrencesAtPositionContains(range, false);
33+
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////function f(a: number) {
4+
//// if (a > 0) {
5+
//// return (function () {
6+
//// [|return|];
7+
//// [|ret/**/urn|];
8+
//// [|return|];
9+
////
10+
//// while (false) {
11+
//// [|return|] true;
12+
//// }
13+
//// })() || true;
14+
//// }
15+
////
16+
//// var unusued = [1, 2, 3, 4].map(x => { return 4 })
17+
////
18+
//// return;
19+
//// return true;
20+
////}
21+
22+
test.ranges().forEach(r => {
23+
goTo.position(r.start);
24+
25+
test.ranges().forEach(range => {
26+
verify.occurrencesAtPositionContains(range, false);
27+
});
28+
});
29+
30+
goTo.marker();
31+
test.ranges().forEach(range => {
32+
verify.occurrencesAtPositionContains(range, false);
33+
});
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////function f(a: number) {
4+
//// if (a > 0) {
5+
//// return (function () {
6+
//// return;
7+
//// return;
8+
//// return;
9+
////
10+
//// if (false) {
11+
//// return true;
12+
//// }
13+
//// })() || true;
14+
//// }
15+
////
16+
//// var unusued = [1, 2, 3, 4].map(x => { [|return|] 4 })
17+
////
18+
//// return;
19+
//// return true;
20+
////}
21+
22+
test.ranges().forEach(r => {
23+
goTo.position(r.start);
24+
25+
test.ranges().forEach(range => {
26+
verify.occurrencesAtPositionContains(range, false);
27+
});
28+
});
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////ret/*1*/urn;
4+
////retu/*2*/rn;
5+
////function f(a: number) {
6+
//// if (a > 0) {
7+
//// return (function () {
8+
//// () => [|return|];
9+
//// [|return|];
10+
//// [|return|];
11+
////
12+
//// if (false) {
13+
//// [|return|] true;
14+
//// }
15+
//// })() || true;
16+
//// }
17+
////
18+
//// var unusued = [1, 2, 3, 4].map(x => { return 4 })
19+
////
20+
//// return;
21+
//// return true;
22+
////}
23+
////
24+
////class A {
25+
//// ret/*3*/urn;
26+
//// r/*4*/eturn 8675309;
27+
////}
28+
29+
// Note: For this test, these 'return's get highlighted as a result of a parse recovery
30+
// where if an arrow function starts with a statement, we try to parse a body
31+
// as if it was missing curly braces. If the behavior changes in the future,
32+
// a change to this test is very much welcome.
33+
test.ranges().forEach(r => {
34+
goTo.position(r.start);
35+
36+
test.ranges().forEach(range => {
37+
verify.occurrencesAtPositionContains(range, false);
38+
});
39+
});
40+
41+
for (var i = 1; i <= test.markers().length; i++) {
42+
goTo.marker("" + i);
43+
44+
switch (i) {
45+
case 0:
46+
case 1:
47+
case 4:
48+
verify.occurrencesAtPositionCount(0);
49+
break;
50+
case 3:
51+
verify.occurrencesAtPositionCount(1); // 'return' is an instance member
52+
break;
53+
}
54+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////function f(a: number) {
4+
//// if (a > 0) {
5+
//// return (function () {
6+
//// return/*1*/;
7+
//// return/*2*/;
8+
//// return/*3*/;
9+
////
10+
//// if (false) {
11+
//// return/*4*/ true;
12+
//// }
13+
//// })() || true;
14+
//// }
15+
////
16+
//// var unusued = [1, 2, 3, 4].map(x => { return/*5*/ 4 })
17+
////
18+
//// return/*6*/;
19+
//// return/*7*/ true;
20+
////}
21+
22+
test.markers().forEach(m => {
23+
goTo.position(m.position, m.fileName)
24+
verify.occurrencesAtPositionCount(0);
25+
});

0 commit comments

Comments
 (0)