Skip to content

Commit 0675a92

Browse files
committed
consider binding elements as always initialized with doing shadow check
1 parent 82a940d commit 0675a92

File tree

4 files changed

+110
-30
lines changed

4 files changed

+110
-30
lines changed

src/compiler/checker.ts

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8609,36 +8609,47 @@ module ts {
86098609
// const x = 0; // localDeclarationSymbol obtained after name resolution will correspond to this declaration
86108610
// var x = 0; // symbol for this declaration will be 'symbol'
86118611
// }
8612-
if (node.initializer && (getCombinedNodeFlags(node) & NodeFlags.BlockScoped) === 0) {
8613-
var symbol = getSymbolOfNode(node);
8614-
if (symbol.flags & SymbolFlags.FunctionScopedVariable) {
8615-
var localDeclarationSymbol = resolveName(node, (<Identifier>node.name).text, SymbolFlags.Variable, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
8616-
if (localDeclarationSymbol &&
8617-
localDeclarationSymbol !== symbol &&
8618-
localDeclarationSymbol.flags & SymbolFlags.BlockScopedVariable) {
8619-
if (getDeclarationFlagsFromSymbol(localDeclarationSymbol) & NodeFlags.BlockScoped) {
8620-
8621-
var varDeclList = getAncestor(localDeclarationSymbol.valueDeclaration, SyntaxKind.VariableDeclarationList);
8622-
var container =
8623-
varDeclList.parent.kind === SyntaxKind.VariableStatement &&
8624-
varDeclList.parent.parent;
8625-
8626-
// names of block-scoped and function scoped variables can collide only
8627-
// if block scoped variable is defined in the function\module\source file scope (because of variable hoisting)
8628-
var namesShareScope =
8629-
container &&
8630-
(container.kind === SyntaxKind.Block && isFunctionLike(container.parent) ||
8631-
(container.kind === SyntaxKind.ModuleBlock && container.kind === SyntaxKind.ModuleDeclaration) ||
8632-
container.kind === SyntaxKind.SourceFile);
8633-
8634-
// here we know that function scoped variable is shadowed by block scoped one
8635-
// if they are defined in the same scope - binder has already reported redeclaration error
8636-
// otherwise if variable has an initializer - show error that initialization will fail
8637-
// since LHS will be block scoped name instead of function scoped
8638-
if (!namesShareScope) {
8639-
var name = symbolToString(localDeclarationSymbol);
8640-
error(node, Diagnostics.Cannot_initialize_outer_scoped_variable_0_in_the_same_scope_as_block_scoped_declaration_1, name, name);
8641-
}
8612+
8613+
// skip block-scoped variables and parameters
8614+
if ((getCombinedNodeFlags(node) & NodeFlags.BlockScoped) !== 0 || isParameterDeclaration(node)) {
8615+
return;
8616+
}
8617+
8618+
// skip variable declarations that don't have initializers
8619+
// NOTE: in ES6 spec initializer is required in variable declarations where name is binding pattern
8620+
// so we'll always treat binding elements as initialized
8621+
if (node.kind === SyntaxKind.VariableDeclaration && !node.initializer) {
8622+
return;
8623+
}
8624+
8625+
var symbol = getSymbolOfNode(node);
8626+
if (symbol.flags & SymbolFlags.FunctionScopedVariable) {
8627+
var localDeclarationSymbol = resolveName(node, (<Identifier>node.name).text, SymbolFlags.Variable, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
8628+
if (localDeclarationSymbol &&
8629+
localDeclarationSymbol !== symbol &&
8630+
localDeclarationSymbol.flags & SymbolFlags.BlockScopedVariable) {
8631+
if (getDeclarationFlagsFromSymbol(localDeclarationSymbol) & NodeFlags.BlockScoped) {
8632+
8633+
var varDeclList = getAncestor(localDeclarationSymbol.valueDeclaration, SyntaxKind.VariableDeclarationList);
8634+
var container =
8635+
varDeclList.parent.kind === SyntaxKind.VariableStatement &&
8636+
varDeclList.parent.parent;
8637+
8638+
// names of block-scoped and function scoped variables can collide only
8639+
// if block scoped variable is defined in the function\module\source file scope (because of variable hoisting)
8640+
var namesShareScope =
8641+
container &&
8642+
(container.kind === SyntaxKind.Block && isFunctionLike(container.parent) ||
8643+
(container.kind === SyntaxKind.ModuleBlock && container.kind === SyntaxKind.ModuleDeclaration) ||
8644+
container.kind === SyntaxKind.SourceFile);
8645+
8646+
// here we know that function scoped variable is shadowed by block scoped one
8647+
// if they are defined in the same scope - binder has already reported redeclaration error
8648+
// otherwise if variable has an initializer - show error that initialization will fail
8649+
// since LHS will be block scoped name instead of function scoped
8650+
if (!namesShareScope) {
8651+
var name = symbolToString(localDeclarationSymbol);
8652+
error(node, Diagnostics.Cannot_initialize_outer_scoped_variable_0_in_the_same_scope_as_block_scoped_declaration_1, name, name);
86428653
}
86438654
}
86448655
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
tests/cases/compiler/shadowingViaLocalValueOrBindingElement.ts(4,13): error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
2+
tests/cases/compiler/shadowingViaLocalValueOrBindingElement.ts(5,15): error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
3+
tests/cases/compiler/shadowingViaLocalValueOrBindingElement.ts(6,18): error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
4+
tests/cases/compiler/shadowingViaLocalValueOrBindingElement.ts(7,15): error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
5+
tests/cases/compiler/shadowingViaLocalValueOrBindingElement.ts(8,18): error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
6+
7+
8+
==== tests/cases/compiler/shadowingViaLocalValueOrBindingElement.ts (5 errors) ====
9+
if (true) {
10+
let x;
11+
if (true) {
12+
var x = 0; // Error
13+
~
14+
!!! error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
15+
var { x = 0 } = { x: 0 }; // Error
16+
~
17+
!!! error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
18+
var { x: x = 0 } = { x: 0 }; // Error
19+
~
20+
!!! error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
21+
var { x } = { x: 0 }; // No error, even though the let x is being initialized
22+
~
23+
!!! error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
24+
var { x: x } = { x: 0 }; // No error, even though the let x is being initialized
25+
~
26+
!!! error TS2481: Cannot initialize outer scoped variable 'x' in the same scope as block scoped declaration 'x'.
27+
}
28+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//// [shadowingViaLocalValueOrBindingElement.ts]
2+
if (true) {
3+
let x;
4+
if (true) {
5+
var x = 0; // Error
6+
var { x = 0 } = { x: 0 }; // Error
7+
var { x: x = 0 } = { x: 0 }; // Error
8+
var { x } = { x: 0 }; // No error, even though the let x is being initialized
9+
var { x: x } = { x: 0 }; // No error, even though the let x is being initialized
10+
}
11+
}
12+
13+
//// [shadowingViaLocalValueOrBindingElement.js]
14+
if (true) {
15+
var _x;
16+
if (true) {
17+
var x = 0; // Error
18+
var _a = ({
19+
_x: 0
20+
}).x, x = _a === void 0 ? 0 : _a; // Error
21+
var _b = ({
22+
_x: 0
23+
}).x, x = _b === void 0 ? 0 : _b; // Error
24+
var x = ({
25+
_x: 0
26+
}).x; // No error, even though the let x is being initialized
27+
var x = ({
28+
_x: 0
29+
}).x; // No error, even though the let x is being initialized
30+
}
31+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
if (true) {
2+
let x;
3+
if (true) {
4+
var x = 0; // Error
5+
var { x = 0 } = { x: 0 }; // Error
6+
var { x: x = 0 } = { x: 0 }; // Error
7+
var { x } = { x: 0 }; // No error, even though the let x is being initialized
8+
var { x: x } = { x: 0 }; // No error, even though the let x is being initialized
9+
}
10+
}

0 commit comments

Comments
 (0)