@@ -1296,6 +1296,16 @@ module ts {
1296
1296
( < LabelledStatement > node . parent ) . label === node ;
1297
1297
}
1298
1298
1299
+ function isLabelledBy ( node : Node , labelName : string ) {
1300
+ for ( var owner = node . parent ; owner && owner . kind === SyntaxKind . LabelledStatement ; owner = owner . parent ) {
1301
+ if ( ( < LabelledStatement > owner ) . label . text === labelName ) {
1302
+ return true ;
1303
+ }
1304
+ }
1305
+
1306
+ return false ;
1307
+ }
1308
+
1299
1309
function isLabelName ( node : Node ) : boolean {
1300
1310
return isLabelOfLabeledStatement ( node ) || isJumpStatementTarget ( node ) ;
1301
1311
}
@@ -2181,8 +2191,20 @@ module ts {
2181
2191
}
2182
2192
break ;
2183
2193
case SyntaxKind . BreakKeyword :
2184
- if ( hasKind ( node . parent , SyntaxKind . BreakStatement ) ) {
2185
- return getBreakStatementOccurences ( < BreakOrContinueStatement > node . parent ) ;
2194
+ case SyntaxKind . ContinueKeyword :
2195
+ if ( hasKind ( node . parent , SyntaxKind . BreakStatement ) || hasKind ( node . parent , SyntaxKind . ContinueStatement ) ) {
2196
+ return getBreakOrContinueStatementOccurences ( < BreakOrContinueStatement > node . parent ) ;
2197
+ }
2198
+ break ;
2199
+ case SyntaxKind . ForKeyword :
2200
+ if ( hasKind ( node . parent , SyntaxKind . ForStatement ) || hasKind ( node . parent , SyntaxKind . ForInStatement ) ) {
2201
+ return getLoopBreakContinueOccurrences ( < IterationStatement > node . parent ) ;
2202
+ }
2203
+ break ;
2204
+ case SyntaxKind . WhileKeyword :
2205
+ case SyntaxKind . DoKeyword :
2206
+ if ( hasKind ( node . parent , SyntaxKind . WhileStatement ) || hasKind ( node . parent , SyntaxKind . DoStatement ) ) {
2207
+ return getLoopBreakContinueOccurrences ( < IterationStatement > node . parent ) ;
2186
2208
}
2187
2209
break ;
2188
2210
}
@@ -2281,6 +2303,66 @@ module ts {
2281
2303
return map ( keywords , getReferenceEntryFromNode ) ;
2282
2304
}
2283
2305
2306
+ function getLoopBreakContinueOccurrences ( loopNode : IterationStatement ) : ReferenceEntry [ ] {
2307
+ var keywords : Node [ ] = [ ] ;
2308
+
2309
+ if ( pushKeywordIf ( keywords , loopNode . getFirstToken ( ) , SyntaxKind . ForKeyword , SyntaxKind . WhileKeyword , SyntaxKind . DoKeyword ) ) {
2310
+ // If we succeeded and got a do-while loop, then start looking for a 'while' keyword.
2311
+ if ( loopNode . kind === SyntaxKind . DoStatement ) {
2312
+ var loopTokens = loopNode . getChildren ( ) ;
2313
+
2314
+ for ( var i = loopTokens . length - 1 ; i >= 0 ; i -- ) {
2315
+ if ( pushKeywordIf ( keywords , loopTokens [ i ] , SyntaxKind . WhileKeyword ) ) {
2316
+ break ;
2317
+ }
2318
+ }
2319
+ }
2320
+ }
2321
+
2322
+ // This switch tracks whether or not we're traversing into a construct that takes
2323
+ // ownership over unlabelled 'break'/'continue' statements.
2324
+ var onlyCheckLabelled = false ;
2325
+
2326
+ forEachChild ( loopNode . statement , function aggregateBreakContinues ( node : Node ) {
2327
+ // This tracks the status of the flag before diving into the next node.
2328
+ var lastOnlyCheckLabelled = onlyCheckLabelled ;
2329
+
2330
+ switch ( node . kind ) {
2331
+ case SyntaxKind . BreakStatement :
2332
+ case SyntaxKind . ContinueStatement :
2333
+ // If the 'break'/'continue' statement has a label, it must be one of our tracked labels.
2334
+ if ( ( < BreakOrContinueStatement > node ) . label ) {
2335
+ var labelName = ( < BreakOrContinueStatement > node ) . label . text ;
2336
+ if ( isLabelledBy ( loopNode , labelName ) ) {
2337
+ pushKeywordIf ( keywords , node . getFirstToken ( ) , SyntaxKind . BreakKeyword , SyntaxKind . ContinueKeyword ) ;
2338
+ }
2339
+ }
2340
+ // If not, we are free to add it if we haven't lost ownership of unlabeled break/continue statements.
2341
+ else if ( ! onlyCheckLabelled ) {
2342
+ pushKeywordIf ( keywords , node . getFirstToken ( ) , SyntaxKind . BreakKeyword , SyntaxKind . ContinueKeyword ) ;
2343
+ }
2344
+ break ;
2345
+
2346
+ case SyntaxKind . ForStatement :
2347
+ case SyntaxKind . ForInStatement :
2348
+ case SyntaxKind . DoStatement :
2349
+ case SyntaxKind . WhileStatement :
2350
+ case SyntaxKind . SwitchStatement :
2351
+ onlyCheckLabelled = true ;
2352
+ // Fall through
2353
+ default :
2354
+ // Do not cross function boundaries.
2355
+ if ( ! isAnyFunction ( node ) ) {
2356
+ forEachChild ( node , aggregateBreakContinues ) ;
2357
+ }
2358
+ }
2359
+ // Restore the last state.
2360
+ onlyCheckLabelled = lastOnlyCheckLabelled ;
2361
+ } ) ;
2362
+
2363
+ return map ( keywords , keywordToReferenceEntry ) ;
2364
+ }
2365
+
2284
2366
function getSwitchCaseDefaultOccurrences ( switchStatement : SwitchStatement ) {
2285
2367
var keywords : Node [ ] = [ ] ;
2286
2368
@@ -2317,28 +2399,30 @@ module ts {
2317
2399
return map ( keywords , getReferenceEntryFromNode ) ;
2318
2400
}
2319
2401
2320
- function getBreakStatementOccurences ( breakStatement : BreakOrContinueStatement ) : ReferenceEntry [ ] {
2321
- // TODO (drosen): Deal with labeled statements.
2322
- if ( breakStatement . label ) {
2323
- return undefined ;
2324
- }
2325
-
2402
+ function getBreakOrContinueStatementOccurences ( breakOrContinueStatement : BreakOrContinueStatement ) : ReferenceEntry [ ] {
2326
2403
for ( var owner = node . parent ; owner ; owner = owner . parent ) {
2327
2404
switch ( owner . kind ) {
2328
2405
case SyntaxKind . ForStatement :
2329
2406
case SyntaxKind . ForInStatement :
2330
2407
case SyntaxKind . DoStatement :
2331
2408
case SyntaxKind . WhileStatement :
2332
- // TODO (drosen): Handle loops!
2333
- return undefined ;
2334
-
2409
+ // The iteration statement is the owner if the break/continue statement is either unlabeled,
2410
+ // or if the break/continue statement's label corresponds to one of the loop's labels.
2411
+ if ( ! breakOrContinueStatement . label || isLabelledBy ( owner , breakOrContinueStatement . label . text ) ) {
2412
+ return getLoopBreakContinueOccurrences ( < IterationStatement > owner )
2413
+ }
2414
+ break ;
2335
2415
case SyntaxKind . SwitchStatement :
2336
- return getSwitchCaseDefaultOccurrences ( < SwitchStatement > owner ) ;
2337
-
2416
+ // A switch statement can only be the owner of an unlabeled break statement.
2417
+ if ( breakOrContinueStatement . kind === SyntaxKind . BreakStatement && ! breakOrContinueStatement . label ) {
2418
+ return getSwitchCaseDefaultOccurrences ( < SwitchStatement > owner ) ;
2419
+ }
2420
+ break ;
2338
2421
default :
2339
2422
if ( isAnyFunction ( owner ) ) {
2340
2423
return undefined ;
2341
2424
}
2425
+ break ;
2342
2426
}
2343
2427
}
2344
2428
@@ -2347,7 +2431,7 @@ module ts {
2347
2431
2348
2432
// returns true if 'node' is defined and has a matching 'kind'.
2349
2433
function hasKind ( node : Node , kind : SyntaxKind ) {
2350
- return ! ! ( node && node . kind === kind ) ;
2434
+ return node !== undefined && node . kind === kind ;
2351
2435
}
2352
2436
2353
2437
// Null-propagating 'parent' function.
0 commit comments