Skip to content

Commit 82f5fb4

Browse files
committed
Flag const declarations shodowed by var redeclarations
1 parent f5c2740 commit 82f5fb4

6 files changed

+139
-0
lines changed

src/compiler/checker.ts

+32
Original file line numberDiff line numberDiff line change
@@ -6174,6 +6174,36 @@ module ts {
61746174
}
61756175
}
61766176

6177+
function checkCollisionsWithConstDeclarations(node: VariableDeclaration) {
6178+
// Variable declarations are hoisted to the top of their function scope. They can shadow
6179+
// block scoped declarations, which bind tighter. this will not be flagged as duplicate definition
6180+
// by the binder as the declaration scope is different.
6181+
// A non-initialized declaration is a no-op as the block declaration will resolve before the var
6182+
// declaration. the problem is if the declaration has an initializer. this will act as a write to the
6183+
// block declared value. this is fine for let, but not const.
6184+
//
6185+
// Only consider declarations with initializers, uninitialized var declarations will not
6186+
// step on a const variable.
6187+
// Do not consider let and const declarations, as duplicate block-scoped declarations
6188+
// are handled by the binder.
6189+
// We are only looking for var declarations that step on const declarations from a
6190+
// different scope. e.g.:
6191+
// var x = 0;
6192+
// {
6193+
// const x = 0;
6194+
// var x = 0;
6195+
// }
6196+
if (node.initializer && (node.flags & NodeFlags.BlockScoped) === 0) {
6197+
var symbol = getSymbolOfNode(node);
6198+
var localDeclarationSymbol = resolveName(node, node.name.text, SymbolFlags.BlockScoped, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
6199+
if (localDeclarationSymbol && localDeclarationSymbol !== symbol) {
6200+
if (getDeclarationFlagsFromSymbol(localDeclarationSymbol) & NodeFlags.Const) {
6201+
error(node, Diagnostics.Cannot_redeclare_constant_0, symbolToString(localDeclarationSymbol));
6202+
}
6203+
}
6204+
}
6205+
}
6206+
61776207
function checkVariableDeclaration(node: VariableDeclaration) {
61786208
checkSourceElement(node.type);
61796209
checkExportsOnMergedDeclarations(node);
@@ -6197,6 +6227,8 @@ module ts {
61976227
// Use default messages
61986228
checkTypeAssignableTo(checkAndMarkExpression(node.initializer), type, node, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
61996229
}
6230+
6231+
checkCollisionsWithConstDeclarations(node);
62006232
}
62016233

62026234
checkCollisionWithCapturedSuperVariable(node, node.name);

src/compiler/diagnosticInformationMap.generated.ts

+1
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ module ts {
272272
Block_scoped_variable_0_used_before_its_declaration: { code: 2448, category: DiagnosticCategory.Error, key: "Block-scoped variable '{0}' used before its declaration." },
273273
The_operand_of_an_increment_or_decrement_operator_cannot_be_a_constant: { code: 2449, category: DiagnosticCategory.Error, key: "The operand of an increment or decrement operator cannot be a constant." },
274274
Left_hand_side_of_assignment_expression_cannot_be_a_constant: { code: 2450, category: DiagnosticCategory.Error, key: "Left-hand side of assignment expression cannot be a constant." },
275+
Cannot_redeclare_constant_0: { code: 2451, category: DiagnosticCategory.Error, key: "Cannot redeclare constant '{0}'." },
275276
Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." },
276277
Type_parameter_0_of_exported_class_has_or_is_using_name_1_from_private_module_2: { code: 4001, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using name '{1}' from private module '{2}'." },
277278
Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." },

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -1080,6 +1080,10 @@
10801080
"category": "Error",
10811081
"code": 2450
10821082
},
1083+
"Cannot redeclare constant '{0}'.": {
1084+
"category": "Error",
1085+
"code": 2451
1086+
},
10831087

10841088
"Import declaration '{0}' is using private name '{1}'.": {
10851089
"category": "Error",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts(7,9): error TS2451: Cannot redeclare constant 'x'.
2+
tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts(15,13): error TS2451: Cannot redeclare constant 'y'.
3+
tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts(22,7): error TS2451: Cannot redeclare constant 'z'.
4+
5+
6+
==== tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts (3 errors) ====
7+
8+
// Error as declaration of var would cause a write to the const value
9+
var x = 0;
10+
{
11+
const x = 0;
12+
13+
var x = 0;
14+
~
15+
!!! error TS2451: Cannot redeclare constant 'x'.
16+
}
17+
18+
19+
var y = 0;
20+
{
21+
const y = 0;
22+
{
23+
var y = 0;
24+
~
25+
!!! error TS2451: Cannot redeclare constant 'y'.
26+
}
27+
}
28+
29+
30+
{
31+
const z = 0;
32+
var z = 0
33+
~
34+
!!! error TS2451: Cannot redeclare constant 'z'.
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//// [constDeclarationShadowedByVarDeclaration.ts]
2+
3+
// Error as declaration of var would cause a write to the const value
4+
var x = 0;
5+
{
6+
const x = 0;
7+
8+
var x = 0;
9+
}
10+
11+
12+
var y = 0;
13+
{
14+
const y = 0;
15+
{
16+
var y = 0;
17+
}
18+
}
19+
20+
21+
{
22+
const z = 0;
23+
var z = 0
24+
}
25+
26+
//// [constDeclarationShadowedByVarDeclaration.js]
27+
// Error as declaration of var would cause a write to the const value
28+
var x = 0;
29+
{
30+
const x = 0;
31+
var x = 0;
32+
}
33+
var y = 0;
34+
{
35+
const y = 0;
36+
{
37+
var y = 0;
38+
}
39+
}
40+
{
41+
const z = 0;
42+
var z = 0;
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// @target: ES6
2+
3+
// Error as declaration of var would cause a write to the const value
4+
var x = 0;
5+
{
6+
const x = 0;
7+
8+
var x = 0;
9+
}
10+
11+
12+
var y = 0;
13+
{
14+
const y = 0;
15+
{
16+
var y = 0;
17+
}
18+
}
19+
20+
21+
{
22+
const z = 0;
23+
var z = 0
24+
}

0 commit comments

Comments
 (0)