Skip to content

Commit 241920c

Browse files
committed
Merge branch 'outerControlFlows' of https://github.com/Microsoft/TypeScript into typedefForJsdoc
# Conflicts: # src/compiler/binder.ts
2 parents e93f9df + 5b4dffc commit 241920c

16 files changed

+992
-241
lines changed

src/compiler/binder.ts

Lines changed: 168 additions & 173 deletions
Large diffs are not rendered by default.

src/compiler/checker.ts

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7660,7 +7660,7 @@ namespace ts {
76607660
getInitialTypeOfBindingElement(<BindingElement>node);
76617661
}
76627662

7663-
function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean) {
7663+
function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, includeOuterFunctions: boolean) {
76647664
let key: string;
76657665
if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
76667666
return declaredType;
@@ -7706,15 +7706,21 @@ namespace ts {
77067706
getTypeAtFlowBranchLabel(<FlowLabel>flow) :
77077707
getTypeAtFlowLoopLabel(<FlowLabel>flow);
77087708
}
7709-
else if (flow.flags & FlowFlags.Unreachable) {
7709+
else if (flow.flags & FlowFlags.Start) {
7710+
// Check if we should continue with the control flow of the containing function.
7711+
const container = (<FlowStart>flow).container;
7712+
if (container && includeOuterFunctions) {
7713+
flow = container.flowNode;
7714+
continue;
7715+
}
7716+
// At the top of the flow we have the initial type.
7717+
type = initialType;
7718+
}
7719+
else {
77107720
// Unreachable code errors are reported in the binding phase. Here we
77117721
// simply return the declared type to reduce follow-on errors.
77127722
type = declaredType;
77137723
}
7714-
else {
7715-
// At the top of the flow we have the initial type.
7716-
type = initialType;
7717-
}
77187724
if (flow.flags & FlowFlags.Shared) {
77197725
// Record visited node and the associated type in the cache.
77207726
visitedFlowNodes[visitedFlowCount] = flow;
@@ -8083,6 +8089,17 @@ namespace ts {
80838089
return expression;
80848090
}
80858091

8092+
function isDeclarationIncludedInFlow(reference: Node, declaration: Declaration, includeOuterFunctions: boolean) {
8093+
const declarationContainer = getContainingFunctionOrModule(declaration);
8094+
let container = getContainingFunctionOrModule(reference);
8095+
while (container !== declarationContainer &&
8096+
(container.kind === SyntaxKind.FunctionExpression || container.kind === SyntaxKind.ArrowFunction) &&
8097+
(includeOuterFunctions || getImmediatelyInvokedFunctionExpression(<FunctionExpression>container))) {
8098+
container = getContainingFunctionOrModule(container);
8099+
}
8100+
return container === declarationContainer;
8101+
}
8102+
80868103
function checkIdentifier(node: Identifier): Type {
80878104
const symbol = getResolvedSymbol(node);
80888105

@@ -8139,10 +8156,11 @@ namespace ts {
81398156
return type;
81408157
}
81418158
const declaration = localOrExportSymbol.valueDeclaration;
8159+
const includeOuterFunctions = isReadonlySymbol(localOrExportSymbol);
81428160
const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || !declaration ||
81438161
getRootDeclaration(declaration).kind === SyntaxKind.Parameter || isInAmbientContext(declaration) ||
8144-
getContainingFunctionOrModule(declaration) !== getContainingFunctionOrModule(node);
8145-
const flowType = getFlowTypeOfReference(node, type, assumeInitialized);
8162+
!isDeclarationIncludedInFlow(node, declaration, includeOuterFunctions);
8163+
const flowType = getFlowTypeOfReference(node, type, assumeInitialized, includeOuterFunctions);
81468164
if (!assumeInitialized && !(getNullableKind(type) & TypeFlags.Undefined) && getNullableKind(flowType) & TypeFlags.Undefined) {
81478165
error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol));
81488166
// Return the declared type to reduce follow-on errors
@@ -8391,7 +8409,7 @@ namespace ts {
83918409
if (isClassLike(container.parent)) {
83928410
const symbol = getSymbolOfNode(container.parent);
83938411
const type = container.flags & NodeFlags.Static ? getTypeOfSymbol(symbol) : (<InterfaceType>getDeclaredTypeOfSymbol(symbol)).thisType;
8394-
return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true);
8412+
return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true, /*includeOuterFunctions*/ true);
83958413
}
83968414

83978415
if (isInJavaScriptFile(node)) {
@@ -8637,23 +8655,25 @@ namespace ts {
86378655
function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type {
86388656
const func = parameter.parent;
86398657
if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) {
8640-
const iife = getImmediatelyInvokedFunctionExpression(func);
8641-
if (iife) {
8642-
const indexOfParameter = indexOf(func.parameters, parameter);
8643-
if (iife.arguments && indexOfParameter < iife.arguments.length) {
8644-
if (parameter.dotDotDotToken) {
8645-
const restTypes: Type[] = [];
8646-
for (let i = indexOfParameter; i < iife.arguments.length; i++) {
8647-
restTypes.push(getTypeOfExpression(iife.arguments[i]));
8658+
if (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) {
8659+
const iife = getImmediatelyInvokedFunctionExpression(func);
8660+
if (iife) {
8661+
const indexOfParameter = indexOf(func.parameters, parameter);
8662+
if (iife.arguments && indexOfParameter < iife.arguments.length) {
8663+
if (parameter.dotDotDotToken) {
8664+
const restTypes: Type[] = [];
8665+
for (let i = indexOfParameter; i < iife.arguments.length; i++) {
8666+
restTypes.push(getTypeOfExpression(iife.arguments[i]));
8667+
}
8668+
return createArrayType(getUnionType(restTypes));
86488669
}
8649-
return createArrayType(getUnionType(restTypes));
8670+
const links = getNodeLinks(iife);
8671+
const cached = links.resolvedSignature;
8672+
links.resolvedSignature = anySignature;
8673+
const type = checkExpression(iife.arguments[indexOfParameter]);
8674+
links.resolvedSignature = cached;
8675+
return type;
86508676
}
8651-
const links = getNodeLinks(iife);
8652-
const cached = links.resolvedSignature;
8653-
links.resolvedSignature = anySignature;
8654-
const type = checkExpression(iife.arguments[indexOfParameter]);
8655-
links.resolvedSignature = cached;
8656-
return type;
86578677
}
86588678
}
86598679
const contextualSignature = getContextualSignature(func);
@@ -8676,20 +8696,6 @@ namespace ts {
86768696
return undefined;
86778697
}
86788698

8679-
function getImmediatelyInvokedFunctionExpression(func: FunctionExpression | MethodDeclaration) {
8680-
if (isFunctionExpressionOrArrowFunction(func)) {
8681-
let prev: Node = func;
8682-
let parent: Node = func.parent;
8683-
while (parent.kind === SyntaxKind.ParenthesizedExpression) {
8684-
prev = parent;
8685-
parent = parent.parent;
8686-
}
8687-
if (parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === prev) {
8688-
return parent as CallExpression;
8689-
}
8690-
}
8691-
}
8692-
86938699
// In a variable, parameter or property declaration with a type annotation,
86948700
// the contextual type of an initializer expression is the type of the variable, parameter or property.
86958701
// Otherwise, in a parameter declaration of a contextually typed function expression,
@@ -10003,7 +10009,7 @@ namespace ts {
1000310009
return propType;
1000410010
}
1000510011
}
10006-
return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true);
10012+
return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*includeOuterFunctions*/ false);
1000710013
}
1000810014

1000910015
function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean {

src/compiler/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,7 @@ namespace ts {
423423

424424
ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn,
425425
EmitHelperFlags = HasClassExtends | HasDecorators | HasParamDecorators | HasAsyncFunctions,
426+
ReachabilityAndEmitFlags = ReachabilityCheckFlags | EmitHelperFlags,
426427

427428
// Parsing context flags
428429
ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile,
@@ -1564,6 +1565,13 @@ namespace ts {
15641565
id?: number; // Node id used by flow type cache in checker
15651566
}
15661567

1568+
// FlowStart represents the start of a control flow. For a function expression or arrow
1569+
// function, the container property references the function (which in turn has a flowNode
1570+
// property for the containing control flow).
1571+
export interface FlowStart extends FlowNode {
1572+
container?: FunctionExpression | ArrowFunction;
1573+
}
1574+
15671575
// FlowLabel represents a junction with multiple possible preceding control flows.
15681576
export interface FlowLabel extends FlowNode {
15691577
antecedents: FlowNode[];

src/compiler/utilities.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,20 @@ namespace ts {
10071007
}
10081008
}
10091009

1010+
export function getImmediatelyInvokedFunctionExpression(func: Node): CallExpression {
1011+
if (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) {
1012+
let prev = func;
1013+
let parent = func.parent;
1014+
while (parent.kind === SyntaxKind.ParenthesizedExpression) {
1015+
prev = parent;
1016+
parent = parent.parent;
1017+
}
1018+
if (parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === prev) {
1019+
return parent as CallExpression;
1020+
}
1021+
}
1022+
}
1023+
10101024
/**
10111025
* Determines whether a node is a property or element access expression for super.
10121026
*/
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//// [constLocalsInFunctionExpressions.ts]
2+
declare function getStringOrNumber(): string | number;
3+
4+
function f1() {
5+
const x = getStringOrNumber();
6+
if (typeof x === "string") {
7+
const f = () => x.length;
8+
}
9+
}
10+
11+
function f2() {
12+
const x = getStringOrNumber();
13+
if (typeof x !== "string") {
14+
return;
15+
}
16+
const f = () => x.length;
17+
}
18+
19+
function f3() {
20+
const x = getStringOrNumber();
21+
if (typeof x === "string") {
22+
const f = function() { return x.length; };
23+
}
24+
}
25+
26+
function f4() {
27+
const x = getStringOrNumber();
28+
if (typeof x !== "string") {
29+
return;
30+
}
31+
const f = function() { return x.length; };
32+
}
33+
34+
function f5() {
35+
const x = getStringOrNumber();
36+
if (typeof x === "string") {
37+
const f = () => () => x.length;
38+
}
39+
}
40+
41+
//// [constLocalsInFunctionExpressions.js]
42+
function f1() {
43+
var x = getStringOrNumber();
44+
if (typeof x === "string") {
45+
var f = function () { return x.length; };
46+
}
47+
}
48+
function f2() {
49+
var x = getStringOrNumber();
50+
if (typeof x !== "string") {
51+
return;
52+
}
53+
var f = function () { return x.length; };
54+
}
55+
function f3() {
56+
var x = getStringOrNumber();
57+
if (typeof x === "string") {
58+
var f = function () { return x.length; };
59+
}
60+
}
61+
function f4() {
62+
var x = getStringOrNumber();
63+
if (typeof x !== "string") {
64+
return;
65+
}
66+
var f = function () { return x.length; };
67+
}
68+
function f5() {
69+
var x = getStringOrNumber();
70+
if (typeof x === "string") {
71+
var f = function () { return function () { return x.length; }; };
72+
}
73+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
=== tests/cases/conformance/controlFlow/constLocalsInFunctionExpressions.ts ===
2+
declare function getStringOrNumber(): string | number;
3+
>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0))
4+
5+
function f1() {
6+
>f1 : Symbol(f1, Decl(constLocalsInFunctionExpressions.ts, 0, 54))
7+
8+
const x = getStringOrNumber();
9+
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 3, 9))
10+
>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0))
11+
12+
if (typeof x === "string") {
13+
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 3, 9))
14+
15+
const f = () => x.length;
16+
>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 5, 13))
17+
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
18+
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 3, 9))
19+
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
20+
}
21+
}
22+
23+
function f2() {
24+
>f2 : Symbol(f2, Decl(constLocalsInFunctionExpressions.ts, 7, 1))
25+
26+
const x = getStringOrNumber();
27+
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 10, 9))
28+
>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0))
29+
30+
if (typeof x !== "string") {
31+
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 10, 9))
32+
33+
return;
34+
}
35+
const f = () => x.length;
36+
>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 14, 9))
37+
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
38+
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 10, 9))
39+
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
40+
}
41+
42+
function f3() {
43+
>f3 : Symbol(f3, Decl(constLocalsInFunctionExpressions.ts, 15, 1))
44+
45+
const x = getStringOrNumber();
46+
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 18, 9))
47+
>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0))
48+
49+
if (typeof x === "string") {
50+
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 18, 9))
51+
52+
const f = function() { return x.length; };
53+
>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 20, 13))
54+
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
55+
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 18, 9))
56+
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
57+
}
58+
}
59+
60+
function f4() {
61+
>f4 : Symbol(f4, Decl(constLocalsInFunctionExpressions.ts, 22, 1))
62+
63+
const x = getStringOrNumber();
64+
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 25, 9))
65+
>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0))
66+
67+
if (typeof x !== "string") {
68+
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 25, 9))
69+
70+
return;
71+
}
72+
const f = function() { return x.length; };
73+
>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 29, 9))
74+
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
75+
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 25, 9))
76+
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
77+
}
78+
79+
function f5() {
80+
>f5 : Symbol(f5, Decl(constLocalsInFunctionExpressions.ts, 30, 1))
81+
82+
const x = getStringOrNumber();
83+
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 33, 9))
84+
>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0))
85+
86+
if (typeof x === "string") {
87+
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 33, 9))
88+
89+
const f = () => () => x.length;
90+
>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 35, 13))
91+
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
92+
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 33, 9))
93+
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
94+
}
95+
}

0 commit comments

Comments
 (0)