consider binding elements as always initialized with doing shadow check

This commit is contained in:
Vladimir Matveev 2015-03-13 14:34:10 -07:00
parent 82a940df06
commit 0675a92acc
4 changed files with 107 additions and 27 deletions

View File

@ -8609,36 +8609,47 @@ module ts {
// const x = 0; // localDeclarationSymbol obtained after name resolution will correspond to this declaration
// var x = 0; // symbol for this declaration will be 'symbol'
// }
if (node.initializer && (getCombinedNodeFlags(node) & NodeFlags.BlockScoped) === 0) {
var symbol = getSymbolOfNode(node);
if (symbol.flags & SymbolFlags.FunctionScopedVariable) {
var localDeclarationSymbol = resolveName(node, (<Identifier>node.name).text, SymbolFlags.Variable, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
if (localDeclarationSymbol &&
localDeclarationSymbol !== symbol &&
localDeclarationSymbol.flags & SymbolFlags.BlockScopedVariable) {
if (getDeclarationFlagsFromSymbol(localDeclarationSymbol) & NodeFlags.BlockScoped) {
var varDeclList = getAncestor(localDeclarationSymbol.valueDeclaration, SyntaxKind.VariableDeclarationList);
var container =
varDeclList.parent.kind === SyntaxKind.VariableStatement &&
varDeclList.parent.parent;
// skip block-scoped variables and parameters
if ((getCombinedNodeFlags(node) & NodeFlags.BlockScoped) !== 0 || isParameterDeclaration(node)) {
return;
}
// names of block-scoped and function scoped variables can collide only
// if block scoped variable is defined in the function\module\source file scope (because of variable hoisting)
var namesShareScope =
container &&
(container.kind === SyntaxKind.Block && isFunctionLike(container.parent) ||
(container.kind === SyntaxKind.ModuleBlock && container.kind === SyntaxKind.ModuleDeclaration) ||
container.kind === SyntaxKind.SourceFile);
// skip variable declarations that don't have initializers
// NOTE: in ES6 spec initializer is required in variable declarations where name is binding pattern
// so we'll always treat binding elements as initialized
if (node.kind === SyntaxKind.VariableDeclaration && !node.initializer) {
return;
}
// here we know that function scoped variable is shadowed by block scoped one
// if they are defined in the same scope - binder has already reported redeclaration error
// otherwise if variable has an initializer - show error that initialization will fail
// since LHS will be block scoped name instead of function scoped
if (!namesShareScope) {
var name = symbolToString(localDeclarationSymbol);
error(node, Diagnostics.Cannot_initialize_outer_scoped_variable_0_in_the_same_scope_as_block_scoped_declaration_1, name, name);
}
var symbol = getSymbolOfNode(node);
if (symbol.flags & SymbolFlags.FunctionScopedVariable) {
var localDeclarationSymbol = resolveName(node, (<Identifier>node.name).text, SymbolFlags.Variable, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
if (localDeclarationSymbol &&
localDeclarationSymbol !== symbol &&
localDeclarationSymbol.flags & SymbolFlags.BlockScopedVariable) {
if (getDeclarationFlagsFromSymbol(localDeclarationSymbol) & NodeFlags.BlockScoped) {
var varDeclList = getAncestor(localDeclarationSymbol.valueDeclaration, SyntaxKind.VariableDeclarationList);
var container =
varDeclList.parent.kind === SyntaxKind.VariableStatement &&
varDeclList.parent.parent;
// names of block-scoped and function scoped variables can collide only
// if block scoped variable is defined in the function\module\source file scope (because of variable hoisting)
var namesShareScope =
container &&
(container.kind === SyntaxKind.Block && isFunctionLike(container.parent) ||
(container.kind === SyntaxKind.ModuleBlock && container.kind === SyntaxKind.ModuleDeclaration) ||
container.kind === SyntaxKind.SourceFile);
// here we know that function scoped variable is shadowed by block scoped one
// if they are defined in the same scope - binder has already reported redeclaration error
// otherwise if variable has an initializer - show error that initialization will fail
// since LHS will be block scoped name instead of function scoped
if (!namesShareScope) {
var name = symbolToString(localDeclarationSymbol);
error(node, Diagnostics.Cannot_initialize_outer_scoped_variable_0_in_the_same_scope_as_block_scoped_declaration_1, name, name);
}
}
}

View File

@ -0,0 +1,28 @@
tests/cases/compiler/shadowingViaLocalValueOrBindingElement.ts(4,13): error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
tests/cases/compiler/shadowingViaLocalValueOrBindingElement.ts(5,15): error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
tests/cases/compiler/shadowingViaLocalValueOrBindingElement.ts(6,18): error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
tests/cases/compiler/shadowingViaLocalValueOrBindingElement.ts(7,15): error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
tests/cases/compiler/shadowingViaLocalValueOrBindingElement.ts(8,18): error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
==== tests/cases/compiler/shadowingViaLocalValueOrBindingElement.ts (5 errors) ====
if (true) {
let x;
if (true) {
var x = 0; // Error
~
!!! error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
var { x = 0 } = { x: 0 }; // Error
~
!!! error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
var { x: x = 0 } = { x: 0 }; // Error
~
!!! error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
var { x } = { x: 0 }; // No error, even though the let x is being initialized
~
!!! error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
var { x: x } = { x: 0 }; // No error, even though the let x is being initialized
~
!!! error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
}
}

View File

@ -0,0 +1,31 @@
//// [shadowingViaLocalValueOrBindingElement.ts]
if (true) {
let x;
if (true) {
var x = 0; // Error
var { x = 0 } = { x: 0 }; // Error
var { x: x = 0 } = { x: 0 }; // Error
var { x } = { x: 0 }; // No error, even though the let x is being initialized
var { x: x } = { x: 0 }; // No error, even though the let x is being initialized
}
}
//// [shadowingViaLocalValueOrBindingElement.js]
if (true) {
var _x;
if (true) {
var x = 0; // Error
var _a = ({
_x: 0
}).x, x = _a === void 0 ? 0 : _a; // Error
var _b = ({
_x: 0
}).x, x = _b === void 0 ? 0 : _b; // Error
var x = ({
_x: 0
}).x; // No error, even though the let x is being initialized
var x = ({
_x: 0
}).x; // No error, even though the let x is being initialized
}
}

View File

@ -0,0 +1,10 @@
if (true) {
let x;
if (true) {
var x = 0; // Error
var { x = 0 } = { x: 0 }; // Error
var { x: x = 0 } = { x: 0 }; // Error
var { x } = { x: 0 }; // No error, even though the let x is being initialized
var { x: x } = { x: 0 }; // No error, even though the let x is being initialized
}
}