Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,13 @@ namespace ts {
};
}

function createFlowLoopLabel(): FlowLabel {
return {
kind: FlowKind.LoopLabel,
antecedents: undefined
};
}

function addAntecedent(label: FlowLabel, antecedent: FlowNode): void {
if (antecedent.kind !== FlowKind.Unreachable && !contains(label.antecedents, antecedent)) {
(label.antecedents || (label.antecedents = [])).push(antecedent);
Expand Down Expand Up @@ -755,7 +762,7 @@ namespace ts {
}

function bindWhileStatement(node: WhileStatement): void {
const preWhileLabel = createFlowLabel();
const preWhileLabel = createFlowLoopLabel();
const preBodyLabel = createFlowLabel();
const postWhileLabel = createFlowLabel();
addAntecedent(preWhileLabel, currentFlow);
Expand All @@ -768,7 +775,7 @@ namespace ts {
}

function bindDoStatement(node: DoStatement): void {
const preDoLabel = createFlowLabel();
const preDoLabel = createFlowLoopLabel();
const preConditionLabel = createFlowLabel();
const postDoLabel = createFlowLabel();
addAntecedent(preDoLabel, currentFlow);
Expand All @@ -781,7 +788,7 @@ namespace ts {
}

function bindForStatement(node: ForStatement): void {
const preLoopLabel = createFlowLabel();
const preLoopLabel = createFlowLoopLabel();
const preBodyLabel = createFlowLabel();
const postLoopLabel = createFlowLabel();
bind(node.initializer);
Expand All @@ -796,7 +803,7 @@ namespace ts {
}

function bindForInOrForOfStatement(node: ForInStatement | ForOfStatement): void {
const preLoopLabel = createFlowLabel();
const preLoopLabel = createFlowLoopLabel();
const postLoopLabel = createFlowLabel();
addAntecedent(preLoopLabel, currentFlow);
currentFlow = preLoopLabel;
Expand Down Expand Up @@ -943,7 +950,7 @@ namespace ts {
}

function bindLabeledStatement(node: LabeledStatement): void {
const preStatementLabel = createFlowLabel();
const preStatementLabel = createFlowLoopLabel();
const postStatementLabel = createFlowLabel();
bind(node.label);
addAntecedent(preStatementLabel, currentFlow);
Expand Down
12 changes: 8 additions & 4 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7592,6 +7592,7 @@ namespace ts {
case FlowKind.Condition:
return getTypeAtFlowCondition(<FlowCondition>flow);
case FlowKind.Label:
case FlowKind.LoopLabel:
if ((<FlowLabel>flow).antecedents.length === 1) {
flow = (<FlowLabel>flow).antecedents[0];
continue;
Expand Down Expand Up @@ -7639,7 +7640,8 @@ namespace ts {
}

function getTypeAtFlowCondition(flow: FlowCondition) {
return narrowType(getTypeAtFlowNode(flow.antecedent), flow.expression, flow.assumeTrue);
const type = getTypeAtFlowNode(flow.antecedent);
return type && narrowType(type, flow.expression, flow.assumeTrue);
}

function getTypeAtFlowNodeCached(flow: FlowNode) {
Expand All @@ -7665,13 +7667,15 @@ namespace ts {
flowStackCount--;
// Record the result only if the cache is still empty. If checkExpressionCached was called
// during processing it is possible we've already recorded a result.
return cache[key] || (cache[key] = type);
return cache[key] || type && (cache[key] = type);
}

function getTypeAtFlowLabel(flow: FlowLabel) {
const antecedentTypes: Type[] = [];
for (const antecedent of flow.antecedents) {
const type = getTypeAtFlowNodeCached(antecedent);
const type = flow.kind === FlowKind.LoopLabel ?
getTypeAtFlowNodeCached(antecedent) :
getTypeAtFlowNode(antecedent);
if (type) {
// If the type at a particular antecedent path is the declared type and the
// reference is known to always be assigned (i.e. when declared and initial types
Expand All @@ -7685,7 +7689,7 @@ namespace ts {
}
}
}
return antecedentTypes.length === 0 ? declaredType :
return antecedentTypes.length === 0 ? undefined :
antecedentTypes.length === 1 ? antecedentTypes[0] :
getUnionType(antecedentTypes);
}
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1519,6 +1519,7 @@ namespace ts {
Unreachable,
Start,
Label,
LoopLabel,
Assignment,
Condition
}
Expand Down
40 changes: 40 additions & 0 deletions tests/baselines/reference/controlFlowIteration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//// [controlFlowIteration.ts]

let cond: boolean;

function ff() {
let x: string | undefined;
while (true) {
if (cond) {
x = "";
}
else {
if (x) {
x.length;
}
if (x) {
x.length;
}
}
}
}


//// [controlFlowIteration.js]
var cond;
function ff() {
var x;
while (true) {
if (cond) {
x = "";
}
else {
if (x) {
x.length;
}
if (x) {
x.length;
}
}
}
}
39 changes: 39 additions & 0 deletions tests/baselines/reference/controlFlowIteration.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
=== tests/cases/conformance/controlFlow/controlFlowIteration.ts ===

let cond: boolean;
>cond : Symbol(cond, Decl(controlFlowIteration.ts, 1, 3))

function ff() {
>ff : Symbol(ff, Decl(controlFlowIteration.ts, 1, 18))

let x: string | undefined;
>x : Symbol(x, Decl(controlFlowIteration.ts, 4, 7))

while (true) {
if (cond) {
>cond : Symbol(cond, Decl(controlFlowIteration.ts, 1, 3))

x = "";
>x : Symbol(x, Decl(controlFlowIteration.ts, 4, 7))
}
else {
if (x) {
>x : Symbol(x, Decl(controlFlowIteration.ts, 4, 7))

x.length;
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowIteration.ts, 4, 7))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
}
if (x) {
>x : Symbol(x, Decl(controlFlowIteration.ts, 4, 7))

x.length;
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowIteration.ts, 4, 7))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
}
}
}
}

43 changes: 43 additions & 0 deletions tests/baselines/reference/controlFlowIteration.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
=== tests/cases/conformance/controlFlow/controlFlowIteration.ts ===

let cond: boolean;
>cond : boolean

function ff() {
>ff : () => void

let x: string | undefined;
>x : string | undefined

while (true) {
>true : boolean

if (cond) {
>cond : boolean

x = "";
>x = "" : string
>x : string | undefined
>"" : string
}
else {
if (x) {
>x : string | undefined

x.length;
>x.length : number
>x : string
>length : number
}
if (x) {
>x : string | undefined

x.length;
>x.length : number
>x : string
>length : number
}
}
}
}

20 changes: 20 additions & 0 deletions tests/cases/conformance/controlFlow/controlFlowIteration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// @strictNullChecks: true

let cond: boolean;

function ff() {
let x: string | undefined;
while (true) {
if (cond) {
x = "";
}
else {
if (x) {
x.length;
}
if (x) {
x.length;
}
}
}
}