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