Skip to content

Commit 2e2d0c3

Browse files
Extracted 'break'/'continue' aggregation into common helper function.
Also addressed other CR feedback. Still need tests.
1 parent 90dd327 commit 2e2d0c3

File tree

1 file changed

+65
-73
lines changed

1 file changed

+65
-73
lines changed

src/services/services.ts

Lines changed: 65 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,8 +1296,12 @@ module ts {
12961296
(<LabelledStatement>node.parent).label === node;
12971297
}
12981298

1299+
/**
1300+
* Whether or not a 'node' is preceded by a label of the given string.
1301+
* Note: 'node' cannot be a SourceFile.
1302+
*/
12991303
function isLabelledBy(node: Node, labelName: string) {
1300-
for (var owner = node.parent; owner && owner.kind === SyntaxKind.LabelledStatement; owner = owner.parent) {
1304+
for (var owner = node.parent; owner.kind === SyntaxKind.LabelledStatement; owner = owner.parent) {
13011305
if ((<LabelledStatement>owner).label.text === labelName) {
13021306
return true;
13031307
}
@@ -2326,43 +2330,12 @@ module ts {
23262330
}
23272331
}
23282332
}
2329-
2330-
// These track whether we can own unlabeled break/continues.
2331-
var breakSearchType = BreakContinueSearchType.All;
2332-
var continueSearchType = BreakContinueSearchType.All;
23332333

2334-
(function aggregateBreakContinues(node: Node) {
2335-
// Remember the statuses of the flags before diving into the next node.
2336-
var prevBreakSearchType = breakSearchType;
2337-
var prevContinueSearchType = continueSearchType;
2338-
2339-
switch (node.kind) {
2340-
case SyntaxKind.BreakStatement:
2341-
case SyntaxKind.ContinueStatement:
2342-
if (ownsBreakOrContinue(loopNode, <BreakOrContinueStatement>node, breakSearchType, continueSearchType)) {
2343-
pushKeywordIf(keywords, node.getFirstToken(), SyntaxKind.BreakKeyword, SyntaxKind.ContinueKeyword);
2344-
}
2345-
break;
2346-
2347-
case SyntaxKind.ForStatement:
2348-
case SyntaxKind.ForInStatement:
2349-
case SyntaxKind.DoStatement:
2350-
case SyntaxKind.WhileStatement:
2351-
continueSearchType = BreakContinueSearchType.Labeled;
2352-
// Fall through
2353-
case SyntaxKind.SwitchStatement:
2354-
breakSearchType = BreakContinueSearchType.Labeled;
2355-
}
2356-
2357-
// Do not cross function boundaries.
2358-
if (!isAnyFunction(node)) {
2359-
forEachChild(node, aggregateBreakContinues);
2360-
}
2361-
2362-
// Restore the last state.
2363-
breakSearchType = prevBreakSearchType;
2364-
continueSearchType = prevContinueSearchType;
2365-
})(loopNode.statement);
2334+
aggregateBreakAndContinueKeywords(/* owner */ loopNode,
2335+
/* startPoint */ loopNode.statement,
2336+
/* breakSearchType */ BreakContinueSearchType.All,
2337+
/* continueSearchType */ BreakContinueSearchType.All,
2338+
/* keywordAccumulator */ keywords);
23662339

23672340
return map(keywords, getReferenceEntryFromNode);
23682341
}
@@ -2375,41 +2348,16 @@ module ts {
23752348
// Types of break statements we can grab on to.
23762349
var breakSearchType = BreakContinueSearchType.All;
23772350

2378-
// Go through each clause in the switch statement, collecting the case/default keywords.
2351+
// Go through each clause in the switch statement, collecting the 'case'/'default' keywords.
23792352
forEach(switchStatement.clauses, clause => {
23802353
pushKeywordIf(keywords, clause.getFirstToken(), SyntaxKind.CaseKeyword, SyntaxKind.DefaultKeyword);
23812354

2382-
// For each clause, also recursively traverse the statements where we can find analogous breaks.
2383-
forEachChild(clause, function aggregateBreakKeywords(node: Node): void {
2384-
// Back the old search value up.
2385-
var oldBreakSearchType = breakSearchType;
2386-
2387-
switch (node.kind) {
2388-
case SyntaxKind.BreakStatement:
2389-
// If the break statement has a label, it cannot be part of a switch block.
2390-
if (ownsBreakOrContinue(switchStatement,
2391-
<BreakOrContinueStatement>node,
2392-
breakSearchType,
2393-
/*continuesSearchType*/ BreakContinueSearchType.None)) {
2394-
pushKeywordIf(keywords, node.getFirstToken(), SyntaxKind.BreakKeyword);
2395-
}
2396-
break;
2397-
case SyntaxKind.ForStatement:
2398-
case SyntaxKind.ForInStatement:
2399-
case SyntaxKind.DoStatement:
2400-
case SyntaxKind.WhileStatement:
2401-
case SyntaxKind.SwitchStatement:
2402-
breakSearchType = BreakContinueSearchType.Labeled;
2403-
}
2404-
2405-
// Do not cross function boundaries.
2406-
if (!isAnyFunction(node)) {
2407-
forEachChild(node, aggregateBreakKeywords);
2408-
}
2409-
2410-
// Restore the last state.
2411-
breakSearchType = oldBreakSearchType;
2412-
});
2355+
// For each clause, aggregate each of the analogous 'break' statements.
2356+
aggregateBreakAndContinueKeywords(/* owner */ switchStatement,
2357+
/* startPoint */ clause,
2358+
/* breakSearchType */ BreakContinueSearchType.All,
2359+
/* continueSearchType */ BreakContinueSearchType.None,
2360+
/* keywordAccumulator */ keywords);
24132361
});
24142362

24152363
return map(keywords, getReferenceEntryFromNode);
@@ -2445,19 +2393,63 @@ module ts {
24452393
return undefined;
24462394
}
24472395

2396+
function aggregateBreakAndContinueKeywords(owner: Node,
2397+
startPoint: Node,
2398+
breakSearchType: BreakContinueSearchType,
2399+
continueSearchType: BreakContinueSearchType,
2400+
keywordAccumulator: Node[]): void {
2401+
(function aggregate(node: Node) {
2402+
// Remember the statuses of the flags before diving into the next node.
2403+
var prevBreakSearchType = breakSearchType;
2404+
var prevContinueSearchType = continueSearchType;
2405+
2406+
switch (node.kind) {
2407+
case SyntaxKind.BreakStatement:
2408+
case SyntaxKind.ContinueStatement:
2409+
if (ownsBreakOrContinue(owner, <BreakOrContinueStatement>node, breakSearchType, continueSearchType)) {
2410+
pushKeywordIf(keywordAccumulator, node.getFirstToken(), SyntaxKind.BreakKeyword, SyntaxKind.ContinueKeyword);
2411+
}
2412+
break;
2413+
2414+
case SyntaxKind.ForStatement:
2415+
case SyntaxKind.ForInStatement:
2416+
case SyntaxKind.DoStatement:
2417+
case SyntaxKind.WhileStatement:
2418+
// Inner loops take ownership of unlabeled 'continue' statements.
2419+
continueSearchType &= ~BreakContinueSearchType.Unlabeled;
2420+
// Fall through
2421+
case SyntaxKind.SwitchStatement:
2422+
// Inner loops & 'switch' statements take ownership of unlabeled 'break' statements.
2423+
breakSearchType &= ~BreakContinueSearchType.Unlabeled;
2424+
break;
2425+
}
2426+
2427+
// Do not cross function boundaries.
2428+
if (!isAnyFunction(node)) {
2429+
forEachChild(node, aggregate);
2430+
}
2431+
2432+
// Restore the last state.
2433+
breakSearchType = prevBreakSearchType;
2434+
continueSearchType = prevContinueSearchType;
2435+
})(startPoint);
2436+
2437+
return;
2438+
}
2439+
24482440
// Note: 'statement' must be a descendant of 'root'.
24492441
// Reasonable logic for restricting traversal prior to arriving at the
24502442
// 'statement' node is beyond the scope of this function.
2451-
function ownsBreakOrContinue(root: Node,
2443+
function ownsBreakOrContinue(owner: Node,
24522444
statement: BreakOrContinueStatement,
24532445
breakSearchType: BreakContinueSearchType,
24542446
continueSearchType: BreakContinueSearchType): boolean {
24552447
var searchType = statement.kind === SyntaxKind.BreakStatement ?
24562448
breakSearchType :
24572449
continueSearchType;
24582450

2459-
if (statement.label) {
2460-
return isLabelledBy(root, statement.label.text);
2451+
if (statement.label && (searchType & BreakContinueSearchType.Labeled)) {
2452+
return isLabelledBy(owner, statement.label.text);
24612453
}
24622454
else {
24632455
return !!(searchType & BreakContinueSearchType.Unlabeled);
@@ -2817,7 +2809,7 @@ module ts {
28172809
if (isExternalModule(<SourceFile>searchSpaceNode)) {
28182810
return undefined;
28192811
}
2820-
// Fall through
2812+
// Fall through
28212813
case SyntaxKind.FunctionDeclaration:
28222814
case SyntaxKind.FunctionExpression:
28232815
break;

0 commit comments

Comments
 (0)