@@ -129,8 +129,8 @@ namespace ts {
129
129
let Symbol : { new ( flags : SymbolFlags , name : string ) : Symbol } ;
130
130
let classifiableNames : Map < string > ;
131
131
132
- const unreachableFlow : FlowNode = { kind : FlowKind . Unreachable } ;
133
- const reportedUnreachableFlow : FlowNode = { kind : FlowKind . Unreachable } ;
132
+ const unreachableFlow : FlowNode = { flags : FlowFlags . Unreachable } ;
133
+ const reportedUnreachableFlow : FlowNode = { flags : FlowFlags . Unreachable } ;
134
134
135
135
function bindSourceFile ( f : SourceFile , opts : CompilerOptions ) {
136
136
file = f ;
@@ -471,7 +471,7 @@ namespace ts {
471
471
savedActiveLabels = activeLabels ;
472
472
473
473
hasExplicitReturn = false ;
474
- currentFlow = { kind : FlowKind . Start } ;
474
+ currentFlow = { flags : FlowFlags . Start } ;
475
475
currentBreakTarget = undefined ;
476
476
currentContinueTarget = undefined ;
477
477
activeLabels = undefined ;
@@ -483,7 +483,7 @@ namespace ts {
483
483
484
484
bindReachableStatement ( node ) ;
485
485
486
- if ( currentFlow . kind !== FlowKind . Unreachable && isFunctionLikeKind ( kind ) && nodeIsPresent ( ( < FunctionLikeDeclaration > node ) . body ) ) {
486
+ if ( ! ( currentFlow . flags & FlowFlags . Unreachable ) && isFunctionLikeKind ( kind ) && nodeIsPresent ( ( < FunctionLikeDeclaration > node ) . body ) ) {
487
487
flags |= NodeFlags . HasImplicitReturn ;
488
488
if ( hasExplicitReturn ) {
489
489
flags |= NodeFlags . HasExplicitReturn ;
@@ -639,55 +639,80 @@ namespace ts {
639
639
return false ;
640
640
}
641
641
642
- function createFlowLabel ( ) : FlowLabel {
642
+ function createBranchLabel ( ) : FlowLabel {
643
643
return {
644
- kind : FlowKind . Label ,
644
+ flags : FlowFlags . BranchLabel ,
645
645
antecedents : undefined
646
646
} ;
647
647
}
648
648
649
- function createFlowLoopLabel ( ) : FlowLabel {
649
+ function createLoopLabel ( ) : FlowLabel {
650
650
return {
651
- kind : FlowKind . LoopLabel ,
651
+ flags : FlowFlags . LoopLabel ,
652
652
antecedents : undefined
653
653
} ;
654
654
}
655
655
656
+ function setFlowNodeReferenced ( flow : FlowNode ) {
657
+ // On first reference we set the Referenced flag, thereafter we set the Shared flag
658
+ flow . flags |= flow . flags & FlowFlags . Referenced ? FlowFlags . Shared : FlowFlags . Referenced ;
659
+ }
660
+
656
661
function addAntecedent ( label : FlowLabel , antecedent : FlowNode ) : void {
657
- if ( antecedent . kind !== FlowKind . Unreachable && ! contains ( label . antecedents , antecedent ) ) {
662
+ if ( ! ( antecedent . flags & FlowFlags . Unreachable ) && ! contains ( label . antecedents , antecedent ) ) {
658
663
( label . antecedents || ( label . antecedents = [ ] ) ) . push ( antecedent ) ;
664
+ setFlowNodeReferenced ( antecedent ) ;
659
665
}
660
666
}
661
667
662
- function createFlowCondition ( antecedent : FlowNode , expression : Expression , assumeTrue : boolean ) : FlowNode {
663
- if ( antecedent . kind === FlowKind . Unreachable ) {
668
+ function createFlowCondition ( flags : FlowFlags , antecedent : FlowNode , expression : Expression ) : FlowNode {
669
+ if ( antecedent . flags & FlowFlags . Unreachable ) {
664
670
return antecedent ;
665
671
}
666
672
if ( ! expression ) {
667
- return assumeTrue ? antecedent : unreachableFlow ;
673
+ return flags & FlowFlags . TrueCondition ? antecedent : unreachableFlow ;
668
674
}
669
- if ( expression . kind === SyntaxKind . TrueKeyword && ! assumeTrue || expression . kind === SyntaxKind . FalseKeyword && assumeTrue ) {
675
+ if ( expression . kind === SyntaxKind . TrueKeyword && flags & FlowFlags . FalseCondition ||
676
+ expression . kind === SyntaxKind . FalseKeyword && flags & FlowFlags . TrueCondition ) {
670
677
return unreachableFlow ;
671
678
}
672
679
if ( ! isNarrowingExpression ( expression ) ) {
673
680
return antecedent ;
674
681
}
682
+ setFlowNodeReferenced ( antecedent ) ;
675
683
return < FlowCondition > {
676
- kind : FlowKind . Condition ,
684
+ flags ,
677
685
antecedent,
678
686
expression,
679
- assumeTrue
680
687
} ;
681
688
}
682
689
683
690
function createFlowAssignment ( antecedent : FlowNode , node : Expression | VariableDeclaration | BindingElement ) : FlowNode {
691
+ setFlowNodeReferenced ( antecedent ) ;
684
692
return < FlowAssignment > {
685
- kind : FlowKind . Assignment ,
693
+ flags : FlowFlags . Assignment ,
686
694
antecedent,
687
695
node
688
696
} ;
689
697
}
690
698
699
+ function skipSimpleConditionalFlow ( flow : FlowNode ) {
700
+ // We skip over simple conditional flows of the form 'x ? aaa : bbb', where 'aaa' and 'bbb' contain
701
+ // no constructs that affect control flow type analysis. Such simple flows have no effect on the
702
+ // code paths that follow and ignoring them means we'll do less work.
703
+ if ( flow . flags & FlowFlags . BranchLabel && ( < FlowLabel > flow ) . antecedents . length === 2 ) {
704
+ const a = ( < FlowLabel > flow ) . antecedents [ 0 ] ;
705
+ const b = ( < FlowLabel > flow ) . antecedents [ 1 ] ;
706
+ if ( ( a . flags & FlowFlags . TrueCondition && b . flags & FlowFlags . FalseCondition ||
707
+ a . flags & FlowFlags . FalseCondition && b . flags & FlowFlags . TrueCondition ) &&
708
+ ( < FlowCondition > a ) . antecedent === ( < FlowCondition > b ) . antecedent &&
709
+ ( < FlowCondition > a ) . expression === ( < FlowCondition > b ) . expression ) {
710
+ return ( < FlowCondition > a ) . antecedent ;
711
+ }
712
+ }
713
+ return flow ;
714
+ }
715
+
691
716
function finishFlowLabel ( flow : FlowLabel ) : FlowNode {
692
717
const antecedents = flow . antecedents ;
693
718
if ( ! antecedents ) {
@@ -696,7 +721,7 @@ namespace ts {
696
721
if ( antecedents . length === 1 ) {
697
722
return antecedents [ 0 ] ;
698
723
}
699
- return flow ;
724
+ return skipSimpleConditionalFlow ( flow ) ;
700
725
}
701
726
702
727
function isStatementCondition ( node : Node ) {
@@ -747,8 +772,8 @@ namespace ts {
747
772
currentTrueTarget = saveTrueTarget ;
748
773
currentFalseTarget = saveFalseTarget ;
749
774
if ( ! node || ! isLogicalExpression ( node ) ) {
750
- addAntecedent ( trueTarget , createFlowCondition ( currentFlow , node , /*assumeTrue*/ true ) ) ;
751
- addAntecedent ( falseTarget , createFlowCondition ( currentFlow , node , /*assumeTrue*/ false ) ) ;
775
+ addAntecedent ( trueTarget , createFlowCondition ( FlowFlags . TrueCondition , currentFlow , node ) ) ;
776
+ addAntecedent ( falseTarget , createFlowCondition ( FlowFlags . FalseCondition , currentFlow , node ) ) ;
752
777
}
753
778
}
754
779
@@ -763,9 +788,9 @@ namespace ts {
763
788
}
764
789
765
790
function bindWhileStatement ( node : WhileStatement ) : void {
766
- const preWhileLabel = createFlowLoopLabel ( ) ;
767
- const preBodyLabel = createFlowLabel ( ) ;
768
- const postWhileLabel = createFlowLabel ( ) ;
791
+ const preWhileLabel = createLoopLabel ( ) ;
792
+ const preBodyLabel = createBranchLabel ( ) ;
793
+ const postWhileLabel = createBranchLabel ( ) ;
769
794
addAntecedent ( preWhileLabel , currentFlow ) ;
770
795
currentFlow = preWhileLabel ;
771
796
bindCondition ( node . expression , preBodyLabel , postWhileLabel ) ;
@@ -776,9 +801,9 @@ namespace ts {
776
801
}
777
802
778
803
function bindDoStatement ( node : DoStatement ) : void {
779
- const preDoLabel = createFlowLoopLabel ( ) ;
780
- const preConditionLabel = createFlowLabel ( ) ;
781
- const postDoLabel = createFlowLabel ( ) ;
804
+ const preDoLabel = createLoopLabel ( ) ;
805
+ const preConditionLabel = createBranchLabel ( ) ;
806
+ const postDoLabel = createBranchLabel ( ) ;
782
807
addAntecedent ( preDoLabel , currentFlow ) ;
783
808
currentFlow = preDoLabel ;
784
809
bindIterativeStatement ( node . statement , postDoLabel , preConditionLabel ) ;
@@ -789,9 +814,9 @@ namespace ts {
789
814
}
790
815
791
816
function bindForStatement ( node : ForStatement ) : void {
792
- const preLoopLabel = createFlowLoopLabel ( ) ;
793
- const preBodyLabel = createFlowLabel ( ) ;
794
- const postLoopLabel = createFlowLabel ( ) ;
817
+ const preLoopLabel = createLoopLabel ( ) ;
818
+ const preBodyLabel = createBranchLabel ( ) ;
819
+ const postLoopLabel = createBranchLabel ( ) ;
795
820
bind ( node . initializer ) ;
796
821
addAntecedent ( preLoopLabel , currentFlow ) ;
797
822
currentFlow = preLoopLabel ;
@@ -804,8 +829,8 @@ namespace ts {
804
829
}
805
830
806
831
function bindForInOrForOfStatement ( node : ForInStatement | ForOfStatement ) : void {
807
- const preLoopLabel = createFlowLoopLabel ( ) ;
808
- const postLoopLabel = createFlowLabel ( ) ;
832
+ const preLoopLabel = createLoopLabel ( ) ;
833
+ const postLoopLabel = createBranchLabel ( ) ;
809
834
addAntecedent ( preLoopLabel , currentFlow ) ;
810
835
currentFlow = preLoopLabel ;
811
836
bind ( node . expression ) ;
@@ -820,9 +845,9 @@ namespace ts {
820
845
}
821
846
822
847
function bindIfStatement ( node : IfStatement ) : void {
823
- const thenLabel = createFlowLabel ( ) ;
824
- const elseLabel = createFlowLabel ( ) ;
825
- const postIfLabel = createFlowLabel ( ) ;
848
+ const thenLabel = createBranchLabel ( ) ;
849
+ const elseLabel = createBranchLabel ( ) ;
850
+ const postIfLabel = createBranchLabel ( ) ;
826
851
bindCondition ( node . expression , thenLabel , elseLabel ) ;
827
852
currentFlow = finishFlowLabel ( thenLabel ) ;
828
853
bind ( node . thenStatement ) ;
@@ -875,7 +900,7 @@ namespace ts {
875
900
}
876
901
877
902
function bindTryStatement ( node : TryStatement ) : void {
878
- const postFinallyLabel = createFlowLabel ( ) ;
903
+ const postFinallyLabel = createBranchLabel ( ) ;
879
904
const preTryFlow = currentFlow ;
880
905
// TODO: Every statement in try block is potentially an exit point!
881
906
bind ( node . tryBlock ) ;
@@ -893,7 +918,7 @@ namespace ts {
893
918
}
894
919
895
920
function bindSwitchStatement ( node : SwitchStatement ) : void {
896
- const postSwitchLabel = createFlowLabel ( ) ;
921
+ const postSwitchLabel = createBranchLabel ( ) ;
897
922
bind ( node . expression ) ;
898
923
const saveBreakTarget = currentBreakTarget ;
899
924
const savePreSwitchCaseFlow = preSwitchCaseFlow ;
@@ -915,17 +940,17 @@ namespace ts {
915
940
for ( let i = 0 ; i < clauses . length ; i ++ ) {
916
941
const clause = clauses [ i ] ;
917
942
if ( clause . statements . length ) {
918
- if ( currentFlow . kind === FlowKind . Unreachable ) {
943
+ if ( currentFlow . flags & FlowFlags . Unreachable ) {
919
944
currentFlow = preSwitchCaseFlow ;
920
945
}
921
946
else {
922
- const preCaseLabel = createFlowLabel ( ) ;
947
+ const preCaseLabel = createBranchLabel ( ) ;
923
948
addAntecedent ( preCaseLabel , preSwitchCaseFlow ) ;
924
949
addAntecedent ( preCaseLabel , currentFlow ) ;
925
950
currentFlow = finishFlowLabel ( preCaseLabel ) ;
926
951
}
927
952
bind ( clause ) ;
928
- if ( currentFlow . kind !== FlowKind . Unreachable && i !== clauses . length - 1 && options . noFallthroughCasesInSwitch ) {
953
+ if ( ! ( currentFlow . flags & FlowFlags . Unreachable ) && i !== clauses . length - 1 && options . noFallthroughCasesInSwitch ) {
929
954
errorOnFirstToken ( clause , Diagnostics . Fallthrough_case_in_switch ) ;
930
955
}
931
956
}
@@ -951,8 +976,8 @@ namespace ts {
951
976
}
952
977
953
978
function bindLabeledStatement ( node : LabeledStatement ) : void {
954
- const preStatementLabel = createFlowLoopLabel ( ) ;
955
- const postStatementLabel = createFlowLabel ( ) ;
979
+ const preStatementLabel = createLoopLabel ( ) ;
980
+ const postStatementLabel = createBranchLabel ( ) ;
956
981
bind ( node . label ) ;
957
982
addAntecedent ( preStatementLabel , currentFlow ) ;
958
983
const activeLabel = pushActiveLabel ( node . label . text , postStatementLabel , preStatementLabel ) ;
@@ -1001,7 +1026,7 @@ namespace ts {
1001
1026
}
1002
1027
1003
1028
function bindLogicalExpression ( node : BinaryExpression , trueTarget : FlowLabel , falseTarget : FlowLabel ) {
1004
- const preRightLabel = createFlowLabel ( ) ;
1029
+ const preRightLabel = createBranchLabel ( ) ;
1005
1030
if ( node . operatorToken . kind === SyntaxKind . AmpersandAmpersandToken ) {
1006
1031
bindCondition ( node . left , preRightLabel , falseTarget ) ;
1007
1032
}
@@ -1031,7 +1056,7 @@ namespace ts {
1031
1056
const operator = node . operatorToken . kind ;
1032
1057
if ( operator === SyntaxKind . AmpersandAmpersandToken || operator === SyntaxKind . BarBarToken ) {
1033
1058
if ( isTopLevelLogicalExpression ( node ) ) {
1034
- const postExpressionLabel = createFlowLabel ( ) ;
1059
+ const postExpressionLabel = createBranchLabel ( ) ;
1035
1060
bindLogicalExpression ( node , postExpressionLabel , postExpressionLabel ) ;
1036
1061
currentFlow = finishFlowLabel ( postExpressionLabel ) ;
1037
1062
}
@@ -1048,9 +1073,9 @@ namespace ts {
1048
1073
}
1049
1074
1050
1075
function bindConditionalExpressionFlow ( node : ConditionalExpression ) {
1051
- const trueLabel = createFlowLabel ( ) ;
1052
- const falseLabel = createFlowLabel ( ) ;
1053
- const postExpressionLabel = createFlowLabel ( ) ;
1076
+ const trueLabel = createBranchLabel ( ) ;
1077
+ const falseLabel = createBranchLabel ( ) ;
1078
+ const postExpressionLabel = createBranchLabel ( ) ;
1054
1079
bindCondition ( node . condition , trueLabel , falseLabel ) ;
1055
1080
currentFlow = finishFlowLabel ( trueLabel ) ;
1056
1081
bind ( node . whenTrue ) ;
@@ -2065,7 +2090,7 @@ namespace ts {
2065
2090
}
2066
2091
2067
2092
function checkUnreachable ( node : Node ) : boolean {
2068
- if ( currentFlow . kind !== FlowKind . Unreachable ) {
2093
+ if ( ! ( currentFlow . flags & FlowFlags . Unreachable ) ) {
2069
2094
return false ;
2070
2095
}
2071
2096
if ( currentFlow === unreachableFlow ) {
0 commit comments