From 833fd010d7537312de8dc6f0890f21f2a39f3e9d Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 4 Apr 2017 14:49:06 -0700 Subject: [PATCH 1/3] Static prop. inits can refer to later static methods Static methods are created before static property initializers run, so static property initializers may refer to static methods. This was not allowed previously. --- src/compiler/checker.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7bdde91f286..1e309cdf488 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -751,6 +751,7 @@ namespace ts { // 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 + // 3. inside a static property initializer, a reference to a static method in the same class const container = getEnclosingBlockScopeContainer(declaration); return isUsedInFunctionOrInstanceProperty(usage, declaration, container); @@ -792,14 +793,22 @@ namespace ts { return true; } - const initializerOfInstanceProperty = current.parent && + const initializerOfProperty = current.parent && current.parent.kind === SyntaxKind.PropertyDeclaration && - (getModifierFlags(current.parent) & ModifierFlags.Static) === 0 && (current.parent).initializer === current; - if (initializerOfInstanceProperty) { - const isDeclarationInstanceProperty = declaration.kind === SyntaxKind.PropertyDeclaration && !(getModifierFlags(declaration) & ModifierFlags.Static); - return !isDeclarationInstanceProperty || getContainingClass(usage) !== getContainingClass(declaration); + if (initializerOfProperty) { + if (getModifierFlags(current.parent) & ModifierFlags.Static) { + if (declaration.kind === SyntaxKind.MethodDeclaration) { + return true; + } + } + else { + const isDeclarationInstanceProperty = declaration.kind === SyntaxKind.PropertyDeclaration && !(getModifierFlags(declaration) & ModifierFlags.Static); + if(!isDeclarationInstanceProperty || getContainingClass(usage) !== getContainingClass(declaration)) { + return true; + } + } } current = current.parent; From 4214aed37de464490128294fa425797096a7f644 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 4 Apr 2017 14:50:42 -0700 Subject: [PATCH 2/3] Test:static inits can refer to later static methods --- .../scopeCheckStaticInitializer.errors.txt | 30 +++++++++++++++ .../reference/scopeCheckStaticInitializer.js | 37 +++++++++++++++++++ .../compiler/scopeCheckStaticInitializer.ts | 14 +++++++ 3 files changed, 81 insertions(+) create mode 100644 tests/baselines/reference/scopeCheckStaticInitializer.errors.txt create mode 100644 tests/baselines/reference/scopeCheckStaticInitializer.js create mode 100644 tests/cases/compiler/scopeCheckStaticInitializer.ts diff --git a/tests/baselines/reference/scopeCheckStaticInitializer.errors.txt b/tests/baselines/reference/scopeCheckStaticInitializer.errors.txt new file mode 100644 index 00000000000..c023c356a2a --- /dev/null +++ b/tests/baselines/reference/scopeCheckStaticInitializer.errors.txt @@ -0,0 +1,30 @@ +tests/cases/compiler/scopeCheckStaticInitializer.ts(2,38): error TS2448: Block-scoped variable 'data' used before its declaration. +tests/cases/compiler/scopeCheckStaticInitializer.ts(5,23): error TS2449: Class 'After' used before its declaration. +tests/cases/compiler/scopeCheckStaticInitializer.ts(5,29): error TS2448: Block-scoped variable 'data' used before its declaration. +tests/cases/compiler/scopeCheckStaticInitializer.ts(6,23): error TS2449: Class 'After' used before its declaration. + + +==== tests/cases/compiler/scopeCheckStaticInitializer.ts (4 errors) ==== + class X { + static illegalBeforeProperty = X.data; + ~~~~ +!!! error TS2448: Block-scoped variable 'data' used before its declaration. + static okBeforeMethod = X.method; + + static illegal2 = After.data; + ~~~~~ +!!! error TS2449: Class 'After' used before its declaration. + ~~~~ +!!! error TS2448: Block-scoped variable 'data' used before its declaration. + static illegal3 = After.method; + ~~~~~ +!!! error TS2449: Class 'After' used before its declaration. + static data = 13; + static method() { } + } + class After { + static data = 12; + static method() { }; + } + + \ No newline at end of file diff --git a/tests/baselines/reference/scopeCheckStaticInitializer.js b/tests/baselines/reference/scopeCheckStaticInitializer.js new file mode 100644 index 00000000000..59711f07380 --- /dev/null +++ b/tests/baselines/reference/scopeCheckStaticInitializer.js @@ -0,0 +1,37 @@ +//// [scopeCheckStaticInitializer.ts] +class X { + static illegalBeforeProperty = X.data; + static okBeforeMethod = X.method; + + static illegal2 = After.data; + static illegal3 = After.method; + static data = 13; + static method() { } +} +class After { + static data = 12; + static method() { }; +} + + + +//// [scopeCheckStaticInitializer.js] +var X = (function () { + function X() { + } + X.method = function () { }; + return X; +}()); +X.illegalBeforeProperty = X.data; +X.okBeforeMethod = X.method; +X.illegal2 = After.data; +X.illegal3 = After.method; +X.data = 13; +var After = (function () { + function After() { + } + After.method = function () { }; + ; + return After; +}()); +After.data = 12; diff --git a/tests/cases/compiler/scopeCheckStaticInitializer.ts b/tests/cases/compiler/scopeCheckStaticInitializer.ts new file mode 100644 index 00000000000..a5c7b6ccd62 --- /dev/null +++ b/tests/cases/compiler/scopeCheckStaticInitializer.ts @@ -0,0 +1,14 @@ +class X { + static illegalBeforeProperty = X.data; + static okBeforeMethod = X.method; + + static illegal2 = After.data; + static illegal3 = After.method; + static data = 13; + static method() { } +} +class After { + static data = 12; + static method() { }; +} + From a5d320a83e3f295d6ffbafa219de7e8919ae16dd Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 4 Apr 2017 15:15:25 -0700 Subject: [PATCH 3/3] Fix whitespace lint --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1e309cdf488..2fe44641efd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -805,7 +805,7 @@ namespace ts { } else { const isDeclarationInstanceProperty = declaration.kind === SyntaxKind.PropertyDeclaration && !(getModifierFlags(declaration) & ModifierFlags.Static); - if(!isDeclarationInstanceProperty || getContainingClass(usage) !== getContainingClass(declaration)) { + if (!isDeclarationInstanceProperty || getContainingClass(usage) !== getContainingClass(declaration)) { return true; } }