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(42678): detect access to uninitialized variable in IIFE #42776

Merged
merged 6 commits into from Dec 6, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/compiler/checker.ts
Expand Up @@ -2533,10 +2533,12 @@ namespace ts {

/* Starting from 'initial' node walk up the parent chain until 'stopAt' node is reached.
* If at any point current node is equal to 'parent' node - return true.
* If current node is an IIFE, continue walking up.
* Return false if 'stopAt' node is reached or isFunctionLike(current) === true.
*/
function isSameScopeDescendentOf(initial: Node, parent: Node | undefined, stopAt: Node): boolean {
return !!parent && !!findAncestor(initial, n => n === stopAt || isFunctionLike(n) ? "quit" : n === parent);
return !!parent && !!findAncestor(initial, n => n === parent
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this equivalent? I think I like it better simply because it's so much shorter, although I'm not 100% sure.

Suggested change
return !!parent && !!findAncestor(initial, n => {
return !!parent && !!findAncestor(initial, n => n === stopAt || isFunctionLike(n) && !getImmediatelyInvokedFunctionExpression() : n === parent);

Copy link
Contributor Author

@Zzzen Zzzen Dec 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it works. But I wonder if we should have n === parent before isFunctionLike(n) && !getImmediatelyInvokedFunctionExpression() because parent might be a function.

|| (n === stopAt || isFunctionLike(n) && !getImmediatelyInvokedFunctionExpression(n) ? "quit" : false));
}

function getAnyImportSyntax(node: Node): AnyImportSyntax | undefined {
Expand Down
Expand Up @@ -2,9 +2,12 @@ tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(2,13): error TS2448: Bl
tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(58,20): error TS2448: Block-scoped variable 'x' used before its declaration.
tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(65,20): error TS2448: Block-scoped variable 'x' used before its declaration.
tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(100,12): error TS2448: Block-scoped variable 'x' used before its declaration.
tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(111,28): error TS2448: Block-scoped variable 'a' used before its declaration.
tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(112,21): error TS2448: Block-scoped variable 'a' used before its declaration.
tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(122,22): error TS2448: Block-scoped variable 'a' used before its declaration.


==== tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts (4 errors) ====
==== tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts (7 errors) ====
function foo0() {
let a = x;
~
Expand Down Expand Up @@ -120,4 +123,33 @@ tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(100,12): error TS2448:
}
let x
}

function foo15() {
// https://github.com/microsoft/TypeScript/issues/42678
const [
a,
b,
] = ((): [number, number] => {
(() => console.log(a))(); // should error
~
!!! error TS2448: Block-scoped variable 'a' used before its declaration.
!!! related TS2728 tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts:108:9: 'a' is declared here.
console.log(a); // should error
~
!!! error TS2448: Block-scoped variable 'a' used before its declaration.
!!! related TS2728 tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts:108:9: 'a' is declared here.
const b = () => a; // should be ok
return [
0,
0,
];
})();
}

function foo16() {
let [a] = (() => a)();
~
!!! error TS2448: Block-scoped variable 'a' used before its declaration.
!!! related TS2728 tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts:122:10: 'a' is declared here.
}

35 changes: 35 additions & 0 deletions tests/baselines/reference/blockScopedVariablesUseBeforeDef.js
Expand Up @@ -102,6 +102,26 @@ function foo14() {
}
let x
}

function foo15() {
// https://github.com/microsoft/TypeScript/issues/42678
const [
a,
b,
] = ((): [number, number] => {
(() => console.log(a))(); // should error
console.log(a); // should error
const b = () => a; // should be ok
return [
0,
0,
];
})();
}

function foo16() {
let [a] = (() => a)();
}


//// [blockScopedVariablesUseBeforeDef.js]
Expand Down Expand Up @@ -219,3 +239,18 @@ function foo14() {
};
var x;
}
function foo15() {
// https://github.com/microsoft/TypeScript/issues/42678
var _a = (function () {
(function () { return console.log(a); })(); // should error
console.log(a); // should error
var b = function () { return a; }; // should be ok
return [
0,
0,
];
})(), a = _a[0], b = _a[1];
}
function foo16() {
var a = (function () { return a; })()[0];
}
43 changes: 43 additions & 0 deletions tests/baselines/reference/blockScopedVariablesUseBeforeDef.symbols
Expand Up @@ -213,3 +213,46 @@ function foo14() {
>x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 101, 7))
}

