From 3ec5eb17bc4298081f3c8fe7753aa90a31e13dcb Mon Sep 17 00:00:00 2001 From: Vladimir Matveev 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 @@ /// /// /// +/// 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 @@ +/// + +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 = {}; + // 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(n); + break; + case SyntaxKind.SourceFile: + checkBlock(n); + break; + case SyntaxKind.Block: + case SyntaxKind.TryBlock: + case SyntaxKind.CatchBlock: + case SyntaxKind.FinallyBlock: + case SyntaxKind.ModuleBlock: + case SyntaxKind.FunctionBlock: + checkBlock(n); + break; + case SyntaxKind.IfStatement: + checkIfStatement(n); + break; + case SyntaxKind.ReturnStatement: + case SyntaxKind.ThrowStatement: + checkReturnOrThrow(n); + break; + case SyntaxKind.BreakStatement: + case SyntaxKind.ContinueStatement: + checkBreakOrContinueStatement(n); + break; + case SyntaxKind.VariableStatement: + case SyntaxKind.EmptyStatement: + case SyntaxKind.ExpressionStatement: + case SyntaxKind.DebuggerStatement: + verifyReachable(n); + break; + case SyntaxKind.DoStatement: + checkDoStatement(n); + break; + case SyntaxKind.ForInStatement: + checkForInStatement(n); + break; + case SyntaxKind.ForStatement: + checkForStatement(n); + break; + case SyntaxKind.LabeledStatement: + checkLabelledStatement(n); + break; + case SyntaxKind.SwitchStatement: + checkSwitchStatement(n); + break; + case SyntaxKind.TryStatement: + checkTryStatement(n); + break; + case SyntaxKind.WithStatement: + checkWithStatement(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 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(n); From 147959fd31c9346674059dacec97e3a82fca2514 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev 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 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 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 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 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)) } }