mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-18 07:29:16 -05:00
added check that var and let\const cannot share scope, added check that var is not shadowed by the let\const from the inner scope
This commit is contained in:
@@ -482,6 +482,16 @@ module ts {
|
||||
break;
|
||||
}
|
||||
case SyntaxKind.Block:
|
||||
// do not treat function block a block-scope container
|
||||
// all block-scope locals that reside in this block should go to the function locals.
|
||||
// Otherwise this won't be considered as redeclaration of a block scoped local:
|
||||
// function foo() {
|
||||
// let x;
|
||||
// var x;
|
||||
// }
|
||||
// 'var x' will be placed into the function locals and 'let x' - into the locals of the block
|
||||
bindChildren(node, 0, /*isBlockScopeContainer*/ !isAnyFunction(node.parent));
|
||||
break;
|
||||
case SyntaxKind.CatchClause:
|
||||
case SyntaxKind.ForStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
|
||||
@@ -8198,19 +8198,26 @@ module ts {
|
||||
}
|
||||
}
|
||||
|
||||
function checkCollisionWithConstDeclarations(node: VariableLikeDeclaration) {
|
||||
function checkVarDeclaredNamesNotShadowed(node: VariableDeclaration | BindingElement) {
|
||||
// - ScriptBody : StatementList
|
||||
// It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList
|
||||
// also occurs in the VarDeclaredNames of StatementList.
|
||||
|
||||
// - Block : { StatementList }
|
||||
// It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList
|
||||
// also occurs in the VarDeclaredNames of StatementList.
|
||||
|
||||
// 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.
|
||||
// step on a let\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
|
||||
// are handled by the binder.
|
||||
// We are only looking for var declarations that step on let\const declarations from a
|
||||
// different scope. e.g.:
|
||||
// var x = 0;
|
||||
// {
|
||||
@@ -8219,11 +8226,33 @@ module ts {
|
||||
// }
|
||||
if (node.initializer && (getCombinedNodeFlags(node) & NodeFlags.BlockScoped) === 0) {
|
||||
var symbol = getSymbolOfNode(node);
|
||||
if (symbol.flags & SymbolFlags.FunctionScopedVariable) {
|
||||
if (symbol.flags & (SymbolFlags.FunctionScopedVariable)) {
|
||||
var localDeclarationSymbol = resolveName(node, (<Identifier>node.name).text, SymbolFlags.Variable, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
|
||||
if (localDeclarationSymbol && localDeclarationSymbol !== symbol && localDeclarationSymbol.flags & SymbolFlags.BlockScopedVariable) {
|
||||
if (getDeclarationFlagsFromSymbol(localDeclarationSymbol) & NodeFlags.Const) {
|
||||
error(node, Diagnostics.Cannot_redeclare_block_scoped_variable_0, symbolToString(localDeclarationSymbol));
|
||||
if (localDeclarationSymbol &&
|
||||
localDeclarationSymbol !== symbol &&
|
||||
localDeclarationSymbol.flags & SymbolFlags.BlockScopedVariable) {
|
||||
if (getDeclarationFlagsFromSymbol(localDeclarationSymbol) & (NodeFlags.Let | NodeFlags.Const)) {
|
||||
// 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
|
||||
|
||||
var localVarDeclList = getAncestor(localDeclarationSymbol.valueDeclaration, SyntaxKind.VariableDeclarationList);
|
||||
var localContainer =
|
||||
localVarDeclList.parent.kind === SyntaxKind.VariableStatement &&
|
||||
localVarDeclList.parent.parent;
|
||||
|
||||
// if block scoped variable is defined in the function\module\source file scope
|
||||
// then since function scoped variable is hoised their names will collide
|
||||
var namesShareScope =
|
||||
localContainer &&
|
||||
(localContainer.kind === SyntaxKind.Block && isAnyFunction(localContainer.parent) ||
|
||||
(localContainer.kind === SyntaxKind.ModuleBlock && localContainer.kind === SyntaxKind.ModuleDeclaration) ||
|
||||
localContainer.kind === SyntaxKind.SourceFile);
|
||||
|
||||
if (!namesShareScope) {
|
||||
error(getErrorSpanForNode(node), Diagnostics.Cannot_initialize_outer_scope_variable_0_when_having_block_scoped_variable_with_the_same_name, symbolToString(localDeclarationSymbol));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8320,7 +8349,9 @@ module ts {
|
||||
if (node.kind !== SyntaxKind.PropertyDeclaration && node.kind !== SyntaxKind.PropertySignature) {
|
||||
// We know we don't have a binding pattern or computed name here
|
||||
checkExportsOnMergedDeclarations(node);
|
||||
checkCollisionWithConstDeclarations(node);
|
||||
if (node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) {
|
||||
checkVarDeclaredNamesNotShadowed(<VariableDeclaration | BindingElement>node);
|
||||
}
|
||||
checkCollisionWithCapturedSuperVariable(node, <Identifier>node.name);
|
||||
checkCollisionWithCapturedThisVariable(node, <Identifier>node.name);
|
||||
checkCollisionWithRequireExportsInGeneratedCode(node, <Identifier>node.name);
|
||||
|
||||
@@ -381,6 +381,7 @@ module ts {
|
||||
const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN: { code: 4087, category: DiagnosticCategory.Error, key: "'const' enum member initializer was evaluated to disallowed value 'NaN'." },
|
||||
Property_0_does_not_exist_on_const_enum_1: { code: 4088, category: DiagnosticCategory.Error, key: "Property '{0}' does not exist on 'const' enum '{1}'." },
|
||||
let_is_not_allowed_to_be_used_as_a_name_in_let_or_const_declarations: { code: 4089, category: DiagnosticCategory.Error, key: "'let' is not allowed to be used as a name in 'let' or 'const' declarations." },
|
||||
Cannot_initialize_outer_scope_variable_0_when_having_block_scoped_variable_with_the_same_name: { code: 4090, category: DiagnosticCategory.Error, key: "Cannot initialize outer scope variable '{0}' when having block-scoped variable with the same name." },
|
||||
The_current_host_does_not_support_the_0_option: { code: 5001, category: DiagnosticCategory.Error, key: "The current host does not support the '{0}' option." },
|
||||
Cannot_find_the_common_subdirectory_path_for_the_input_files: { code: 5009, category: DiagnosticCategory.Error, key: "Cannot find the common subdirectory path for the input files." },
|
||||
Cannot_read_file_0_Colon_1: { code: 5012, category: DiagnosticCategory.Error, key: "Cannot read file '{0}': {1}" },
|
||||
|
||||
@@ -1517,6 +1517,10 @@
|
||||
"category": "Error",
|
||||
"code": 4089
|
||||
},
|
||||
"Cannot initialize outer scope variable '{0}' when having block-scoped variable with the same name.": {
|
||||
"category": "Error",
|
||||
"code": 4090
|
||||
},
|
||||
"The current host does not support the '{0}' option.": {
|
||||
"category": "Error",
|
||||
"code": 5001
|
||||
|
||||
Reference in New Issue
Block a user