Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix outer variable control flow analysis #10815

Merged
merged 2 commits into from
Sep 9, 2016
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
3 changes: 2 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8920,6 +8920,7 @@ namespace ts {
const isParameter = getRootDeclaration(declaration).kind === SyntaxKind.Parameter;
const declarationContainer = getControlFlowContainer(declaration);
let flowContainer = getControlFlowContainer(node);
const isOuterVariable = flowContainer !== declarationContainer;
// When the control flow originates in a function expression or arrow function and we are referencing
// a const variable or parameter from an outer function, we extend the origin of the control flow
// analysis to include the immediately enclosing function.
Expand All @@ -8932,7 +8933,7 @@ namespace ts {
// the entire control flow graph from the variable's declaration (i.e. when the flow container and
// declaration container are the same).
const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || isParameter ||
flowContainer !== declarationContainer || isInAmbientContext(declaration);
isOuterVariable || isInAmbientContext(declaration);
const flowType = getFlowTypeOfReference(node, type, assumeInitialized, flowContainer);
// A variable is considered uninitialized when it is possible to analyze the entire control flow graph
// from declaration to use, and when the variable's declared type doesn't include undefined but the
Expand Down
26 changes: 26 additions & 0 deletions tests/baselines/reference/controlFlowOuterVariable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//// [controlFlowOuterVariable.ts]

// Repros from #10641

const CONFIG = {
foo: '',
setFoo: function(foo: string) {
CONFIG.foo = foo;
}
};

const helper = function<T>(t: T[]) {
helper(t.slice(1));
}

//// [controlFlowOuterVariable.js]
// Repros from #10641
var CONFIG = {
foo: '',
setFoo: function (foo) {
CONFIG.foo = foo;
}
};
var helper = function (t) {
helper(t.slice(1));
};
34 changes: 34 additions & 0 deletions tests/baselines/reference/controlFlowOuterVariable.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
=== tests/cases/compiler/controlFlowOuterVariable.ts ===

// Repros from #10641

const CONFIG = {
>CONFIG : Symbol(CONFIG, Decl(controlFlowOuterVariable.ts, 3, 5))

foo: '',
>foo : Symbol(foo, Decl(controlFlowOuterVariable.ts, 3, 16))

setFoo: function(foo: string) {
>setFoo : Symbol(setFoo, Decl(controlFlowOuterVariable.ts, 4, 12))
>foo : Symbol(foo, Decl(controlFlowOuterVariable.ts, 5, 21))

CONFIG.foo = foo;
>CONFIG.foo : Symbol(foo, Decl(controlFlowOuterVariable.ts, 3, 16))
>CONFIG : Symbol(CONFIG, Decl(controlFlowOuterVariable.ts, 3, 5))
>foo : Symbol(foo, Decl(controlFlowOuterVariable.ts, 3, 16))
>foo : Symbol(foo, Decl(controlFlowOuterVariable.ts, 5, 21))
}
};

const helper = function<T>(t: T[]) {
>helper : Symbol(helper, Decl(controlFlowOuterVariable.ts, 10, 5))
>T : Symbol(T, Decl(controlFlowOuterVariable.ts, 10, 24))
>t : Symbol(t, Decl(controlFlowOuterVariable.ts, 10, 27))
>T : Symbol(T, Decl(controlFlowOuterVariable.ts, 10, 24))

helper(t.slice(1));
>helper : Symbol(helper, Decl(controlFlowOuterVariable.ts, 10, 5))
>t.slice : Symbol(Array.slice, Decl(lib.d.ts, --, --))
>t : Symbol(t, Decl(controlFlowOuterVariable.ts, 10, 27))
>slice : Symbol(Array.slice, Decl(lib.d.ts, --, --))
}
42 changes: 42 additions & 0 deletions tests/baselines/reference/controlFlowOuterVariable.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
=== tests/cases/compiler/controlFlowOuterVariable.ts ===

// Repros from #10641

const CONFIG = {
>CONFIG : { foo: string; setFoo: (foo: string) => void; }
>{ foo: '', setFoo: function(foo: string) { CONFIG.foo = foo; }} : { foo: string; setFoo: (foo: string) => void; }

foo: '',
>foo : string
>'' : string

setFoo: function(foo: string) {
>setFoo : (foo: string) => void
>function(foo: string) { CONFIG.foo = foo; } : (foo: string) => void
>foo : string

CONFIG.foo = foo;
>CONFIG.foo = foo : string
>CONFIG.foo : string
>CONFIG : { foo: string; setFoo: (foo: string) => void; }
>foo : string
>foo : string
}
};

const helper = function<T>(t: T[]) {
>helper : <T>(t: T[]) => void
>function<T>(t: T[]) { helper(t.slice(1));} : <T>(t: T[]) => void
>T : T
>t : T[]
>T : T

helper(t.slice(1));
>helper(t.slice(1)) : void
>helper : <T>(t: T[]) => void
>t.slice(1) : T[]
>t.slice : (start?: number | undefined, end?: number | undefined) => T[]
>t : T[]
>slice : (start?: number | undefined, end?: number | undefined) => T[]
>1 : number
}
14 changes: 14 additions & 0 deletions tests/cases/compiler/controlFlowOuterVariable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// @strictNullChecks: true

// Repros from #10641

const CONFIG = {
foo: '',
setFoo: function(foo: string) {
CONFIG.foo = foo;
}
};

const helper = function<T>(t: T[]) {
helper(t.slice(1));
}