From 92f2721b4619bfe956c64acace0130dc4ffa0bf1 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 6 Dec 2016 11:48:14 -0800 Subject: [PATCH] Binding initialisers can refer to previous elements --- src/compiler/checker.ts | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 903c8cfda1c..7ea84d093ec 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -638,11 +638,24 @@ namespace ts { if (declaration.pos <= usage.pos) { // declaration is before usage - // still might be illegal if usage is in the initializer of the variable declaration - return declaration.kind !== SyntaxKind.VariableDeclaration || - !isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration, usage); + if (declaration.kind === SyntaxKind.BindingElement) { + // still might be illegal if declaration and usage are both binding elements (eg var [a = b, b = b] = [1, 2]) + const errorBindingElement = getAncestor(usage, SyntaxKind.BindingElement) as BindingElement; + if (errorBindingElement) { + return getAncestorBindingPattern(errorBindingElement) !== getAncestorBindingPattern(declaration) || + declaration.pos < errorBindingElement.pos; + } + // or it might be illegal if usage happens before parent variable is declared (eg var [a] = a) + return isBlockScopedNameDeclaredBeforeUse(getAncestor(declaration, SyntaxKind.VariableDeclaration) as Declaration, usage); + } + else if (declaration.kind === SyntaxKind.VariableDeclaration) { + // still might be illegal if usage is in the initializer of the variable declaration (eg var a = a) + return !isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration as VariableDeclaration, usage); + } + return true; } + // declaration is after usage // can be legal if usage is deferred (i.e. inside function or in initializer of instance property) const container = getEnclosingBlockScopeContainer(declaration); @@ -699,6 +712,16 @@ namespace ts { } return false; } + + function getAncestorBindingPattern(node: Node): BindingPattern { + while (node) { + if (isBindingPattern(node)) { + return node; + } + node = node.parent; + } + return undefined; + } } // Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and @@ -1065,7 +1088,7 @@ namespace ts { Debug.assert(declaration !== undefined, "Block-scoped variable declaration is undefined"); - if (!isInAmbientContext(declaration) && !isBlockScopedNameDeclaredBeforeUse(getAncestor(declaration, SyntaxKind.VariableDeclaration), errorLocation)) { + if (!isInAmbientContext(declaration) && !isBlockScopedNameDeclaredBeforeUse(declaration, errorLocation)) { error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(declaration.name)); } }