Skip to content

Commit d3246a3

Browse files
committed
addressed PR feedback
1 parent 751b1ae commit d3246a3

File tree

4 files changed

+69
-37
lines changed

4 files changed

+69
-37
lines changed

src/compiler/checker.ts

Lines changed: 40 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -435,53 +435,60 @@ module ts {
435435
return undefined;
436436
}
437437
if (result.flags & SymbolFlags.BlockScopedVariable) {
438-
// Block-scoped variables cannot be used before their definition
439-
var declaration = forEach(result.declarations, d => isBlockOrCatchScoped(d) ? d : undefined);
438+
checkResolvedBlockScopedVariable(result, errorLocation);
439+
}
440+
}
441+
return result;
442+
}
443+
444+
function checkResolvedBlockScopedVariable(result: Symbol, errorLocation: Node): void {
445+
Debug.assert((result.flags & SymbolFlags.BlockScopedVariable) !== 0)
446+
// Block-scoped variables cannot be used before their definition
447+
var declaration = forEach(result.declarations, d => isBlockOrCatchScoped(d) ? d : undefined);
440448

441-
Debug.assert(declaration !== undefined, "Block-scoped variable declaration is undefined");
449+
Debug.assert(declaration !== undefined, "Block-scoped variable declaration is undefined");
442450

443-
// first check if usage is lexically located after the declaration
444-
var isUsedBeforeDeclaration = !isDefinedBefore(declaration, errorLocation);
445-
if (!isUsedBeforeDeclaration) {
446-
// lexical check succedded however code still can be illegal.
447-
// - block scoped variables cannot be used in its initializers
448-
// let x = x; // illegal but usage is lexically after definition
449-
// - in ForIn/ForOf statements variable cannot be contained in expression part
450-
// for (let x in x)
451-
// for (let x of x)
451+
// first check if usage is lexically located after the declaration
452+
var isUsedBeforeDeclaration = !isDefinedBefore(declaration, errorLocation);
453+
if (!isUsedBeforeDeclaration) {
454+
// lexical check succeeded however code still can be illegal.
455+
// - block scoped variables cannot be used in its initializers
456+
// let x = x; // illegal but usage is lexically after definition
457+
// - in ForIn/ForOf statements variable cannot be contained in expression part
458+
// for (let x in x)
459+
// for (let x of x)
452460

453-
// climb up to the variable declaration skipping binding patterns
454-
var variableDeclaration = <VariableDeclaration>getAncestor(declaration, SyntaxKind.VariableDeclaration);
455-
var container = getEnclosingBlockScopeContainer(variableDeclaration);
461+
// climb up to the variable declaration skipping binding patterns
462+
var variableDeclaration = <VariableDeclaration>getAncestor(declaration, SyntaxKind.VariableDeclaration);
463+
var container = getEnclosingBlockScopeContainer(variableDeclaration);
456464

457-
if (variableDeclaration.parent.parent.kind === SyntaxKind.VariableStatement ||
458-
variableDeclaration.parent.parent.kind === SyntaxKind.ForStatement) {
459-
// variable statement/for statement case, use site should not be inside initializer
460-
isUsedBeforeDeclaration = isChildNode(errorLocation, variableDeclaration.initializer, container);
461-
}
462-
else if (variableDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement ||
463-
variableDeclaration.parent.parent.kind === SyntaxKind.ForInStatement) {
464-
// ForIn/ForOf case - use site should not be used in expression part
465-
isUsedBeforeDeclaration = isChildNode(errorLocation, (<ForInStatement>variableDeclaration.parent.parent).expression, container);
466-
}
467-
}
468-
if (isUsedBeforeDeclaration) {
469-
error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(declaration.name));
470-
}
465+
if (variableDeclaration.parent.parent.kind === SyntaxKind.VariableStatement ||
466+
variableDeclaration.parent.parent.kind === SyntaxKind.ForStatement) {
467+
// variable statement/for statement case,
468+
// use site should not be inside variable declaration (initializer of declaration or binding element)
469+
isUsedBeforeDeclaration = isDescendentOf(errorLocation, variableDeclaration, container);
470+
}
471+
else if (variableDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement ||
472+
variableDeclaration.parent.parent.kind === SyntaxKind.ForInStatement) {
473+
// ForIn/ForOf case - use site should not be used in expression part
474+
var expression = (<ForInStatement>variableDeclaration.parent.parent).expression;
475+
isUsedBeforeDeclaration = isDescendentOf(errorLocation, expression, container);
471476
}
472477
}
473-
return result;
478+
if (isUsedBeforeDeclaration) {
479+
error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(declaration.name));
480+
}
474481
}
475482

