@@ -1359,11 +1359,19 @@ module ts {
1359
1359
}
1360
1360
1361
1361
enum SearchMeaning {
1362
+ None = 0x0 ,
1362
1363
Value = 0x1 ,
1363
1364
Type = 0x2 ,
1364
1365
Namespace = 0x4
1365
1366
}
1366
1367
1368
+ enum BreakContinueSearchType {
1369
+ None = 0x0 ,
1370
+ Unlabeled = 0x1 ,
1371
+ Labeled = 0x2 ,
1372
+ All = Unlabeled | Labeled
1373
+ }
1374
+
1367
1375
// A cache of completion entries for keywords, these do not change between sessions
1368
1376
var keywordCompletions :CompletionEntry [ ] = [ ] ;
1369
1377
for ( var i = SyntaxKind . FirstKeyword ; i <= SyntaxKind . LastKeyword ; i ++ ) {
@@ -2318,88 +2326,96 @@ module ts {
2318
2326
}
2319
2327
}
2320
2328
}
2329
+
2330
+ // These track whether we can own unlabeled break/continues.
2331
+ var breakSearchType = BreakContinueSearchType . All ;
2332
+ var continueSearchType = BreakContinueSearchType . All ;
2321
2333
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 ;
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 ;
2329
2338
2330
2339
switch ( node . kind ) {
2331
2340
case SyntaxKind . BreakStatement :
2332
2341
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
+ if ( ownsBreakOrContinue ( loopNode , < BreakOrContinueStatement > node , breakSearchType , continueSearchType ) ) {
2342
2343
pushKeywordIf ( keywords , node . getFirstToken ( ) , SyntaxKind . BreakKeyword , SyntaxKind . ContinueKeyword ) ;
2343
2344
}
2344
2345
break ;
2345
-
2346
+
2346
2347
case SyntaxKind . ForStatement :
2347
2348
case SyntaxKind . ForInStatement :
2348
2349
case SyntaxKind . DoStatement :
2349
2350
case SyntaxKind . WhileStatement :
2350
- case SyntaxKind . SwitchStatement :
2351
- onlyCheckLabelled = true ;
2351
+ continueSearchType = BreakContinueSearchType . Labeled ;
2352
2352
// Fall through
2353
- default :
2354
- // Do not cross function boundaries.
2355
- if ( ! isAnyFunction ( node ) ) {
2356
- forEachChild ( node , aggregateBreakContinues ) ;
2357
- }
2353
+ case SyntaxKind . SwitchStatement :
2354
+ breakSearchType = BreakContinueSearchType . Labeled ;
2355
+ }
2356
+
2357
+ // Do not cross function boundaries.
2358
+ if ( ! isAnyFunction ( node ) ) {
2359
+ forEachChild ( node , aggregateBreakContinues ) ;
2358
2360
}
2361
+
2359
2362
// Restore the last state.
2360
- onlyCheckLabelled = lastOnlyCheckLabelled ;
2361
- } ) ;
2363
+ breakSearchType = prevBreakSearchType ;
2364
+ continueSearchType = prevContinueSearchType ;
2365
+ } ) ( loopNode . statement ) ;
2362
2366
2363
- return map ( keywords , keywordToReferenceEntry ) ;
2367
+ return map ( keywords , getReferenceEntryFromNode ) ;
2364
2368
}
2365
2369
2366
2370
function getSwitchCaseDefaultOccurrences ( switchStatement : SwitchStatement ) {
2367
2371
var keywords : Node [ ] = [ ] ;
2368
2372
2369
2373
pushKeywordIf ( keywords , switchStatement . getFirstToken ( ) , SyntaxKind . SwitchKeyword ) ;
2370
2374
2371
- // Go through each clause in the switch statement, collecting the clause keywords.
2375
+ // Types of break statements we can grab on to.
2376
+ var breakSearchType = BreakContinueSearchType . All ;
2377
+
2378
+ // Go through each clause in the switch statement, collecting the case/default keywords.
2372
2379
forEach ( switchStatement . clauses , clause => {
2373
2380
pushKeywordIf ( keywords , clause . getFirstToken ( ) , SyntaxKind . CaseKeyword , SyntaxKind . DefaultKeyword ) ;
2374
2381
2375
2382
// For each clause, also recursively traverse the statements where we can find analogous breaks.
2376
2383
forEachChild ( clause , function aggregateBreakKeywords ( node : Node ) : void {
2384
+ // Back the old search value up.
2385
+ var oldBreakSearchType = breakSearchType ;
2386
+
2377
2387
switch ( node . kind ) {
2378
2388
case SyntaxKind . BreakStatement :
2379
2389
// If the break statement has a label, it cannot be part of a switch block.
2380
- if ( ! ( < BreakOrContinueStatement > node ) . label ) {
2390
+ if ( ownsBreakOrContinue ( switchStatement ,
2391
+ < BreakOrContinueStatement > node ,
2392
+ breakSearchType ,
2393
+ /*continuesSearchType*/ BreakContinueSearchType . None ) ) {
2381
2394
pushKeywordIf ( keywords , node . getFirstToken ( ) , SyntaxKind . BreakKeyword ) ;
2382
2395
}
2383
- // Fall through
2396
+ break ;
2384
2397
case SyntaxKind . ForStatement :
2385
2398
case SyntaxKind . ForInStatement :
2386
2399
case SyntaxKind . DoStatement :
2387
2400
case SyntaxKind . WhileStatement :
2388
2401
case SyntaxKind . SwitchStatement :
2389
- return ;
2402
+ breakSearchType = BreakContinueSearchType . Labeled ;
2390
2403
}
2391
2404
2392
2405
// Do not cross function boundaries.
2393
2406
if ( ! isAnyFunction ( node ) ) {
2394
2407
forEachChild ( node , aggregateBreakKeywords ) ;
2395
2408
}
2409
+
2410
+ // Restore the last state.
2411
+ breakSearchType = oldBreakSearchType ;
2396
2412
} ) ;
2397
2413
} ) ;
2398
2414
2399
2415
return map ( keywords , getReferenceEntryFromNode ) ;
2400
2416
}
2401
2417
2402
- function getBreakOrContinueStatementOccurences ( breakOrContinueStatement : BreakOrContinueStatement ) : ReferenceEntry [ ] {
2418
+ function getBreakOrContinueStatementOccurences ( breakOrContinueStatement : BreakOrContinueStatement ) : ReferenceEntry [ ] {
2403
2419
for ( var owner = node . parent ; owner ; owner = owner . parent ) {
2404
2420
switch ( owner . kind ) {
2405
2421
case SyntaxKind . ForStatement :
@@ -2413,8 +2429,8 @@ module ts {
2413
2429
}
2414
2430
break ;
2415
2431
case SyntaxKind . SwitchStatement :
2416
- // A switch statement can only be the owner of an unlabeled break statement.
2417
- if ( breakOrContinueStatement . kind === SyntaxKind . BreakStatement && ! breakOrContinueStatement . label ) {
2432
+ // A switch statement can only be the owner of an break statement.
2433
+ if ( breakOrContinueStatement . kind === SyntaxKind . BreakStatement && ( ! breakOrContinueStatement . label || isLabelledBy ( owner , breakOrContinueStatement . label . text ) ) ) {
2418
2434
return getSwitchCaseDefaultOccurrences ( < SwitchStatement > owner ) ;
2419
2435
}
2420
2436
break ;
@@ -2429,6 +2445,25 @@ module ts {
2429
2445
return undefined ;
2430
2446
}
2431
2447
2448
+ // Note: 'statement' must be a descendant of 'root'.
2449
+ // Reasonable logic for restricting traversal prior to arriving at the
2450
+ // 'statement' node is beyond the scope of this function.
2451
+ function ownsBreakOrContinue ( root : Node ,
2452
+ statement : BreakOrContinueStatement ,
2453
+ breakSearchType : BreakContinueSearchType ,
2454
+ continueSearchType : BreakContinueSearchType ) : boolean {
2455
+ var searchType = statement . kind === SyntaxKind . BreakStatement ?
2456
+ breakSearchType :
2457
+ continueSearchType ;
2458
+
2459
+ if ( statement . label ) {
2460
+ return isLabelledBy ( root , statement . label . text ) ;
2461
+ }
2462
+ else {
2463
+ return ! ! ( searchType & BreakContinueSearchType . Unlabeled ) ;
2464
+ }
2465
+ }
2466
+
2432
2467
// returns true if 'node' is defined and has a matching 'kind'.
2433
2468
function hasKind ( node : Node , kind : SyntaxKind ) {
2434
2469
return node !== undefined && node . kind === kind ;
0 commit comments