Error on forward references for property initializers

The error only appears when a property initializer references another
property before its definition. References to outer variables, etc are
still allowed.
This commit is contained in:
Nathan Shively-Sanders 2017-02-06 15:53:00 -08:00
parent 501084a93c
commit c28edc31c0

View File

@ -641,7 +641,7 @@ namespace ts {
}
// declaration is after usage
// can be legal if usage is deferred (i.e. inside function or in initializer of instance property)
if (isUsedInFunctionOrNonStaticProperty(usage)) {
if (isUsedInFunctionOrInstanceProperty(usage)) {
return true;
}
const sourceFiles = host.getSourceFiles();
@ -668,10 +668,12 @@ namespace ts {
}
// declaration is after usage
// can be legal if usage is deferred (i.e. inside function or in initializer of instance property)
// declaration is after usage, but it can still be legal if usage is deferred:
// 1. inside a function
// 2. inside an instance property initializer, a reference to a non-instance property
const container = getEnclosingBlockScopeContainer(declaration);
return isUsedInFunctionOrNonStaticProperty(usage, container);
const isInstanceProperty = declaration.kind === SyntaxKind.PropertyDeclaration && !(getModifierFlags(declaration) & ModifierFlags.Static);
return isUsedInFunctionOrInstanceProperty(usage, isInstanceProperty, container);
function isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration: VariableDeclaration, usage: Node): boolean {
const container = getEnclosingBlockScopeContainer(declaration);
@ -700,7 +702,7 @@ namespace ts {
return false;
}
function isUsedInFunctionOrNonStaticProperty(usage: Node, container?: Node): boolean {
function isUsedInFunctionOrInstanceProperty(usage: Node, isDeclarationInstanceProperty?: boolean, container?: Node): boolean {
let current = usage;
while (current) {
if (current === container) {
@ -711,13 +713,13 @@ namespace ts {
return true;
}
const initializerOfNonStaticProperty = current.parent &&
const initializerOfInstanceProperty = current.parent &&
current.parent.kind === SyntaxKind.PropertyDeclaration &&
(getModifierFlags(current.parent) & ModifierFlags.Static) === 0 &&
(<PropertyDeclaration>current.parent).initializer === current;
if (initializerOfNonStaticProperty) {
return true;
if (initializerOfInstanceProperty) {
return !isDeclarationInstanceProperty;
}
current = current.parent;
@ -986,10 +988,10 @@ namespace ts {
// interface bar {}
// }
// const foo/*1*/: foo/*2*/.bar;
// The foo at /*1*/ and /*2*/ will share same symbol with two meaning
// block - scope variable and namespace module. However, only when we
// The foo at /*1*/ and /*2*/ will share same symbol with two meanings:
// block-scoped variable and namespace module. However, only when we
// try to resolve name in /*1*/ which is used in variable position,
// we want to check for block- scoped
// we want to check for block-scoped
if (meaning & SymbolFlags.BlockScopedVariable) {
const exportOrLocalSymbol = getExportSymbolOfValueSymbolIfExported(result);
if (exportOrLocalSymbol.flags & SymbolFlags.BlockScopedVariable) {
@ -1013,7 +1015,7 @@ namespace ts {
return false;
}
const container = getThisContainer(errorLocation, /* includeArrowFunctions */ true);
const container = getThisContainer(errorLocation, /*includeArrowFunctions*/ true);
let location = container;
while (location) {
if (isClassLike(location.parent)) {
@ -12543,6 +12545,16 @@ namespace ts {
}
}
function isInPropertyInitializer(node: Node): boolean {
while (node) {
if (node.parent && node.parent.kind === SyntaxKind.PropertyDeclaration && (node.parent as PropertyDeclaration).initializer === node) {
return true;
}
node = node.parent;
}
return false;
}
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
const type = checkNonNullExpression(left);
if (isTypeAny(type) || type === silentNeverType) {
@ -12565,6 +12577,11 @@ namespace ts {
}
return unknownType;
}
if (prop.valueDeclaration &&
isInPropertyInitializer(node) &&
!isBlockScopedNameDeclaredBeforeUse(prop.valueDeclaration, right)) {
error(right, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, right.text);
}
markPropertyAsReferenced(prop);