Skip to content

Commit 3cea68d

Browse files
committed
Merge pull request #2353 from Microsoft/shadowingNameViaBindingPattern
consider binding elements as always initialized with doing shadow check
2 parents 1bb4a62 + fac3cf8 commit 3cea68d

File tree

4 files changed

+111
-30
lines changed

4 files changed

+111
-30
lines changed

src/compiler/checker.ts

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8634,36 +8634,48 @@ module ts {
86348634
// const x = 0; // localDeclarationSymbol obtained after name resolution will correspond to this declaration
86358635
// let x = 0; // symbol for this declaration will be 'symbol'
86368636
// }
8637-
if (node.initializer && (getCombinedNodeFlags(node) & NodeFlags.BlockScoped) === 0) {
8638-
let symbol = getSymbolOfNode(node);
8639-
if (symbol.flags & SymbolFlags.FunctionScopedVariable) {
8640-
let localDeclarationSymbol = resolveName(node, (<Identifier>node.name).text, SymbolFlags.Variable, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
8641-
if (localDeclarationSymbol &&
8642-
localDeclarationSymbol !== symbol &&
8643-
localDeclarationSymbol.flags & SymbolFlags.BlockScopedVariable) {
8644-
if (getDeclarationFlagsFromSymbol(localDeclarationSymbol) & NodeFlags.BlockScoped) {
8645-
8646-
let varDeclList = getAncestor(localDeclarationSymbol.valueDeclaration, SyntaxKind.VariableDeclarationList);
8647-
let container =
8648-
varDeclList.parent.kind === SyntaxKind.VariableStatement &&
8649-
varDeclList.parent.parent;
8650-
8651-
// names of block-scoped and function scoped variables can collide only
8652-
// if block scoped variable is defined in the function\module\source file scope (because of variable hoisting)
8653-
let namesShareScope =
8654-
container &&
8655-
(container.kind === SyntaxKind.Block && isFunctionLike(container.parent) ||
8656-
(container.kind === SyntaxKind.ModuleBlock && container.kind === SyntaxKind.ModuleDeclaration) ||
8657-
container.kind === SyntaxKind.SourceFile);
8658-
8659-
// here we know that function scoped variable is shadowed by block scoped one
8660-
// if they are defined in the same scope - binder has already reported redeclaration error
8661-
// otherwise if variable has an initializer - show error that initialization will fail
8662-
// since LHS will be block scoped name instead of function scoped
8663-
if (!namesShareScope) {
8664-
let name = symbolToString(localDeclarationSymbol);
8665-
error(node, Diagnostics.Cannot_initialize_outer_scoped_variable_0_in_the_same_scope_as_block_scoped_declaration_1, name, name);
8666-
}
8637+
8638+
// skip block-scoped variables and parameters
8639+
if ((getCombinedNodeFlags(node) & NodeFlags.BlockScoped) !== 0 || isParameterDeclaration(node)) {
8640+
return;
8641+
}
8642+
8643+
// skip variable declarations that don't have initializers
8644+
// NOTE: in ES6 spec initializer is required in variable declarations where name is binding pattern
8645+
// so we'll always treat binding elements as initialized
8646+
if (node.kind === SyntaxKind.VariableDeclaration && !node.initializer) {
8647+
return;
8648+
}
8649+
8650+
var symbol = getSymbolOfNode(node);
8651+
if (symbol.flags & SymbolFlags.FunctionScopedVariable) {
8652+
let localDeclarationSymbol = resolveName(node, (<Identifier>node.name).text, SymbolFlags.Variable, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
8653+
if (localDeclarationSymbol &&
8654+
localDeclarationSymbol !== symbol &&
8655+
localDeclarationSymbol.flags & SymbolFlags.BlockScopedVariable) {
8656+
if (getDeclarationFlagsFromSymbol(localDeclarationSymbol) & NodeFlags.BlockScoped) {
8657+
let varDeclList = getAncestor(localDeclarationSymbol.valueDeclaration, SyntaxKind.VariableDeclarationList);
8658+
let container =
8659+
varDeclList.parent.kind === SyntaxKind.VariableStatement && varDeclList.parent.parent
8660+
? varDeclList.parent.parent
8661+
: undefined;
8662+
8663+
// names of block-scoped and function scoped variables can collide only
8664+
// if block scoped variable is defined in the function\module\source file scope (because of variable hoisting)
8665+
let namesShareScope =
8666+
container &&
8667+
(container.kind === SyntaxKind.Block && isFunctionLike(container.parent) ||
8668+
container.kind === SyntaxKind.ModuleBlock ||
8669+
container.kind === SyntaxKind.ModuleDeclaration ||
8670+
container.kind === SyntaxKind.SourceFile);
8671+
8672+
// here we know that function scoped variable is shadowed by block scoped one
8673+
// if they are defined in the same scope - binder has already reported redeclaration error
8674+
// otherwise if variable has an initializer - show error that initialization will fail
8675+
// since LHS will be block scoped name instead of function scoped
8676+
if (!namesShareScope) {
8677+
let name = symbolToString(localDeclarationSymbol);
8678+
error(node, Diagnostics.Cannot_initialize_outer_scoped_variable_0_in_the_same_scope_as_block_scoped_declaration_1, name, name);
86678679
}
86688680
}
86698681
}
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 }; // Error
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 }; // Error
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 }; // Error
9+
var { x: x } = { x: 0 }; // Error
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; // Error
27+
var x = ({
28+
_x: 0
29+
}).x; // Error
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 }; // Error
8+
var { x: x } = { x: 0 }; // Error
9+
}
10+
}

0 commit comments

Comments
 (0)