diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 782ffccc0ed..000ee35d21f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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, (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, (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); } } } diff --git a/tests/baselines/reference/shadowingViaLocalValueOrBindingElement.errors.txt b/tests/baselines/reference/shadowingViaLocalValueOrBindingElement.errors.txt new file mode 100644 index 00000000000..663f83c1ffb --- /dev/null +++ b/tests/baselines/reference/shadowingViaLocalValueOrBindingElement.errors.txt @@ -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'. + } + } \ No newline at end of file diff --git a/tests/baselines/reference/shadowingViaLocalValueOrBindingElement.js b/tests/baselines/reference/shadowingViaLocalValueOrBindingElement.js new file mode 100644 index 00000000000..8d6319f5ea7 --- /dev/null +++ b/tests/baselines/reference/shadowingViaLocalValueOrBindingElement.js @@ -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 + } +} diff --git a/tests/cases/compiler/shadowingViaLocalValueOrBindingElement.ts b/tests/cases/compiler/shadowingViaLocalValueOrBindingElement.ts new file mode 100644 index 00000000000..9afeb79aa79 --- /dev/null +++ b/tests/cases/compiler/shadowingViaLocalValueOrBindingElement.ts @@ -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 + } +} \ No newline at end of file