From 3ec5eb17bc4298081f3c8fe7753aa90a31e13dcb Mon Sep 17 00:00:00 2001
From: Vladimir Matveev <vladima@microsoft.com>
Date: Fri, 7 Nov 2014 22:21:40 -0800
Subject: [PATCH 1/7] initial rev of controlflow analysis

---
 Jakefile                                      |   2 +-
 src/compiler/checker.ts                       |   7 +-
 src/compiler/controlflow.ts                   | 316 ++++++++++++++++++
 .../diagnosticInformationMap.generated.ts     |   4 +
 src/compiler/diagnosticMessages.json          |  17 +-
 5 files changed, 342 insertions(+), 4 deletions(-)
 create mode 100644 src/compiler/controlflow.ts

diff --git a/Jakefile b/Jakefile
index 0a34de4182b0c..7f514f1e96399 100644
--- a/Jakefile
+++ b/Jakefile
@@ -252,7 +252,7 @@ var tscFile = path.join(builtLocalDirectory, compilerFilename);
 compileFile(tscFile, compilerSources, [builtLocalDirectory, copyright].concat(compilerSources), [copyright], /*useBuiltCompiler:*/ false);
 
 var servicesFile = path.join(builtLocalDirectory, "typescriptServices.js");
-compileFile(servicesFile, servicesSources, [builtLocalDirectory, copyright].concat(servicesSources), [copyright], /*useBuiltCompiler:*/ true);
+compileFile(servicesFile, servicesSources, [builtLocalDirectory, copyright].concat(servicesSources), [copyright], /*useBuiltCompiler:*/ false);
 
 // Local target to build the compiler and services
 desc("Builds the full compiler and services");
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index cd7ada607f96a..5c9b0d81e7e10 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -4,6 +4,7 @@
 /// <reference path="parser.ts"/>
 /// <reference path="binder.ts"/>
 /// <reference path="emitter.ts"/>
