mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 16:38:05 -06:00
align ClassStaticBlockDeclaration with IIFE in CFA (#44969)
* align ClassStaticBlockDeclaration with IIFE in CFA * isIIFELike => isImmediatelyInvoked * fix unexpected used-before-assignment errors * update baseline
This commit is contained in:
parent
4ec16b2a6b
commit
20c01cdd3f
@ -657,11 +657,15 @@ namespace ts {
|
||||
const saveExceptionTarget = currentExceptionTarget;
|
||||
const saveActiveLabelList = activeLabelList;
|
||||
const saveHasExplicitReturn = hasExplicitReturn;
|
||||
const isIIFE = containerFlags & ContainerFlags.IsFunctionExpression && !hasSyntacticModifier(node, ModifierFlags.Async) &&
|
||||
!(node as FunctionLikeDeclaration).asteriskToken && !!getImmediatelyInvokedFunctionExpression(node);
|
||||
const isImmediatelyInvoked =
|
||||
(containerFlags & ContainerFlags.IsFunctionExpression &&
|
||||
!hasSyntacticModifier(node, ModifierFlags.Async) &&
|
||||
!(node as FunctionLikeDeclaration).asteriskToken &&
|
||||
!!getImmediatelyInvokedFunctionExpression(node)) ||
|
||||
node.kind === SyntaxKind.ClassStaticBlockDeclaration;
|
||||
// A non-async, non-generator IIFE is considered part of the containing control flow. Return statements behave
|
||||
// similarly to break statements that exit to a label just past the statement body.
|
||||
if (!isIIFE) {
|
||||
if (!isImmediatelyInvoked) {
|
||||
currentFlow = initFlowNode({ flags: FlowFlags.Start });
|
||||
if (containerFlags & (ContainerFlags.IsFunctionExpression | ContainerFlags.IsObjectLiteralOrClassExpressionMethodOrAccessor)) {
|
||||
currentFlow.node = node as FunctionExpression | ArrowFunction | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration;
|
||||
@ -669,7 +673,7 @@ namespace ts {
|
||||
}
|
||||
// We create a return control flow graph for IIFEs and constructors. For constructors
|
||||
// we use the return control flow graph in strict property initialization checks.
|
||||
currentReturnTarget = isIIFE || node.kind === SyntaxKind.Constructor || node.kind === SyntaxKind.ClassStaticBlockDeclaration || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression)) ? createBranchLabel() : undefined;
|
||||
currentReturnTarget = isImmediatelyInvoked || node.kind === SyntaxKind.Constructor || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression)) ? createBranchLabel() : undefined;
|
||||
currentExceptionTarget = undefined;
|
||||
currentBreakTarget = undefined;
|
||||
currentContinueTarget = undefined;
|
||||
@ -695,7 +699,7 @@ namespace ts {
|
||||
(node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).returnFlowNode = currentFlow;
|
||||
}
|
||||
}
|
||||
if (!isIIFE) {
|
||||
if (!isImmediatelyInvoked) {
|
||||
currentFlow = saveCurrentFlow;
|
||||
}
|
||||
currentBreakTarget = saveBreakTarget;
|
||||
|
||||
@ -28982,6 +28982,7 @@ namespace ts {
|
||||
&& !isOptionalPropertyDeclaration(valueDeclaration)
|
||||
&& !(isAccessExpression(node) && isAccessExpression(node.expression))
|
||||
&& !isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right)
|
||||
&& !(isMethodDeclaration(valueDeclaration) && getCombinedModifierFlags(valueDeclaration) & ModifierFlags.Static)
|
||||
&& (compilerOptions.useDefineForClassFields || !isPropertyDeclaredInAncestorClass(prop))) {
|
||||
diagnosticMessage = error(right, Diagnostics.Property_0_is_used_before_its_initialization, declarationName);
|
||||
}
|
||||
|
||||
23
tests/baselines/reference/classStaticBlock28.js
Normal file
23
tests/baselines/reference/classStaticBlock28.js
Normal file
@ -0,0 +1,23 @@
|
||||
//// [classStaticBlock28.ts]
|
||||
let foo: number;
|
||||
|
||||
class C {
|
||||
static {
|
||||
foo = 1
|
||||
}
|
||||
}
|
||||
|
||||
console.log(foo)
|
||||
|
||||
//// [classStaticBlock28.js]
|
||||
"use strict";
|
||||
var foo;
|
||||
var C = /** @class */ (function () {
|
||||
function C() {
|
||||
}
|
||||
return C;
|
||||
}());
|
||||
(function () {
|
||||
foo = 1;
|
||||
})();
|
||||
console.log(foo);
|
||||
19
tests/baselines/reference/classStaticBlock28.symbols
Normal file
19
tests/baselines/reference/classStaticBlock28.symbols
Normal file
@ -0,0 +1,19 @@
|
||||
=== tests/cases/conformance/classes/classStaticBlock/classStaticBlock28.ts ===
|
||||
let foo: number;
|
||||
>foo : Symbol(foo, Decl(classStaticBlock28.ts, 0, 3))
|
||||
|
||||
class C {
|
||||
>C : Symbol(C, Decl(classStaticBlock28.ts, 0, 16))
|
||||
|
||||
static {
|
||||
foo = 1
|
||||
>foo : Symbol(foo, Decl(classStaticBlock28.ts, 0, 3))
|
||||
}
|
||||
}
|
||||
|
||||
console.log(foo)
|
||||
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
|
||||
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
>foo : Symbol(foo, Decl(classStaticBlock28.ts, 0, 3))
|
||||
|
||||
22
tests/baselines/reference/classStaticBlock28.types
Normal file
22
tests/baselines/reference/classStaticBlock28.types
Normal file
@ -0,0 +1,22 @@
|
||||
=== tests/cases/conformance/classes/classStaticBlock/classStaticBlock28.ts ===
|
||||
let foo: number;
|
||||
>foo : number
|
||||
|
||||
class C {
|
||||
>C : C
|
||||
|
||||
static {
|
||||
foo = 1
|
||||
>foo = 1 : 1
|
||||
>foo : number
|
||||
>1 : 1
|
||||
}
|
||||
}
|
||||
|
||||
console.log(foo)
|
||||
>console.log(foo) : void
|
||||
>console.log : (...data: any[]) => void
|
||||
>console : Console
|
||||
>log : (...data: any[]) => void
|
||||
>foo : number
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
tests/cases/conformance/classes/classStaticBlock/classStaticBlockUseBeforeDef3.ts(14,21): error TS2448: Block-scoped variable 'FOO' used before its declaration.
|
||||
tests/cases/conformance/classes/classStaticBlock/classStaticBlockUseBeforeDef3.ts(14,21): error TS2454: Variable 'FOO' is used before being assigned.
|
||||
|
||||
|
||||
==== tests/cases/conformance/classes/classStaticBlock/classStaticBlockUseBeforeDef3.ts (2 errors) ====
|
||||
class A {
|
||||
static {
|
||||
A.doSomething(); // should not error
|
||||
}
|
||||
|
||||
static doSomething() {
|
||||
console.log("gotcha!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Baz {
|
||||
static {
|
||||
console.log(FOO); // should error
|
||||
~~~
|
||||
!!! error TS2448: Block-scoped variable 'FOO' used before its declaration.
|
||||
!!! related TS2728 tests/cases/conformance/classes/classStaticBlock/classStaticBlockUseBeforeDef3.ts:18:7: 'FOO' is declared here.
|
||||
~~~
|
||||
!!! error TS2454: Variable 'FOO' is used before being assigned.
|
||||
}
|
||||
}
|
||||
|
||||
const FOO = "FOO";
|
||||
class Bar {
|
||||
static {
|
||||
console.log(FOO); // should not error
|
||||
}
|
||||
}
|
||||
|
||||
let u = "FOO" as "FOO" | "BAR";
|
||||
|
||||
class CFA {
|
||||
static {
|
||||
u = "BAR";
|
||||
u; // should be "BAR"
|
||||
}
|
||||
|
||||
static t = 1;
|
||||
|
||||
static doSomething() {}
|
||||
|
||||
static {
|
||||
u; // should be "BAR"
|
||||
}
|
||||
}
|
||||
|
||||
u; // should be "BAR"
|
||||
|
||||
@ -0,0 +1,78 @@
|
||||
=== tests/cases/conformance/classes/classStaticBlock/classStaticBlockUseBeforeDef3.ts ===
|
||||
class A {
|
||||
>A : Symbol(A, Decl(classStaticBlockUseBeforeDef3.ts, 0, 0))
|
||||
|
||||
static {
|
||||
A.doSomething(); // should not error
|
||||
>A.doSomething : Symbol(A.doSomething, Decl(classStaticBlockUseBeforeDef3.ts, 3, 5))
|
||||
>A : Symbol(A, Decl(classStaticBlockUseBeforeDef3.ts, 0, 0))
|
||||
>doSomething : Symbol(A.doSomething, Decl(classStaticBlockUseBeforeDef3.ts, 3, 5))
|
||||
}
|
||||
|
||||
static doSomething() {
|
||||
>doSomething : Symbol(A.doSomething, Decl(classStaticBlockUseBeforeDef3.ts, 3, 5))
|
||||
|
||||
console.log("gotcha!");
|
||||
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
|
||||
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Baz {
|
||||
>Baz : Symbol(Baz, Decl(classStaticBlockUseBeforeDef3.ts, 8, 1))
|
||||
|
||||
static {
|
||||
console.log(FOO); // should error
|
||||
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
|
||||
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
>FOO : Symbol(FOO, Decl(classStaticBlockUseBeforeDef3.ts, 17, 5))
|
||||
}
|
||||
}
|
||||
|
||||
const FOO = "FOO";
|
||||
>FOO : Symbol(FOO, Decl(classStaticBlockUseBeforeDef3.ts, 17, 5))
|
||||
|
||||
class Bar {
|
||||
>Bar : Symbol(Bar, Decl(classStaticBlockUseBeforeDef3.ts, 17, 18))
|
||||
|
||||
static {
|
||||
console.log(FOO); // should not error
|
||||
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
|
||||
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
>FOO : Symbol(FOO, Decl(classStaticBlockUseBeforeDef3.ts, 17, 5))
|
||||
}
|
||||
}
|
||||
|
||||
let u = "FOO" as "FOO" | "BAR";
|
||||
>u : Symbol(u, Decl(classStaticBlockUseBeforeDef3.ts, 24, 3))
|
||||
|
||||
class CFA {
|
||||
>CFA : Symbol(CFA, Decl(classStaticBlockUseBeforeDef3.ts, 24, 31))
|
||||
|
||||
static {
|
||||
u = "BAR";
|
||||
>u : Symbol(u, Decl(classStaticBlockUseBeforeDef3.ts, 24, 3))
|
||||
|
||||
u; // should be "BAR"
|
||||
>u : Symbol(u, Decl(classStaticBlockUseBeforeDef3.ts, 24, 3))
|
||||
}
|
||||
|
||||
static t = 1;
|
||||
>t : Symbol(CFA.t, Decl(classStaticBlockUseBeforeDef3.ts, 30, 5))
|
||||
|
||||
static doSomething() {}
|
||||
>doSomething : Symbol(CFA.doSomething, Decl(classStaticBlockUseBeforeDef3.ts, 32, 17))
|
||||
|
||||
static {
|
||||
u; // should be "BAR"
|
||||
>u : Symbol(u, Decl(classStaticBlockUseBeforeDef3.ts, 24, 3))
|
||||
}
|
||||
}
|
||||
|
||||
u; // should be "BAR"
|
||||
>u : Symbol(u, Decl(classStaticBlockUseBeforeDef3.ts, 24, 3))
|
||||
|
||||
@ -0,0 +1,89 @@
|
||||
=== tests/cases/conformance/classes/classStaticBlock/classStaticBlockUseBeforeDef3.ts ===
|
||||
class A {
|
||||
>A : A
|
||||
|
||||
static {
|
||||
A.doSomething(); // should not error
|
||||
>A.doSomething() : void
|
||||
>A.doSomething : () => void
|
||||
>A : typeof A
|
||||
>doSomething : () => void
|
||||
}
|
||||
|
||||
static doSomething() {
|
||||
>doSomething : () => void
|
||||
|
||||
console.log("gotcha!");
|
||||
>console.log("gotcha!") : void
|
||||
>console.log : (...data: any[]) => void
|
||||
>console : Console
|
||||
>log : (...data: any[]) => void
|
||||
>"gotcha!" : "gotcha!"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Baz {
|
||||
>Baz : Baz
|
||||
|
||||
static {
|
||||
console.log(FOO); // should error
|
||||
>console.log(FOO) : void
|
||||
>console.log : (...data: any[]) => void
|
||||
>console : Console
|
||||
>log : (...data: any[]) => void
|
||||
>FOO : "FOO"
|
||||
}
|
||||
}
|
||||
|
||||
const FOO = "FOO";
|
||||
>FOO : "FOO"
|
||||
>"FOO" : "FOO"
|
||||
|
||||
class Bar {
|
||||
>Bar : Bar
|
||||
|
||||
static {
|
||||
console.log(FOO); // should not error
|
||||
>console.log(FOO) : void
|
||||
>console.log : (...data: any[]) => void
|
||||
>console : Console
|
||||
>log : (...data: any[]) => void
|
||||
>FOO : "FOO"
|
||||
}
|
||||
}
|
||||
|
||||
let u = "FOO" as "FOO" | "BAR";
|
||||
>u : "FOO" | "BAR"
|
||||
>"FOO" as "FOO" | "BAR" : "FOO" | "BAR"
|
||||
>"FOO" : "FOO"
|
||||
|
||||
class CFA {
|
||||
>CFA : CFA
|
||||
|
||||
static {
|
||||
u = "BAR";
|
||||
>u = "BAR" : "BAR"
|
||||
>u : "FOO" | "BAR"
|
||||
>"BAR" : "BAR"
|
||||
|
||||
u; // should be "BAR"
|
||||
>u : "BAR"
|
||||
}
|
||||
|
||||
static t = 1;
|
||||
>t : number
|
||||
>1 : 1
|
||||
|
||||
static doSomething() {}
|
||||
>doSomething : () => void
|
||||
|
||||
static {
|
||||
u; // should be "BAR"
|
||||
>u : "BAR"
|
||||
}
|
||||
}
|
||||
|
||||
u; // should be "BAR"
|
||||
>u : "BAR"
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
// @strict: true
|
||||
|
||||
let foo: number;
|
||||
|
||||
class C {
|
||||
static {
|
||||
foo = 1
|
||||
}
|
||||
}
|
||||
|
||||
console.log(foo)
|
||||
@ -0,0 +1,45 @@
|
||||
// @noEmit: true
|
||||
// @strict: true
|
||||
|
||||
class A {
|
||||
static {
|
||||
A.doSomething(); // should not error
|
||||
}
|
||||
|
||||
static doSomething() {
|
||||
console.log("gotcha!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Baz {
|
||||
static {
|
||||
console.log(FOO); // should error
|
||||
}
|
||||
}
|
||||
|
||||
const FOO = "FOO";
|
||||
class Bar {
|
||||
static {
|
||||
console.log(FOO); // should not error
|
||||
}
|
||||
}
|
||||
|
||||
let u = "FOO" as "FOO" | "BAR";
|
||||
|
||||
class CFA {
|
||||
static {
|
||||
u = "BAR";
|
||||
u; // should be "BAR"
|
||||
}
|
||||
|
||||
static t = 1;
|
||||
|
||||
static doSomething() {}
|
||||
|
||||
static {
|
||||
u; // should be "BAR"
|
||||
}
|
||||
}
|
||||
|
||||
u; // should be "BAR"
|
||||
Loading…
x
Reference in New Issue
Block a user