Skip to content

Commit 766ecca

Browse files
authored
Merge pull request #10815 from Microsoft/controlFlowOuterVariable
Fix outer variable control flow analysis
2 parents a824227 + 7ffc705 commit 766ecca

File tree

5 files changed

+118
-1
lines changed

5 files changed

+118
-1
lines changed

src/compiler/checker.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -8920,6 +8920,7 @@ namespace ts {
89208920
const isParameter = getRootDeclaration(declaration).kind === SyntaxKind.Parameter;
89218921
const declarationContainer = getControlFlowContainer(declaration);
89228922
let flowContainer = getControlFlowContainer(node);
8923+
const isOuterVariable = flowContainer !== declarationContainer;
89238924
// When the control flow originates in a function expression or arrow function and we are referencing
89248925
// a const variable or parameter from an outer function, we extend the origin of the control flow
89258926
// analysis to include the immediately enclosing function.
@@ -8932,7 +8933,7 @@ namespace ts {
89328933
// the entire control flow graph from the variable's declaration (i.e. when the flow container and
89338934
// declaration container are the same).
89348935
const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || isParameter ||
8935-
flowContainer !== declarationContainer || isInAmbientContext(declaration);
8936+
isOuterVariable || isInAmbientContext(declaration);
89368937
const flowType = getFlowTypeOfReference(node, type, assumeInitialized, flowContainer);
89378938
// A variable is considered uninitialized when it is possible to analyze the entire control flow graph
89388939
// from declaration to use, and when the variable's declared type doesn't include undefined but the
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//// [controlFlowOuterVariable.ts]
2+
3+
// Repros from #10641
4+
5+
const CONFIG = {
6+
foo: '',
7+
setFoo: function(foo: string) {
8+
CONFIG.foo = foo;
9+
}
10+
};
11+
12+
const helper = function<T>(t: T[]) {
13+
helper(t.slice(1));
14+
}
15+
16+
//// [controlFlowOuterVariable.js]
17+
// Repros from #10641
18+
var CONFIG = {
19+
foo: '',
20+
setFoo: function (foo) {
21+
CONFIG.foo = foo;
22+
}
23+
};
24+
var helper = function (t) {
25+
helper(t.slice(1));
26+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
=== tests/cases/compiler/controlFlowOuterVariable.ts ===
2+
3+
// Repros from #10641
4+
5+
const CONFIG = {
6+
>CONFIG : Symbol(CONFIG, Decl(controlFlowOuterVariable.ts, 3, 5))
7+
8+
foo: '',
9+
>foo : Symbol(foo, Decl(controlFlowOuterVariable.ts, 3, 16))
10+
11+
setFoo: function(foo: string) {
12+
>setFoo : Symbol(setFoo, Decl(controlFlowOuterVariable.ts, 4, 12))
13+
>foo : Symbol(foo, Decl(controlFlowOuterVariable.ts, 5, 21))
14+
15+
CONFIG.foo = foo;
16+
>CONFIG.foo : Symbol(foo, Decl(controlFlowOuterVariable.ts, 3, 16))
17+
>CONFIG : Symbol(CONFIG, Decl(controlFlowOuterVariable.ts, 3, 5))
18+
>foo : Symbol(foo, Decl(controlFlowOuterVariable.ts, 3, 16))
19+
>foo : Symbol(foo, Decl(controlFlowOuterVariable.ts, 5, 21))
20+
}
21+
};
22+
23+
const helper = function<T>(t: T[]) {
24+
>helper : Symbol(helper, Decl(controlFlowOuterVariable.ts, 10, 5))
25+
>T : Symbol(T, Decl(controlFlowOuterVariable.ts, 10, 24))
26+
>t : Symbol(t, Decl(controlFlowOuterVariable.ts, 10, 27))
27+
>T : Symbol(T, Decl(controlFlowOuterVariable.ts, 10, 24))
28+
29+
helper(t.slice(1));
30+
>helper : Symbol(helper, Decl(controlFlowOuterVariable.ts, 10, 5))
31+
>t.slice : Symbol(Array.slice, Decl(lib.d.ts, --, --))
32+
>t : Symbol(t, Decl(controlFlowOuterVariable.ts, 10, 27))
33+
>slice : Symbol(Array.slice, Decl(lib.d.ts, --, --))
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
=== tests/cases/compiler/controlFlowOuterVariable.ts ===
2+
3+
// Repros from #10641
4+
5+
const CONFIG = {
6+
>CONFIG : { foo: string; setFoo: (foo: string) => void; }
7+
>{ foo: '', setFoo: function(foo: string) { CONFIG.foo = foo; }} : { foo: string; setFoo: (foo: string) => void; }
8+
9+
foo: '',
10+
>foo : string
11+
>'' : string
12+
13+
setFoo: function(foo: string) {
14+
>setFoo : (foo: string) => void
15+
>function(foo: string) { CONFIG.foo = foo; } : (foo: string) => void
16+
>foo : string
17+
18+
CONFIG.foo = foo;
19+
>CONFIG.foo = foo : string
20+
>CONFIG.foo : string
21+
>CONFIG : { foo: string; setFoo: (foo: string) => void; }
22+
>foo : string
23+
>foo : string
24+
}
25+
};
26+
27+
const helper = function<T>(t: T[]) {
28+
>helper : <T>(t: T[]) => void
29+
>function<T>(t: T[]) { helper(t.slice(1));} : <T>(t: T[]) => void
30+
>T : T
31+
>t : T[]
32+
>T : T
33+
34+
helper(t.slice(1));
35+
>helper(t.slice(1)) : void
36+
>helper : <T>(t: T[]) => void
37+
>t.slice(1) : T[]
38+
>t.slice : (start?: number | undefined, end?: number | undefined) => T[]
39+
>t : T[]
40+
>slice : (start?: number | undefined, end?: number | undefined) => T[]
41+
>1 : number
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// @strictNullChecks: true
2+
3+
// Repros from #10641
4+
5+
const CONFIG = {
6+
foo: '',
7+
setFoo: function(foo: string) {
8+
CONFIG.foo = foo;
9+
}
10+
};
11+
12+
const helper = function<T>(t: T[]) {
13+
helper(t.slice(1));
14+
}

0 commit comments

Comments
 (0)