+/// <reference path="controlflow.ts"/>
 
 module ts {
 
@@ -5896,7 +5897,8 @@ module ts {
 
         function checkFunctionExpressionBody(node: FunctionExpression) {
             if (node.type) {
-                checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNode(node.type));
+                checkControlFlowOfFunction(node, getTypeFromTypeNode(node.type) !== voidType, error);
+                // checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNode(node.type));
             }
             if (node.body.kind === SyntaxKind.FunctionBlock) {
                 checkSourceElement(node.body);
@@ -7015,7 +7017,8 @@ module ts {
 
             checkSourceElement(node.body);
             if (node.type && !isAccessor(node.kind)) {
-                checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNode(node.type));
+                checkControlFlowOfFunction(node, getTypeFromTypeNode(node.type) !== voidType, error);
+                // checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNode(node.type));
             }
 
             // If there is no body and no explicit return type, then report an error.
diff --git a/src/compiler/controlflow.ts b/src/compiler/controlflow.ts
new file mode 100644
index 0000000000000..091f70515ead9
--- /dev/null
+++ b/src/compiler/controlflow.ts
@@ -0,0 +1,316 @@
+/// <reference path="types.ts"/>
+
+module ts {
+    export function checkControlFlowOfFunction(decl: FunctionLikeDeclaration, noImplicitReturns: boolean, error: (n: Node, message: DiagnosticMessage, arg0?: any) => void) {
+        if (!decl.body || decl.body.kind !== SyntaxKind.FunctionBlock) {
+            return;
+        }
+
+        var finalState = checkControlFlow(decl.body, error);
+        if (noImplicitReturns && finalState === ControlFlowState.Reachable) {
+            var errorNode: Node = decl.name || decl;
+            error(errorNode, Diagnostics.Not_all_code_paths_return_a_value);
+        }
+    }
+
+    export function checkControlFlowOfBlock(block: Block, error: (n: Node, message: DiagnosticMessage, arg0?: any) => void) {
+        checkControlFlow(block, error);
+    }
+
+    function checkControlFlow(decl: Node, error: (n: Node, message: DiagnosticMessage, arg0?: any) => void): ControlFlowState {
+        var currentState = ControlFlowState.Reachable;
+
+        function setState(newState: ControlFlowState) {
+            currentState = newState;
+        }
+
+        function or(s1: ControlFlowState, s2: ControlFlowState): ControlFlowState {
+            if (s1 === ControlFlowState.Reachable || s2 === ControlFlowState.Reachable) {
+                return ControlFlowState.Reachable;
+            }
+            if (s1 === ControlFlowState.ReportedUnreachable && s2 === ControlFlowState.ReportedUnreachable) {
+                return ControlFlowState.ReportedUnreachable;
+            }
+            return ControlFlowState.Unreachable;
+        }
+
+        function verifyReachable(n: Node): void {
+            if (currentState === ControlFlowState.Unreachable) {
+                error(n, Diagnostics.Unreachable_code_detected);
+                currentState = ControlFlowState.ReportedUnreachable;
+            }
+        }
+
+        // label name -> index in 'labelStack'
+        var labels: Map<number> = {};
+        // CF state at all seen labels
+        var labelStack: ControlFlowState[] = [];
+        // indices of implicit labels in 'labelStack'
+        var implicitLabels: number[] = [];
+
+        function pushNamedLabel(name: Identifier): boolean {
+            if (hasProperty(labels, name.text)) {
+                return false;
+            }
+            var newLen = labelStack.push(ControlFlowState.Uninitialized);
+            labels[name.text] = newLen - 1;
+            return true;
+        }
+
+        function pushImplicitLabel(): number {
+            var newLen = labelStack.push(ControlFlowState.Uninitialized);
+            implicitLabels.push(newLen - 1);
+            return newLen - 1;
+        }
+
+        function setFinalStateAtLabel(mergedStates: ControlFlowState, outerState: ControlFlowState, name: Identifier): void {
+            if (mergedStates === ControlFlowState.Uninitialized) {
+                if (name) {
+                    error(name, Diagnostics.Unused_label);
+                }
+                setState(outerState);
+            }
+            else {
+                setState(or(mergedStates, outerState));
+            }
+        }
+
+        function popNamedLabel(name: Identifier, outerState: ControlFlowState): void {
+            Debug.assert(hasProperty(labels, name.text));
+            var index = labels[name.text];
+            Debug.assert(labelStack.length === index + 1);
+            labels[name.text] = undefined;
+            var mergedStates = labelStack.pop();
+            setFinalStateAtLabel(mergedStates, outerState, name);
+        }
+
+        function popImplicitLabel(index: number, outerState: ControlFlowState): void {
+            Debug.assert(labelStack.length === index + 1);
+            var i = implicitLabels.pop();
+            Debug.assert(index === i);
+            var mergedStates = labelStack.pop();
+            setFinalStateAtLabel(mergedStates, outerState, /*name*/ undefined);
+        }
+
+        function gotoLabel(label: Identifier, outerState: ControlFlowState): void {
+            var stateIndex: number;
+            if (label) {
+                if (!hasProperty(labels, label.text)) {
+                    // reference to non-existing label
+                    return;
+                }
+                stateIndex = labels[label.text];
+            }
+            else {
+                if (implicitLabels.length === 0) {
+                    // non-labeled break\continue being used outside loops
+                    return;
+                }
+                
+                stateIndex = implicitLabels[implicitLabels.length - 1];
+            }
+            var stateAtLabel = labelStack[stateIndex];
+            labelStack[stateIndex] = stateAtLabel === ControlFlowState.Uninitialized ? outerState : or(outerState, stateAtLabel);
+        }
+
+        function checkWhileStatement(n: WhileStatement): void {
+            verifyReachable(n);
+
+            var preWhileState: ControlFlowState = n.expression.kind === SyntaxKind.FalseKeyword ? ControlFlowState.Unreachable : currentState;
+            var postWhileState: ControlFlowState = n.expression.kind === SyntaxKind.TrueKeyword ? ControlFlowState.Unreachable : currentState;
+
+            setState(preWhileState);
+
+            var index = pushImplicitLabel();
+            check(n.statement);
+            popImplicitLabel(index, postWhileState);
+        }
+
+        function checkDoStatement(n: DoStatement): void {
+            verifyReachable(n);
+            var preDoState = currentState;
+
+            var index = pushImplicitLabel();
+            check(n.statement);
+
+            var postDoState = n.expression.kind === SyntaxKind.TrueKeyword ? ControlFlowState.Unreachable : preDoState;
+            popImplicitLabel(index, postDoState);
+        }
+
+        function checkForStatement(n: ForStatement): void {
+            verifyReachable(n);
+
+            var preForState = currentState;
+            var index = pushImplicitLabel();
+            check(n.statement);
+            var postForState = n.declarations || n.initializer || n.condition || n.iterator ? preForState : ControlFlowState.Unreachable;
+            popImplicitLabel(index, postForState);
+        }
+
+        function checkForInStatement(n: ForInStatement): void {
+            verifyReachable(n);
+            var preForInState = currentState;
+            var index = pushImplicitLabel();
+            check(n.statement);
+            popImplicitLabel(index, preForInState);
+        }
+
+        function checkBlock(n: Block): void {
+            forEach(n.statements, check);
+        }
+
+        function checkIfStatement(n: IfStatement): void {
+            var ifTrueState: ControlFlowState = n.expression.kind === SyntaxKind.FalseKeyword ? ControlFlowState.Unreachable : currentState;
+            var ifFalseState: ControlFlowState = n.expression.kind === SyntaxKind.TrueKeyword ? ControlFlowState.Unreachable : currentState;
+
+            setState(ifTrueState);
+            check(n.thenStatement);
+            ifTrueState = currentState;
+
+            setState(ifFalseState);
+            check(n.elseStatement);
+
+            currentState = or(currentState, ifTrueState);
+        }
+
+        function checkReturnOrThrow(n: Node): void {
+            verifyReachable(n);
+            setState(ControlFlowState.Unreachable);
+        }
+
+        function checkBreakOrContinueStatement(n: BreakOrContinueStatement): void {
+            verifyReachable(n);
+            if (n.kind === SyntaxKind.BreakStatement) {
+                gotoLabel(n.label, currentState);
+            }
+            else {
+                gotoLabel(n.label, ControlFlowState.Unreachable); // touch label so it will be marked a used
+            }
+            setState(ControlFlowState.Unreachable);
+        }
+
+        function checkTryStatement(n: TryStatement): void {
+            verifyReachable(n);
+
+            // catch\finally blocks has the same reachability as try block
+            var startState = currentState;
+            check(n.tryBlock);
+            var postTryState = currentState;
+
+            setState(startState);
+            check(n.catchBlock);
+            var postCatchState = currentState;
+
+            setState(startState);
+            check(n.finallyBlock);
+            setState(or(postTryState, postCatchState));
+        }
+
+        function checkSwitchStatement(n: SwitchStatement): void {
+            verifyReachable(n);
+            var startState = currentState;
+            var hasDefault = false;
+
+            var index = pushImplicitLabel();
+
+            forEach(n.clauses, (c: CaseOrDefaultClause) => {
+                hasDefault = hasDefault || c.kind === SyntaxKind.DefaultClause;
+                setState(startState);
+                forEach(c.statements, check);
+                if (c.statements.length && currentState === ControlFlowState.Reachable) {
+                    error(c.expression, Diagnostics.Fallthrough_case_in_switch);
+                }
+            });
+
+            // post switch state is unreachable if switch is exaustive (has a default case ) and does not have fallthrough from the last case
+            var postSwitchState = hasDefault && currentState !== ControlFlowState.Reachable ? ControlFlowState.Unreachable : startState;
+
+            popImplicitLabel(index, postSwitchState);
+        }
+
+        function checkLabelledStatement(n: LabeledStatement): void {
+            verifyReachable(n);
+            var ok = pushNamedLabel(n.label);
+            check(n.statement);
+            if (ok) {
+                popNamedLabel(n.label, currentState);
+            }
+        }
+
+        function checkWithStatement(n: WithStatement): void {
+            verifyReachable(n);
+            check(n.statement);
+        }
+
+        // current assumption: only statements affect CF
+        function check(n: Node): void {
+            if (!n || currentState === ControlFlowState.ReportedUnreachable) {
+                return;
+            }
+            switch (n.kind) {
+                case SyntaxKind.WhileStatement:
+                    checkWhileStatement(<WhileStatement>n);
+                    break;
+                case SyntaxKind.SourceFile:
+                    checkBlock(<SourceFile>n);
+                    break;
+                case SyntaxKind.Block:
+                case SyntaxKind.TryBlock:
+                case SyntaxKind.CatchBlock:
+                case SyntaxKind.FinallyBlock:
+                case SyntaxKind.ModuleBlock:
+                case SyntaxKind.FunctionBlock:
+                    checkBlock(<Block>n);
+                    break;
+                case SyntaxKind.IfStatement:
+                    checkIfStatement(<IfStatement>n);
+                    break;
+                case SyntaxKind.ReturnStatement:
+                case SyntaxKind.ThrowStatement:
+                    checkReturnOrThrow(n);
+                    break;
+                case SyntaxKind.BreakStatement:
+                case SyntaxKind.ContinueStatement:
+                    checkBreakOrContinueStatement(<BreakOrContinueStatement>n);
+                    break;
+                case SyntaxKind.VariableStatement:
+                case SyntaxKind.EmptyStatement:
+                case SyntaxKind.ExpressionStatement:
+                case SyntaxKind.DebuggerStatement:
+                    verifyReachable(n);
+                    break;
+                case SyntaxKind.DoStatement:
+                    checkDoStatement(<DoStatement>n);
+                    break;
+                case SyntaxKind.ForInStatement:
+                    checkForInStatement(<ForInStatement>n);
+                    break;
+                case SyntaxKind.ForStatement:
+                    checkForStatement(<ForStatement>n);
+                    break;
+                case SyntaxKind.LabeledStatement:
+                    checkLabelledStatement(<LabeledStatement>n);
+                    break;
+                case SyntaxKind.SwitchStatement:
+                    checkSwitchStatement(<SwitchStatement>n);
+                    break;
+                case SyntaxKind.TryStatement:
+                    checkTryStatement(<TryStatement>n);
+                    break;
+                case SyntaxKind.WithStatement:
+                    checkWithStatement(<WithStatement>n);
+                    break;
+            }
+        }
+
+        check(decl);
+        return currentState;
+    }
+
+    const enum ControlFlowState {
+        Uninitialized       = 0,
+        Reachable           = 1,
+        Unreachable         = 2,
+        ReportedUnreachable = 3,
+    }
+}
\ No newline at end of file
diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts
index 349601a6db8e5..3eff49e543954 100644
--- a/src/compiler/diagnosticInformationMap.generated.ts
+++ b/src/compiler/diagnosticInformationMap.generated.ts
@@ -270,6 +270,10 @@ module ts {
         Type_argument_candidate_1_is_not_a_valid_type_argument_because_it_is_not_a_supertype_of_candidate_0: { code: 2455, category: DiagnosticCategory.Error, key: "Type argument candidate '{1}' is not a valid type argument because it is not a supertype of candidate '{0}'." },
         Type_alias_0_circularly_references_itself: { code: 2456, category: DiagnosticCategory.Error, key: "Type alias '{0}' circularly references itself." },
         Type_alias_name_cannot_be_0: { code: 2457, category: DiagnosticCategory.Error, key: "Type alias name cannot be '{0}'" },
+        Not_all_code_paths_return_a_value: { code: 2458, category: DiagnosticCategory.Error, key: "Not all code paths return a value" },
+        Unreachable_code_detected: { code: 2459, category: DiagnosticCategory.Error, key: "Unreachable code detected" },
+        Unused_label: { code: 2460, category: DiagnosticCategory.Error, key: "Unused label" },
+        Fallthrough_case_in_switch: { code: 2461, category: DiagnosticCategory.Error, key: "Fallthrough case in switch" },
         Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." },
         Type_parameter_0_of_exported_class_has_or_is_using_name_1_from_private_module_2: { code: 4001, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using name '{1}' from private module '{2}'." },
         Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." },
diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index 7359abefb6048..86789153768a9 100644
--- a/src/compiler/diagnosticMessages.json
+++ b/src/compiler/diagnosticMessages.json
@@ -1076,7 +1076,22 @@
         "category": "Error",
         "code": 2457
     },
-
+    "Not all code paths return a value": {
+        "category": "Error",
+        "code": 2458
+    },
+    "Unreachable code detected": {
+        "category": "Error",
+        "code": 2459
+    },
+    "Unused label": {
+        "category": "Error",
+        "code": 2460
+    },
+    "Fallthrough case in switch": {
+        "category": "Error",
+        "code": 2461
+    },
     "Import declaration '{0}' is using private name '{1}'.": {
         "category": "Error",
         "code": 4000

From 1d6bd0d92f9736bf01d01950bf029e6d2e5316a9 Mon Sep 17 00:00:00 2001
From: Vladimir Matveev <vladima@microsoft.com>
Date: Sun, 30 Nov 2014 19:42:08 -0800
Subject: [PATCH 2/7] addressed CR feedback, exit early after reporting
 'unreachable statement error'

---
 src/compiler/controlflow.ts | 91 +++++++++++++++++++++++++++++--------
 1 file changed, 73 insertions(+), 18 deletions(-)

diff --git a/src/compiler/controlflow.ts b/src/compiler/controlflow.ts
index 091f70515ead9..e9bd83b962d5f 100644
--- a/src/compiler/controlflow.ts
+++ b/src/compiler/controlflow.ts
@@ -34,10 +34,16 @@ module ts {
             return ControlFlowState.Unreachable;
         }
 
-        function verifyReachable(n: Node): void {
-            if (currentState === ControlFlowState.Unreachable) {
-                error(n, Diagnostics.Unreachable_code_detected);
-                currentState = ControlFlowState.ReportedUnreachable;
+        function reportIfNotReachable(n: Node): boolean {
+            switch (currentState) {
+                case ControlFlowState.Unreachable:
+                    error(n, Diagnostics.Unreachable_code_detected);
+                    currentState = ControlFlowState.ReportedUnreachable;
+                    return true;
+                case ControlFlowState.ReportedUnreachable:
+                    return true;
+                default:
+                    return false;
             }
         }
 
@@ -114,10 +120,16 @@ module ts {
         }
 
         function checkWhileStatement(n: WhileStatement): void {
-            verifyReachable(n);
+            if (reportIfNotReachable(n)) {
+                // current state is unreachable.
+                // since nothing downstream can change it - no need to continue
+                return;
+            }
 
-            var preWhileState: ControlFlowState = n.expression.kind === SyntaxKind.FalseKeyword ? ControlFlowState.Unreachable : currentState;
-            var postWhileState: ControlFlowState = n.expression.kind === SyntaxKind.TrueKeyword ? ControlFlowState.Unreachable : currentState;
+            var preWhileState = 
+                n.expression.kind === SyntaxKind.FalseKeyword ? ControlFlowState.Unreachable : currentState;
+            var postWhileState = 
+                n.expression.kind === SyntaxKind.TrueKeyword ? ControlFlowState.Unreachable : currentState;
 
             setState(preWhileState);
 
@@ -127,7 +139,12 @@ module ts {
         }
 
         function checkDoStatement(n: DoStatement): void {
-            verifyReachable(n);
+            if (reportIfNotReachable(n)) {
+                // current state is unreachable.
+                // since nothing downstream can change it - no need to continue
+                return;
+            }
+
             var preDoState = currentState;
 
             var index = pushImplicitLabel();
@@ -138,17 +155,32 @@ module ts {
         }
 
         function checkForStatement(n: ForStatement): void {
-            verifyReachable(n);
+            if (reportIfNotReachable(n)) {
+                // current state is unreachable.
+                // since nothing downstream can change it - no need to continue
+                return;
+            }
 
             var preForState = currentState;
             var index = pushImplicitLabel();
             check(n.statement);
-            var postForState = n.declarations || n.initializer || n.condition || n.iterator ? preForState : ControlFlowState.Unreachable;
+            
+            // for statement is considered infinite when it condition is either omitted or is true keyword
+            // - for(..;;..)
+            // - for(..;true;..)
+            var isInfiniteLoop = (!n.condition || n.condition.kind === SyntaxKind.TrueKeyword);
+
+            var postForState = isInfiniteLoop ? ControlFlowState.Unreachable : preForState;
             popImplicitLabel(index, postForState);
         }
 
         function checkForInStatement(n: ForInStatement): void {
-            verifyReachable(n);
+            if (reportIfNotReachable(n)) {
+                // current state is unreachable.
+                // since nothing downstream can change it - no need to continue
+                return;
+            }
+
             var preForInState = currentState;
             var index = pushImplicitLabel();
             check(n.statement);
@@ -174,12 +206,16 @@ module ts {
         }
 
         function checkReturnOrThrow(n: Node): void {
-            verifyReachable(n);
+            reportIfNotReachable(n);
             setState(ControlFlowState.Unreachable);
         }
 
         function checkBreakOrContinueStatement(n: BreakOrContinueStatement): void {
-            verifyReachable(n);
+            if (reportIfNotReachable(n)) {
+                // current state is unreachable.
+                // since nothing downstream can change it - no need to continue
+                return;
+            }
             if (n.kind === SyntaxKind.BreakStatement) {
                 gotoLabel(n.label, currentState);
             }
@@ -190,7 +226,11 @@ module ts {
         }
 
         function checkTryStatement(n: TryStatement): void {
-            verifyReachable(n);
+            if (reportIfNotReachable(n)) {
+                // current state is unreachable.
+                // since nothing downstream can change it - no need to continue
+                return;
+            }
 
             // catch\finally blocks has the same reachability as try block
             var startState = currentState;
@@ -207,7 +247,12 @@ module ts {
         }
 
         function checkSwitchStatement(n: SwitchStatement): void {
-            verifyReachable(n);
+            if (reportIfNotReachable(n)) {
+                // current state is unreachable.
+                // since nothing downstream can change it - no need to continue
+                return;
+            }
+
             var startState = currentState;
             var hasDefault = false;
 
@@ -229,7 +274,12 @@ module ts {
         }
 
         function checkLabelledStatement(n: LabeledStatement): void {
-            verifyReachable(n);
+            if (reportIfNotReachable(n)) {
+                // current state is unreachable.
+                // since nothing downstream can change it - no need to continue
+                return;
+            }
+
             var ok = pushNamedLabel(n.label);
             check(n.statement);
             if (ok) {
@@ -238,7 +288,12 @@ module ts {
         }
 
         function checkWithStatement(n: WithStatement): void {
-            verifyReachable(n);
+            if (reportIfNotReachable(n)) {
+                // current state is unreachable.
+                // since nothing downstream can change it - no need to continue
+                return;
+            }
+
             check(n.statement);
         }
 
@@ -277,7 +332,7 @@ module ts {
                 case SyntaxKind.EmptyStatement:
                 case SyntaxKind.ExpressionStatement:
                 case SyntaxKind.DebuggerStatement:
-                    verifyReachable(n);
+                    reportIfNotReachable(n);
                     break;
                 case SyntaxKind.DoStatement:
                     checkDoStatement(<DoStatement>n);

From 147959fd31c9346674059dacec97e3a82fca2514 Mon Sep 17 00:00:00 2001
From: Vladimir Matveev <vladima@microsoft.com>
Date: Mon, 1 Dec 2014 10:39:19 -0800
Subject: [PATCH 3/7] removed commented code

---
 src/compiler/checker.ts | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 552d9628aafa6..9c243a91e3803 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -6152,7 +6152,6 @@ module ts {
         function checkFunctionExpressionBody(node: FunctionExpression) {
             if (node.type) {
                 checkControlFlowOfFunction(node, getTypeFromTypeNode(node.type) !== voidType, error);
-                // checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNode(node.type));
             }
             if (node.body.kind === SyntaxKind.FunctionBlock) {
                 checkSourceElement(node.body);
@@ -7297,7 +7296,6 @@ module ts {
             checkSourceElement(node.body);
             if (node.type && !isAccessor(node.kind)) {
                 checkControlFlowOfFunction(node, getTypeFromTypeNode(node.type) !== voidType, error);
-                // checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNode(node.type));
             }
 
             // If there is no body and no explicit return type, then report an error.

From 20d0cd9363119fb27085012e6c3db37b4bd50a10 Mon Sep 17 00:00:00 2001
From: Vladimir Matveev <vladima@microsoft.com>
Date: Mon, 1 Dec 2014 13:50:14 -0800
Subject: [PATCH 4/7] fix post finally state

---
 src/compiler/controlflow.ts | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/src/compiler/controlflow.ts b/src/compiler/controlflow.ts
index 7d4334f9b86e2..95438a4b44130 100644
--- a/src/compiler/controlflow.ts
+++ b/src/compiler/controlflow.ts
@@ -241,9 +241,17 @@ module ts {
             check(n.catchBlock);
             var postCatchState = currentState;
 
-            setState(startState);
-            check(n.finallyBlock);
-            setState(or(postTryState, postCatchState));
+            if (n.finallyBlock) {
+                setState(startState);
+                check(n.finallyBlock);
+                // post-finally state become current state
+            }
+            else {
+                // post catch state is reachable if
+                // - post try state is reachable - control flow can fall out of try block
+                // - post catch state is reachable - control flow can fall out of catch block
+                setState(or(postTryState, postCatchState))
+            }
         }
 
         function checkSwitchStatement(n: SwitchStatement): void {

From 363a517fd66a9ce9315f352b9158b057c9a1f654 Mon Sep 17 00:00:00 2001
From: Vladimir Matveev <vladima@microsoft.com>
Date: Mon, 1 Dec 2014 14:19:35 -0800
Subject: [PATCH 5/7] move check of else statement into conditional

---
 src/compiler/controlflow.ts | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/src/compiler/controlflow.ts b/src/compiler/controlflow.ts
index 95438a4b44130..6d982c271a027 100644
--- a/src/compiler/controlflow.ts
+++ b/src/compiler/controlflow.ts
@@ -197,12 +197,13 @@ module ts {
 
             setState(ifTrueState);
             check(n.thenStatement);
-            ifTrueState = currentState;
 
-            setState(ifFalseState);
-            check(n.elseStatement);
-
-            currentState = or(currentState, ifTrueState);
+            if (n.elseStatement) {
+                ifTrueState = currentState;
+                setState(ifFalseState);
+                check(n.elseStatement);
+                currentState = or(currentState, ifTrueState);
+            }
         }
 
         function checkReturnOrThrow(n: Node): void {

From 15e630cae8359e66d90839b411dfff5b990564e7 Mon Sep 17 00:00:00 2001
From: Vladimir Matveev <vladima@microsoft.com>
Date: Tue, 2 Dec 2014 11:47:37 -0800
Subject: [PATCH 6/7] change category of cf related notification to 'warning'

---
 src/compiler/diagnosticInformationMap.generated.ts | 8 ++++----
 src/compiler/diagnosticMessages.json               | 8 ++++----
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts
index 43acb01cbc87b..88143272ab658 100644
--- a/src/compiler/diagnosticInformationMap.generated.ts
+++ b/src/compiler/diagnosticInformationMap.generated.ts
@@ -288,10 +288,10 @@ module ts {
         Type_alias_0_circularly_references_itself: { code: 2456, category: DiagnosticCategory.Error, key: "Type alias '{0}' circularly references itself." },
         Type_alias_name_cannot_be_0: { code: 2457, category: DiagnosticCategory.Error, key: "Type alias name cannot be '{0}'" },
         An_AMD_module_cannot_have_multiple_name_assignments: { code: 2458, category: DiagnosticCategory.Error, key: "An AMD module cannot have multiple name assignments." },
-        Not_all_code_paths_return_a_value: { code: 2459, category: DiagnosticCategory.Error, key: "Not all code paths return a value" },
-        Unreachable_code_detected: { code: 2460, category: DiagnosticCategory.Error, key: "Unreachable code detected" },
-        Unused_label: { code: 2461, category: DiagnosticCategory.Error, key: "Unused label" },
-        Fallthrough_case_in_switch: { code: 2462, category: DiagnosticCategory.Error, key: "Fallthrough case in switch" },
+        Not_all_code_paths_return_a_value: { code: 2459, category: DiagnosticCategory.Warning, key: "Not all code paths return a value" },
+        Unreachable_code_detected: { code: 2460, category: DiagnosticCategory.Warning, key: "Unreachable code detected" },
+        Unused_label: { code: 2461, category: DiagnosticCategory.Warning, key: "Unused label" },
+        Fallthrough_case_in_switch: { code: 2462, category: DiagnosticCategory.Warning, key: "Fallthrough case in switch" },
         Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." },
         Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." },
         Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." },
diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index 1f0351e90c8d1..8ceedac516662 100644
--- a/src/compiler/diagnosticMessages.json
+++ b/src/compiler/diagnosticMessages.json
@@ -1149,19 +1149,19 @@
         "code": 2458
     },    
     "Not all code paths return a value": {
-        "category": "Error",
+        "category": "Warning",
         "code": 2459
     },
     "Unreachable code detected": {
-        "category": "Error",
+        "category": "Warning",
         "code": 2460
     },
     "Unused label": {
-        "category": "Error",
+        "category": "Warning",
         "code": 2461
     },
     "Fallthrough case in switch": {
-        "category": "Error",
+        "category": "Warning",
         "code": 2462
     },
     "Import declaration '{0}' is using private name '{1}'.": {

From 68b135a9656399749906d54777ce39693fba0eb2 Mon Sep 17 00:00:00 2001
From: Vladimir Matveev <vladima@microsoft.com>
Date: Tue, 2 Dec 2014 14:31:28 -0800
Subject: [PATCH 7/7] fix final state in if statement

---
 src/compiler/controlflow.ts | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/compiler/controlflow.ts b/src/compiler/controlflow.ts
index 6d982c271a027..78c30dce27ed5 100644
--- a/src/compiler/controlflow.ts
+++ b/src/compiler/controlflow.ts
@@ -197,12 +197,14 @@ module ts {
 
             setState(ifTrueState);
             check(n.thenStatement);
-
             if (n.elseStatement) {
                 ifTrueState = currentState;
                 setState(ifFalseState);
                 check(n.elseStatement);
-                currentState = or(currentState, ifTrueState);
+                setState(or(currentState, ifTrueState));
+            }
+            else {
+                setState(or(currentState, ifFalseState))
             }
         }