function foo15() {
>foo15 : Symbol(foo15, Decl(blockScopedVariablesUseBeforeDef.ts, 102, 1))

// https://github.com/microsoft/TypeScript/issues/42678
const [
a,
>a : Symbol(a, Decl(blockScopedVariablesUseBeforeDef.ts, 106, 11))

b,
>b : Symbol(b, Decl(blockScopedVariablesUseBeforeDef.ts, 107, 10))

] = ((): [number, number] => {
(() => console.log(a))(); // should error
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>a : Symbol(a, Decl(blockScopedVariablesUseBeforeDef.ts, 106, 11))

console.log(a); // should error
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>a : Symbol(a, Decl(blockScopedVariablesUseBeforeDef.ts, 106, 11))

const b = () => a; // should be ok
>b : Symbol(b, Decl(blockScopedVariablesUseBeforeDef.ts, 112, 13))
>a : Symbol(a, Decl(blockScopedVariablesUseBeforeDef.ts, 106, 11))

return [
0,
0,
];
})();
}

function foo16() {
>foo16 : Symbol(foo16, Decl(blockScopedVariablesUseBeforeDef.ts, 118, 1))

let [a] = (() => a)();
>a : Symbol(a, Decl(blockScopedVariablesUseBeforeDef.ts, 121, 9))
>a : Symbol(a, Decl(blockScopedVariablesUseBeforeDef.ts, 121, 9))
}

62 changes: 62 additions & 0 deletions tests/baselines/reference/blockScopedVariablesUseBeforeDef.types
Expand Up @@ -225,3 +225,65 @@ function foo14() {
>x : any
}

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

// https://github.com/microsoft/TypeScript/issues/42678
const [
a,
>a : number

b,
>b : number

] = ((): [number, number] => {
>((): [number, number] => { (() => console.log(a))(); // should error console.log(a); // should error const b = () => a; // should be ok return [ 0, 0, ]; })() : [number, number]
>((): [number, number] => { (() => console.log(a))(); // should error console.log(a); // should error const b = () => a; // should be ok return [ 0, 0, ]; }) : () => [number, number]
>(): [number, number] => { (() => console.log(a))(); // should error console.log(a); // should error const b = () => a; // should be ok return [ 0, 0, ]; } : () => [number, number]

(() => console.log(a))(); // should error
>(() => console.log(a))() : void
>(() => console.log(a)) : () => void
>() => console.log(a) : () => void
>console.log(a) : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>a : number

console.log(a); // should error
>console.log(a) : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>a : number

const b = () => a; // should be ok
>b : () => number
>() => a : () => number
>a : number

return [
>[ 0, 0, ] : [number, number]

0,
>0 : 0

0,
>0 : 0

];
})();
}

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

let [a] = (() => a)();
>a : any
>(() => a)() : any
>(() => a) : () => any
>() => a : () => any
>a : any
}

Expand Up @@ -2,10 +2,11 @@ tests/cases/compiler/tryCatchFinallyControlFlow.ts(105,5): error TS7027: Unreach
tests/cases/compiler/tryCatchFinallyControlFlow.ts(118,9): error TS7027: Unreachable code detected.
tests/cases/compiler/tryCatchFinallyControlFlow.ts(218,13): error TS7027: Unreachable code detected.
tests/cases/compiler/tryCatchFinallyControlFlow.ts(220,9): error TS7027: Unreachable code detected.
tests/cases/compiler/tryCatchFinallyControlFlow.ts(255,9): error TS2448: Block-scoped variable 'x' used before its declaration.
tests/cases/compiler/tryCatchFinallyControlFlow.ts(255,9): error TS7027: Unreachable code detected.


==== tests/cases/compiler/tryCatchFinallyControlFlow.ts (5 errors) ====
==== tests/cases/compiler/tryCatchFinallyControlFlow.ts (6 errors) ====
// Repro from #34797

function f1() {
Expand Down Expand Up @@ -270,6 +271,9 @@ tests/cases/compiler/tryCatchFinallyControlFlow.ts(255,9): error TS7027: Unreach
return null;
}
x; // Unreachable
~
!!! error TS2448: Block-scoped variable 'x' used before its declaration.
!!! related TS2728 tests/cases/compiler/tryCatchFinallyControlFlow.ts:248:11: 'x' is declared here.
~~
!!! error TS7027: Unreachable code detected.
})();
Expand Down
20 changes: 20 additions & 0 deletions tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts
Expand Up @@ -102,3 +102,23 @@ function foo14() {
}
let x
}

function foo15() {
// https://github.com/microsoft/TypeScript/issues/42678
const [
a,
b,
] = ((): [number, number] => {
(() => console.log(a))(); // should error
console.log(a); // should error
const b = () => a; // should be ok
return [
0,
0,
];
})();
}

function foo16() {
let [a] = (() => a)();
}