476483
/* Starting from 'initial' node walk up the parent chain until 'stopAt' node is reached.
477484
* If at any point current node is equal to 'parent' node - return true.
478-
* Return false if 'stopAt' node is reached.
485+
* Return false if 'stopAt' node is reached or isFunctionLike(current) === true.
479486
*/
480-
function isChildNode(initial: Node, parent: Node, stopAt: Node): boolean {
487+
function isDescendentOf(initial: Node, parent: Node, stopAt: Node): boolean {
481488
if (!parent) {
482489
return false;
483490
}
484-
for (var current = initial; current && current !== stopAt; current = current.parent) {
491+
for (var current = initial; current && current !== stopAt && !isFunctionLike(current); current = current.parent) {
485492
if (current === parent) {
486493
return true;
487494
}

tests/baselines/reference/recursiveLetConst.errors.txt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ tests/cases/compiler/recursiveLetConst.ts(7,16): error TS2448: Block-scoped vari
77
tests/cases/compiler/recursiveLetConst.ts(8,15): error TS2448: Block-scoped variable 'v' used before its declaration.
88
tests/cases/compiler/recursiveLetConst.ts(9,15): error TS2448: Block-scoped variable 'v' used before its declaration.
99
tests/cases/compiler/recursiveLetConst.ts(10,17): error TS2448: Block-scoped variable 'v' used before its declaration.
10+
tests/cases/compiler/recursiveLetConst.ts(11,11): error TS2448: Block-scoped variable 'x2' used before its declaration.
1011

1112

12-
==== tests/cases/compiler/recursiveLetConst.ts (9 errors) ====
13+
==== tests/cases/compiler/recursiveLetConst.ts (10 errors) ====
1314
'use strict'
1415
let x = x + 1;
1516
~
@@ -37,4 +38,10 @@ tests/cases/compiler/recursiveLetConst.ts(10,17): error TS2448: Block-scoped var
3738
!!! error TS2448: Block-scoped variable 'v' used before its declaration.
3839
for (let [v] of v) { }
3940
~
40-
!!! error TS2448: Block-scoped variable 'v' used before its declaration.
41+
!!! error TS2448: Block-scoped variable 'v' used before its declaration.
42+
let [x2 = x2] = []
43+
~~
44+
!!! error TS2448: Block-scoped variable 'x2' used before its declaration.
45+
let z0 = () => z0;
46+
let z1 = function () { return z1; }
47+
let z2 = { f() { return z2;}}

tests/baselines/reference/recursiveLetConst.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ for (let v = v; ; ) { }
88
for (let [v] = v; ;) { }
99
for (let v in v) { }
1010
for (let v of v) { }
11-
for (let [v] of v) { }
11+
for (let [v] of v) { }
12+
let [x2 = x2] = []
13+
let z0 = () => z0;
14+
let z1 = function () { return z1; }
15+
let z2 = { f() { return z2;}}
1216

1317
//// [recursiveLetConst.js]
1418
'use strict';
@@ -26,3 +30,13 @@ for (let v of v) {
2630
}
2731
for (let [v] of v) {
2832
}
33+
let [x2 = x2] = [];
34+
let z0 = () => z0;
35+
let z1 = function () {
36+
return z1;
37+
};
38+
let z2 = {
39+
f() {
40+
return z2;
41+
}
42+
};

tests/cases/compiler/recursiveLetConst.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,8 @@ for (let v = v; ; ) { }
88
for (let [v] = v; ;) { }
99
for (let v in v) { }
1010
for (let v of v) { }
11-
for (let [v] of v) { }
11+
for (let [v] of v) { }
12+
let [x2 = x2] = []
13+
let z0 = () => z0;
14+
let z1 = function () { return z1; }
15+
let z2 = { f() { return z2;}}

0 commit comments

Comments
 (0)