@@ -435,53 +435,60 @@ module ts {
435
435
return undefined;
436
436
}
437
437
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);
440
448
441
- Debug.assert(declaration !== undefined, "Block-scoped variable declaration is undefined");
449
+ Debug.assert(declaration !== undefined, "Block-scoped variable declaration is undefined");
442
450
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)
452
460
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);
456
464
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);
471
476
}
472
477
}
473
- return result;
478
+ if (isUsedBeforeDeclaration) {
479
+ error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(declaration.name));
480
+ }
474
481
}
475
482
476
483
/* Starting from 'initial' node walk up the parent chain until 'stopAt' node is reached.
477
484
* 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 .
479
486
*/
480
- function isChildNode (initial: Node, parent: Node, stopAt: Node): boolean {
487
+ function isDescendentOf (initial: Node, parent: Node, stopAt: Node): boolean {
481
488
if (!parent) {
482
489
return false;
483
490
}
484
- for (var current = initial; current && current !== stopAt; current = current.parent) {
491
+ for (var current = initial; current && current !== stopAt && !isFunctionLike(current) ; current = current.parent) {
485
492
if (current === parent) {
486
493
return true;
487
494
}
0 commit comments