@@ -2635,6 +2635,11 @@ module ts {
26352635 return getReturnOccurrences ( < ReturnStatement > node . parent ) ;
26362636 }
26372637 break ;
2638+ case SyntaxKind . ThrowKeyword :
2639+ if ( hasKind ( node . parent , SyntaxKind . ThrowStatement ) ) {
2640+ return getThrowOccurrences ( < ThrowStatement > node . parent ) ;
2641+ }
2642+ break ;
26382643 case SyntaxKind . TryKeyword :
26392644 case SyntaxKind . CatchKeyword :
26402645 case SyntaxKind . FinallyKeyword :
@@ -2752,12 +2757,108 @@ module ts {
27522757 }
27532758
27542759 var keywords : Node [ ] = [ ]
2755- forEachReturnStatement ( < Block > ( < FunctionDeclaration > func ) . body , returnStatement => {
2760+ forEachReturnStatement ( < Block > func . body , returnStatement => {
27562761 pushKeywordIf ( keywords , returnStatement . getFirstToken ( ) , SyntaxKind . ReturnKeyword ) ;
27572762 } ) ;
27582763
2764+ // Include 'throw' statements that do not occur within a try block.
2765+ forEach ( aggregateOwnedThrowStatements ( func . body ) , throwStatement => {
2766+ pushKeywordIf ( keywords , throwStatement . getFirstToken ( ) , SyntaxKind . ThrowKeyword ) ;
2767+ } ) ;
2768+
27592769 return map ( keywords , getReferenceEntryFromNode ) ;
27602770 }
2771+
2772+ function getThrowOccurrences ( throwStatement : ThrowStatement ) {
2773+ var owner = getThrowStatementOwner ( throwStatement ) ;
2774+
2775+ if ( ! owner ) {
2776+ return undefined ;
2777+ }
2778+
2779+ var keywords : Node [ ] = [ ] ;
2780+
2781+ forEach ( aggregateOwnedThrowStatements ( owner ) , throwStatement => {
2782+ pushKeywordIf ( keywords , throwStatement . getFirstToken ( ) , SyntaxKind . ThrowKeyword ) ;
2783+ } ) ;
2784+
2785+ // If the "owner" is a function, then we equate 'return' and 'throw' statements in their
2786+ // ability to "jump out" of the function, and include occurrences for both.
2787+ if ( owner . kind === SyntaxKind . FunctionBlock ) {
2788+ forEachReturnStatement ( < Block > owner , returnStatement => {
2789+ pushKeywordIf ( keywords , returnStatement . getFirstToken ( ) , SyntaxKind . ReturnKeyword ) ;
2790+ } ) ;
2791+ }
2792+
2793+ return map ( keywords , getReferenceEntryFromNode ) ;
2794+ }
2795+
2796+ /**
2797+ * Aggregates all throw-statements within this node *without* crossing
2798+ * into function boundaries and try-blocks with catch-clauses.
2799+ */
2800+ function aggregateOwnedThrowStatements ( node : Node ) : ThrowStatement [ ] {
2801+ var statementAccumulator : ThrowStatement [ ] = [ ]
2802+ aggregate ( node ) ;
2803+ return statementAccumulator ;
2804+
2805+ function aggregate ( node : Node ) : void {
2806+ if ( node . kind === SyntaxKind . ThrowStatement ) {
2807+ statementAccumulator . push ( < ThrowStatement > node ) ;
2808+ }
2809+ else if ( node . kind === SyntaxKind . TryStatement ) {
2810+ var tryStatement = < TryStatement > node ;
2811+
2812+ if ( tryStatement . catchBlock ) {
2813+ aggregate ( tryStatement . catchBlock ) ;
2814+ }
2815+ else {
2816+ // Exceptions thrown within a try block lacking a catch clause
2817+ // are "owned" in the current context.
2818+ aggregate ( tryStatement . tryBlock ) ;
2819+ }
2820+
2821+ if ( tryStatement . finallyBlock ) {
2822+ aggregate ( tryStatement . finallyBlock ) ;
2823+ }
2824+ }
2825+ // Do not cross function boundaries.
2826+ else if ( ! isAnyFunction ( node ) ) {
2827+ forEachChild ( node , aggregate ) ;
2828+ }
2829+ } ;
2830+ }
2831+
2832+ /**
2833+ * For lack of a better name, this function takes a throw statement and returns the
2834+ * nearest ancestor that is a try-block (whose try statement has a catch clause),
2835+ * function-block, or source file.
2836+ */
2837+ function getThrowStatementOwner ( throwStatement : ThrowStatement ) : Node {
2838+ var child : Node = throwStatement ;
2839+
2840+ while ( child . parent ) {
2841+ var parent = child . parent ;
2842+
2843+ if ( parent . kind === SyntaxKind . FunctionBlock || parent . kind === SyntaxKind . SourceFile ) {
2844+ return parent ;
2845+ }
2846+
2847+ // A throw-statement is only owned by a try-statement if the try-statement has
2848+ // a catch clause, and if the throw-statement occurs within the try block.
2849+ if ( parent . kind === SyntaxKind . TryStatement ) {
2850+ var tryStatement = < TryStatement > parent ;
2851+
2852+ if ( tryStatement . tryBlock === child && tryStatement . catchBlock ) {
2853+ return child ;
2854+ }
2855+ }
2856+
2857+ child = parent ;
2858+ }
2859+
2860+ return undefined ;
2861+ }
27612862
27622863 function getTryCatchFinallyOccurrences ( tryStatement : TryStatement ) : ReferenceEntry [ ] {
27632864 var keywords : Node [ ] = [ ] ;
0 commit comments