From 82f5fb4055c98ded41f6d6d275cc06c2f286ab2b Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Tue, 14 Oct 2014 17:15:11 -0700 Subject: [PATCH] Flag const declarations shodowed by var redeclarations --- src/compiler/checker.ts | 32 ++++++++++++++ .../diagnosticInformationMap.generated.ts | 1 + src/compiler/diagnosticMessages.json | 4 ++ ...arationShadowedByVarDeclaration.errors.txt | 35 +++++++++++++++ ...onstDeclarationShadowedByVarDeclaration.js | 43 +++++++++++++++++++ ...onstDeclarationShadowedByVarDeclaration.ts | 24 +++++++++++ 6 files changed, 139 insertions(+) create mode 100644 tests/baselines/reference/constDeclarationShadowedByVarDeclaration.errors.txt create mode 100644 tests/baselines/reference/constDeclarationShadowedByVarDeclaration.js create mode 100644 tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5bba99c6d4..6927be355a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6174,6 +6174,36 @@ module ts { } } + function checkCollisionsWithConstDeclarations(node: VariableDeclaration) { + // Variable declarations are hoisted to the top of their function scope. They can shadow + // block scoped declarations, which bind tighter. this will not be flagged as duplicate definition + // by the binder as the declaration scope is different. + // A non-initialized declaration is a no-op as the block declaration will resolve before the var + // declaration. the problem is if the declaration has an initializer. this will act as a write to the + // block declared value. this is fine for let, but not const. + // + // Only consider declarations with initializers, uninitialized var declarations will not + // step on a const variable. + // Do not consider let and const declarations, as duplicate block-scoped declarations + // are handled by the binder. + // We are only looking for var declarations that step on const declarations from a + // different scope. e.g.: + // var x = 0; + // { + // const x = 0; + // var x = 0; + // } + if (node.initializer && (node.flags & NodeFlags.BlockScoped) === 0) { + var symbol = getSymbolOfNode(node); + var localDeclarationSymbol = resolveName(node, node.name.text, SymbolFlags.BlockScoped, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined); + if (localDeclarationSymbol && localDeclarationSymbol !== symbol) { + if (getDeclarationFlagsFromSymbol(localDeclarationSymbol) & NodeFlags.Const) { + error(node, Diagnostics.Cannot_redeclare_constant_0, symbolToString(localDeclarationSymbol)); + } + } + } + } + function checkVariableDeclaration(node: VariableDeclaration) { checkSourceElement(node.type); checkExportsOnMergedDeclarations(node); @@ -6197,6 +6227,8 @@ module ts { // Use default messages checkTypeAssignableTo(checkAndMarkExpression(node.initializer), type, node, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined); } + + checkCollisionsWithConstDeclarations(node); } checkCollisionWithCapturedSuperVariable(node, node.name); diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 5d4acbbc67..86a66c468a 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -272,6 +272,7 @@ module ts { Block_scoped_variable_0_used_before_its_declaration: { code: 2448, category: DiagnosticCategory.Error, key: "Block-scoped variable '{0}' used before its declaration." }, 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." }, 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." }, + Cannot_redeclare_constant_0: { code: 2451, category: DiagnosticCategory.Error, key: "Cannot redeclare constant '{0}'." }, Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." }, 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}'." }, 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}'." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 937d6cdd61..ebe9a815b5 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1080,6 +1080,10 @@ "category": "Error", "code": 2450 }, + "Cannot redeclare constant '{0}'.": { + "category": "Error", + "code": 2451 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/tests/baselines/reference/constDeclarationShadowedByVarDeclaration.errors.txt b/tests/baselines/reference/constDeclarationShadowedByVarDeclaration.errors.txt new file mode 100644 index 0000000000..662628e069 --- /dev/null +++ b/tests/baselines/reference/constDeclarationShadowedByVarDeclaration.errors.txt @@ -0,0 +1,35 @@ +tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts(7,9): error TS2451: Cannot redeclare constant 'x'. +tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts(15,13): error TS2451: Cannot redeclare constant 'y'. +tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts(22,7): error TS2451: Cannot redeclare constant 'z'. + + +==== tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts (3 errors) ==== + + // Error as declaration of var would cause a write to the const value + var x = 0; + { + const x = 0; + + var x = 0; + ~ +!!! error TS2451: Cannot redeclare constant 'x'. + } + + + var y = 0; + { + const y = 0; + { + var y = 0; + ~ +!!! error TS2451: Cannot redeclare constant 'y'. + } + } + + + { + const z = 0; + var z = 0 + ~ +!!! error TS2451: Cannot redeclare constant 'z'. + } \ No newline at end of file diff --git a/tests/baselines/reference/constDeclarationShadowedByVarDeclaration.js b/tests/baselines/reference/constDeclarationShadowedByVarDeclaration.js new file mode 100644 index 0000000000..9fcc9f8f56 --- /dev/null +++ b/tests/baselines/reference/constDeclarationShadowedByVarDeclaration.js @@ -0,0 +1,43 @@ +//// [constDeclarationShadowedByVarDeclaration.ts] + +// Error as declaration of var would cause a write to the const value +var x = 0; +{ + const x = 0; + + var x = 0; +} + + +var y = 0; +{ + const y = 0; + { + var y = 0; + } +} + + +{ + const z = 0; + var z = 0 +} + +//// [constDeclarationShadowedByVarDeclaration.js] +// Error as declaration of var would cause a write to the const value +var x = 0; +{ + const x = 0; + var x = 0; +} +var y = 0; +{ + const y = 0; + { + var y = 0; + } +} +{ + const z = 0; + var z = 0; +} diff --git a/tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts b/tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts new file mode 100644 index 0000000000..a4d3923384 --- /dev/null +++ b/tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts @@ -0,0 +1,24 @@ +// @target: ES6 + +// Error as declaration of var would cause a write to the const value +var x = 0; +{ + const x = 0; + + var x = 0; +} + + +var y = 0; +{ + const y = 0; + { + var y = 0; + } +} + + +{ + const z = 0; + var z = 0 +} \ No newline at end of file