From 0675a92accb49b217b33941ca17f030a6d2bf6db Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Fri, 13 Mar 2015 14:34:10 -0700 Subject: [PATCH 1/3] consider binding elements as always initialized with doing shadow check --- src/compiler/checker.ts | 71 +++++++++++-------- ...ngViaLocalValueOrBindingElement.errors.txt | 28 ++++++++ .../shadowingViaLocalValueOrBindingElement.js | 31 ++++++++ .../shadowingViaLocalValueOrBindingElement.ts | 10 +++ 4 files changed, 110 insertions(+), 30 deletions(-) create mode 100644 tests/baselines/reference/shadowingViaLocalValueOrBindingElement.errors.txt create mode 100644 tests/baselines/reference/shadowingViaLocalValueOrBindingElement.js create mode 100644 tests/cases/compiler/shadowingViaLocalValueOrBindingElement.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 782ffccc0edf8..000ee35d21f42 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; - - // 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); - } + + // skip block-scoped variables and parameters + if ((getCombinedNodeFlags(node) & NodeFlags.BlockScoped) !== 0 || isParameterDeclaration(node)) { + return; + } + + // 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; + } + + 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 0000000000000..663f83c1ffbfc --- /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 0000000000000..8d6319f5ea716 --- /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 0000000000000..9afeb79aa791e --- /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 From d163205da6281cb840312b7c8ee9d5a75512d375 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Fri, 13 Mar 2015 15:59:22 -0700 Subject: [PATCH 2/3] accepted baselines --- .../shadowingViaLocalValueOrBindingElement.errors.txt | 4 ++-- .../reference/shadowingViaLocalValueOrBindingElement.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/baselines/reference/shadowingViaLocalValueOrBindingElement.errors.txt b/tests/baselines/reference/shadowingViaLocalValueOrBindingElement.errors.txt index 663f83c1ffbfc..4fc1d47c3837f 100644 --- a/tests/baselines/reference/shadowingViaLocalValueOrBindingElement.errors.txt +++ b/tests/baselines/reference/shadowingViaLocalValueOrBindingElement.errors.txt @@ -18,10 +18,10 @@ tests/cases/compiler/shadowingViaLocalValueOrBindingElement.ts(8,18): error TS24 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 + var { x } = { x: 0 }; // Error ~ !!! 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 + var { x: x } = { x: 0 }; // Error ~ !!! error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'. } diff --git a/tests/baselines/reference/shadowingViaLocalValueOrBindingElement.js b/tests/baselines/reference/shadowingViaLocalValueOrBindingElement.js index 8d6319f5ea716..76c4a7ac3a646 100644 --- a/tests/baselines/reference/shadowingViaLocalValueOrBindingElement.js +++ b/tests/baselines/reference/shadowingViaLocalValueOrBindingElement.js @@ -5,8 +5,8 @@ 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 + var { x } = { x: 0 }; // Error + var { x: x } = { x: 0 }; // Error } } @@ -23,9 +23,9 @@ if (true) { }).x, x = _b === void 0 ? 0 : _b; // Error var x = ({ _x: 0 - }).x; // No error, even though the let x is being initialized + }).x; // Error var x = ({ _x: 0 - }).x; // No error, even though the let x is being initialized + }).x; // Error } } From fac3cf8b5541216ee7fa01451daffc044986b060 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Sat, 14 Mar 2015 18:50:05 -0700 Subject: [PATCH 3/3] addressed PR feedback --- src/compiler/checker.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1ce946493b00f..e59996c078169 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8625,18 +8625,19 @@ module ts { localDeclarationSymbol !== symbol && localDeclarationSymbol.flags & SymbolFlags.BlockScopedVariable) { if (getDeclarationFlagsFromSymbol(localDeclarationSymbol) & NodeFlags.BlockScoped) { - let varDeclList = getAncestor(localDeclarationSymbol.valueDeclaration, SyntaxKind.VariableDeclarationList); let container = - varDeclList.parent.kind === SyntaxKind.VariableStatement && - varDeclList.parent.parent; + varDeclList.parent.kind === SyntaxKind.VariableStatement && varDeclList.parent.parent + ? varDeclList.parent.parent + : undefined; // 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) let namesShareScope = container && (container.kind === SyntaxKind.Block && isFunctionLike(container.parent) || - (container.kind === SyntaxKind.ModuleBlock && container.kind === SyntaxKind.ModuleDeclaration) || + 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