Skip to content

Commit 906cbd2

Browse files
Kingwlrbuckton
andauthored
Proposal class static block support (microsoft#43370)
* Class static block (microsoft#9) * Add types factory and parser * Add some case * Make class static block as a container * Update cases * Add visitor * Add emitter and more compile target * Check boundary of break and continue * Add basic transformer * Fix emit behavior * Add more tests * Add friend tests * Update baseline * Fix cr issues * Accept baseline * Add decorator and modifier check * Add functional boundary check * Fix conflict * Fix computed prop name within context * Add more tests * Update baseline * Avoid invalid test baseline * Support use before initialize check * wip * Fix class static block context * Fix checks * Fix missing case * Improve assert message * Accept baseline * Avoid new context * Update diagnostic message * Fix name collision * Fix targets * Avoid unnecessary files * Add more case * Add more test cases * Fix strict mode function declaration * Avoid private fields initializer if no private identifier references * Avoid private fields and add more test case * Add more case * Add tests and support for related services functionality * Fix this reference in static block * Split parser diagnostic and binder diagnostic Co-authored-by: Ron Buckton <ron.buckton@microsoft.com>
1 parent 1694c77 commit 906cbd2

File tree

217 files changed

+8636
-469
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

217 files changed

+8636
-469
lines changed

src/compiler/binder.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -1832,6 +1832,7 @@ namespace ts {
18321832
case SyntaxKind.ConstructSignature:
18331833
case SyntaxKind.IndexSignature:
18341834
case SyntaxKind.ConstructorType:
1835+
case SyntaxKind.ClassStaticBlockDeclaration:
18351836
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike;
18361837

18371838
case SyntaxKind.FunctionExpression:
@@ -1867,7 +1868,7 @@ namespace ts {
18671868
// By not creating a new block-scoped-container here, we ensure that both 'var x'
18681869
// and 'let x' go into the Function-container's locals, and we do get a collision
18691870
// conflict.
1870-
return isFunctionLike(node.parent) ? ContainerFlags.None : ContainerFlags.IsBlockScopedContainer;
1871+
return isFunctionLike(node.parent) || isClassStaticBlockDeclaration(node.parent) ? ContainerFlags.None : ContainerFlags.IsBlockScopedContainer;
18711872
}
18721873

18731874
return ContainerFlags.None;
@@ -1929,6 +1930,7 @@ namespace ts {
19291930
case SyntaxKind.JSDocFunctionType:
19301931
case SyntaxKind.JSDocTypedefTag:
19311932
case SyntaxKind.JSDocCallbackTag:
1933+
case SyntaxKind.ClassStaticBlockDeclaration:
19321934
case SyntaxKind.TypeAliasDeclaration:
19331935
case SyntaxKind.MappedType:
19341936
// All the children of these container types are never visible through another
@@ -2328,7 +2330,7 @@ namespace ts {
23282330
// Report error if function is not top level function declaration
23292331
if (blockScopeContainer.kind !== SyntaxKind.SourceFile &&
23302332
blockScopeContainer.kind !== SyntaxKind.ModuleDeclaration &&
2331-
!isFunctionLike(blockScopeContainer)) {
2333+
!isFunctionLikeOrClassStaticBlockDeclaration(blockScopeContainer)) {
23322334
// We check first if the name is inside class declaration or class expression; if so give explicit message
23332335
// otherwise report generic error message.
23342336
const errorSpan = getErrorSpanForNode(file, node);
@@ -2712,7 +2714,7 @@ namespace ts {
27122714
updateStrictModeStatementList((node as SourceFile).statements);
27132715
return bindSourceFileIfExternalModule();
27142716
case SyntaxKind.Block:
2715-
if (!isFunctionLike(node.parent)) {
2717+
if (!isFunctionLikeOrClassStaticBlockDeclaration(node.parent)) {
27162718
return;
27172719
}
27182720
// falls through

src/compiler/checker.ts

+53-22
Original file line numberDiff line numberDiff line change
@@ -24186,7 +24186,7 @@ namespace ts {
2418624186
// To avoid that we will give an error to users if they use arguments objects in arrow function so that they
2418724187
// can explicitly bound arguments objects
2418824188
if (symbol === argumentsSymbol) {
24189-
if (isInPropertyInitializer(node)) {
24189+
if (isInPropertyInitializerOrClassStaticBlock(node)) {
2419024190
error(node, Diagnostics.arguments_cannot_be_referenced_in_property_initializers);
2419124191
return errorType;
2419224192
}
@@ -24544,6 +24544,9 @@ namespace ts {
2454424544
// do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks
2454524545
}
2454624546
break;
24547+
case SyntaxKind.ClassStaticBlockDeclaration:
24548+
error(node, Diagnostics.this_cannot_be_referenced_in_current_location);
24549+
break;
2454724550
case SyntaxKind.ComputedPropertyName:
2454824551
error(node, Diagnostics.this_cannot_be_referenced_in_a_computed_property_name);
2454924552
break;
@@ -24602,7 +24605,8 @@ namespace ts {
2460224605

2460324606
if (isClassLike(container.parent)) {
2460424607
const symbol = getSymbolOfNode(container.parent);
24605-
const type = hasSyntacticModifier(container, ModifierFlags.Static) ? getTypeOfSymbol(symbol) : (getDeclaredTypeOfSymbol(symbol) as InterfaceType).thisType!;
24608+
const isStatic = hasSyntacticModifier(container, ModifierFlags.Static) || isClassStaticBlockDeclaration(container);
24609+
const type = isStatic ? getTypeOfSymbol(symbol) : (getDeclaredTypeOfSymbol(symbol) as InterfaceType).thisType!;
2460624610
return getFlowTypeOfReference(node, type);
2460724611
}
2460824612

@@ -27565,7 +27569,7 @@ namespace ts {
2756527569

2756627570
let diagnosticMessage;
2756727571
const declarationName = idText(right);
27568-
if (isInPropertyInitializer(node)
27572+
if (isInPropertyInitializerOrClassStaticBlock(node)
2756927573
&& !isOptionalPropertyDeclaration(valueDeclaration)
2757027574
&& !(isAccessExpression(node) && isAccessExpression(node.expression))
2757127575
&& !isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right)
@@ -27586,7 +27590,7 @@ namespace ts {
2758627590
}
2758727591
}
2758827592

27589-
function isInPropertyInitializer(node: Node): boolean {
27593+
function isInPropertyInitializerOrClassStaticBlock(node: Node): boolean {
2759027594
return !!findAncestor(node, node => {
2759127595
switch (node.kind) {
2759227596
case SyntaxKind.PropertyDeclaration:
@@ -27606,6 +27610,8 @@ namespace ts {
2760627610
case SyntaxKind.ExpressionWithTypeArguments:
2760727611
case SyntaxKind.HeritageClause:
2760827612
return false;
27613+
case SyntaxKind.ExpressionStatement:
27614+
return isBlock(node.parent) && isClassStaticBlockDeclaration(node.parent.parent) ? true : "quit";
2760927615
default:
2761027616
return isExpressionNode(node) ? false : "quit";
2761127617
}
@@ -31303,7 +31309,11 @@ namespace ts {
3130331309
function checkAwaitExpression(node: AwaitExpression): Type {
3130431310
// Grammar checking
3130531311
if (produceDiagnostics) {
31306-
if (!(node.flags & NodeFlags.AwaitContext)) {
31312+
const container = getContainingFunctionOrClassStaticBlock(node);
31313+
if (container && isClassStaticBlockDeclaration(container)) {
31314+
error(node, Diagnostics.Await_expression_cannot_be_used_inside_a_class_static_block);
31315+
}
31316+
else if (!(node.flags & NodeFlags.AwaitContext)) {
3130731317
if (isInTopLevelContext(node)) {
3130831318
const sourceFile = getSourceFileOfNode(node);
3130931319
if (!hasParseDiagnostics(sourceFile)) {
@@ -31328,9 +31338,8 @@ namespace ts {
3132831338
if (!hasParseDiagnostics(sourceFile)) {
3132931339
const span = getSpanOfTokenAtPosition(sourceFile, node.pos);
3133031340
const diagnostic = createFileDiagnostic(sourceFile, span.start, span.length, Diagnostics.await_expressions_are_only_allowed_within_async_functions_and_at_the_top_levels_of_modules);
31331-
const func = getContainingFunction(node);
31332-
if (func && func.kind !== SyntaxKind.Constructor && (getFunctionFlags(func) & FunctionFlags.Async) === 0) {
31333-
const relatedInfo = createDiagnosticForNode(func, Diagnostics.Did_you_mean_to_mark_this_function_as_async);
31341+
if (container && container.kind !== SyntaxKind.Constructor && (getFunctionFlags(container) & FunctionFlags.Async) === 0) {
31342+
const relatedInfo = createDiagnosticForNode(container, Diagnostics.Did_you_mean_to_mark_this_function_as_async);
3133431343
addRelatedInfo(diagnostic, relatedInfo);
3133531344
}
3133631345
diagnostics.add(diagnostic);
@@ -33489,6 +33498,12 @@ namespace ts {
3348933498
}
3349033499
}
3349133500

33501+
function checkClassStaticBlockDeclaration(node: ClassStaticBlockDeclaration) {
33502+
checkGrammarDecoratorsAndModifiers(node);
33503+
33504+
forEachChild(node, checkSourceElement);
33505+
}
33506+
3349233507
function checkConstructorDeclaration(node: ConstructorDeclaration) {
3349333508
// Grammar check on signature of constructor and modifier of the constructor is done in checkSignatureDeclaration function.
3349433509
checkSignatureDeclaration(node);
@@ -35096,10 +35111,11 @@ namespace ts {
3509635111
break;
3509735112
case SyntaxKind.IndexSignature:
3509835113
case SyntaxKind.SemicolonClassElement:
35114+
case SyntaxKind.ClassStaticBlockDeclaration:
3509935115
// Can't be private
3510035116
break;
3510135117
default:
35102-
Debug.fail();
35118+
Debug.fail("Unexpected class member");
3510335119
}
3510435120
}
3510535121
}
@@ -35531,6 +35547,7 @@ namespace ts {
3553135547
if (!node.name) {
3553235548
return;
3553335549
}
35550+
3553435551
// For a computed property, just check the initializer and exit
3553535552
// Do not use hasDynamicName here, because that returns false for well known symbols.
3553635553
// We want to perform checkComputedPropertyName for all computed properties, including
@@ -35907,11 +35924,17 @@ namespace ts {
3590735924
function checkForOfStatement(node: ForOfStatement): void {
3590835925
checkGrammarForInOrForOfStatement(node);
3590935926

35927+
const container = getContainingFunctionOrClassStaticBlock(node);
3591035928
if (node.awaitModifier) {
35911-
const functionFlags = getFunctionFlags(getContainingFunction(node));
35912-
if ((functionFlags & (FunctionFlags.Invalid | FunctionFlags.Async)) === FunctionFlags.Async && languageVersion < ScriptTarget.ESNext) {
35913-
// for..await..of in an async function or async generator function prior to ESNext requires the __asyncValues helper
35914-
checkExternalEmitHelpers(node, ExternalEmitHelpers.ForAwaitOfIncludes);
35929+
if (container && isClassStaticBlockDeclaration(container)) {
35930+
grammarErrorOnNode(node.awaitModifier, Diagnostics.For_await_loops_cannot_be_used_inside_a_class_static_block);
35931+
}
35932+
else {
35933+
const functionFlags = getFunctionFlags(container);
35934+
if ((functionFlags & (FunctionFlags.Invalid | FunctionFlags.Async)) === FunctionFlags.Async && languageVersion < ScriptTarget.ESNext) {
35935+
// for..await..of in an async function or async generator function prior to ESNext requires the __asyncValues helper
35936+
checkExternalEmitHelpers(node, ExternalEmitHelpers.ForAwaitOfIncludes);
35937+
}
3591535938
}
3591635939
}
3591735940
else if (compilerOptions.downlevelIteration && languageVersion < ScriptTarget.ES2015) {
@@ -36791,28 +36814,33 @@ namespace ts {
3679136814
return;
3679236815
}
3679336816

36794-
const func = getContainingFunction(node);
36795-
if (!func) {
36817+
const container = getContainingFunctionOrClassStaticBlock(node);
36818+
if(container && isClassStaticBlockDeclaration(container)) {
36819+
grammarErrorOnFirstToken(node, Diagnostics.A_return_statement_cannot_be_used_inside_a_class_static_block);
36820+
return;
36821+
}
36822+
36823+
if (!container) {
3679636824
grammarErrorOnFirstToken(node, Diagnostics.A_return_statement_can_only_be_used_within_a_function_body);
3679736825
return;
3679836826
}
3679936827

36800-
const signature = getSignatureFromDeclaration(func);
36828+
const signature = getSignatureFromDeclaration(container);
3680136829
const returnType = getReturnTypeOfSignature(signature);
36802-
const functionFlags = getFunctionFlags(func);
36830+
const functionFlags = getFunctionFlags(container);
3680336831
if (strictNullChecks || node.expression || returnType.flags & TypeFlags.Never) {
3680436832
const exprType = node.expression ? checkExpressionCached(node.expression) : undefinedType;
36805-
if (func.kind === SyntaxKind.SetAccessor) {
36833+
if (container.kind === SyntaxKind.SetAccessor) {
3680636834
if (node.expression) {
3680736835
error(node, Diagnostics.Setters_cannot_return_a_value);
3680836836
}
3680936837
}
36810-
else if (func.kind === SyntaxKind.Constructor) {
36838+
else if (container.kind === SyntaxKind.Constructor) {
3681136839
if (node.expression && !checkTypeAssignableToAndOptionallyElaborate(exprType, returnType, node, node.expression)) {
3681236840
error(node, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class);
3681336841
}
3681436842
}
36815-
else if (getReturnTypeFromAnnotation(func)) {
36843+
else if (getReturnTypeFromAnnotation(container)) {
3681636844
const unwrappedReturnType = unwrapReturnType(returnType, functionFlags) ?? returnType;
3681736845
const unwrappedExprType = functionFlags & FunctionFlags.Async
3681836846
? checkAwaitedType(exprType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)
@@ -36825,7 +36853,7 @@ namespace ts {
3682536853
}
3682636854
}
3682736855
}
36828-
else if (func.kind !== SyntaxKind.Constructor && compilerOptions.noImplicitReturns && !isUnwrappedReturnTypeVoidOrAny(func, returnType)) {
36856+
else if (container.kind !== SyntaxKind.Constructor && compilerOptions.noImplicitReturns && !isUnwrappedReturnTypeVoidOrAny(container, returnType)) {
3682936857
// The function has a return type, but the return statement doesn't have an expression.
3683036858
error(node, Diagnostics.Not_all_code_paths_return_a_value);
3683136859
}
@@ -38655,6 +38683,8 @@ namespace ts {
3865538683
case SyntaxKind.MethodDeclaration:
3865638684
case SyntaxKind.MethodSignature:
3865738685
return checkMethodDeclaration(node as MethodDeclaration | MethodSignature);
38686+
case SyntaxKind.ClassStaticBlockDeclaration:
38687+
return checkClassStaticBlockDeclaration(node as ClassStaticBlockDeclaration);
3865838688
case SyntaxKind.Constructor:
3865938689
return checkConstructorDeclaration(node as ConstructorDeclaration);
3866038690
case SyntaxKind.GetAccessor:
@@ -41132,6 +41162,7 @@ namespace ts {
4113241162
case SyntaxKind.InterfaceDeclaration:
4113341163
case SyntaxKind.VariableStatement:
4113441164
case SyntaxKind.TypeAliasDeclaration:
41165+
case SyntaxKind.ClassStaticBlockDeclaration:
4113541166
return true;
4113641167
case SyntaxKind.EnumDeclaration:
4113741168
return nodeHasAnyModifiersExcept(node, SyntaxKind.ConstKeyword);
@@ -41869,7 +41900,7 @@ namespace ts {
4186941900
function checkGrammarBreakOrContinueStatement(node: BreakOrContinueStatement): boolean {
4187041901
let current: Node = node;
4187141902
while (current) {
41872-
if (isFunctionLike(current)) {
41903+
if (isFunctionLikeOrClassStaticBlockDeclaration(current)) {
4187341904
return grammarErrorOnNode(node, Diagnostics.Jump_target_cannot_cross_function_boundary);
4187441905
}
4187541906

src/compiler/diagnosticMessages.json

+16
Original file line numberDiff line numberDiff line change
@@ -7154,5 +7154,21 @@
71547154
"Class decorators can't be used with static private identifier. Consider removing the experimental decorator.": {
71557155
"category": "Error",
71567156
"code": 18036
7157+
},
7158+
"Await expression cannot be used inside a class static block.": {
7159+
"category": "Error",
7160+
"code": 18037
7161+
},
7162+
"'For await' loops cannot be used inside a class static block.": {
7163+
"category": "Error",
7164+
"code": 18038
7165+
},
7166+
"Invalid use of '{0}'. It cannot be used inside a class static block.": {
7167+
"category": "Error",
7168+
"code": 18039
7169+
},
7170+
"A 'return' statement cannot be used inside a class static block.": {
7171+
"category": "Error",
7172+
"code": 18041
71577173
}
71587174
}

src/compiler/emitter.ts

+9
Original file line numberDiff line numberDiff line change
@@ -1351,6 +1351,8 @@ namespace ts {
13511351
return emitMethodSignature(node as MethodSignature);
13521352
case SyntaxKind.MethodDeclaration:
13531353
return emitMethodDeclaration(node as MethodDeclaration);
1354+
case SyntaxKind.ClassStaticBlockDeclaration:
1355+
return emitClassStaticBlockDeclaration(node as ClassStaticBlockDeclaration);
13541356
case SyntaxKind.Constructor:
13551357
return emitConstructor(node as ConstructorDeclaration);
13561358
case SyntaxKind.GetAccessor:
@@ -2063,6 +2065,13 @@ namespace ts {
20632065
emitSignatureAndBody(node, emitSignatureHead);
20642066
}
20652067

2068+
function emitClassStaticBlockDeclaration(node: ClassStaticBlockDeclaration) {
2069+
emitDecorators(node, node.decorators);
2070+
emitModifiers(node, node.modifiers);
2071+
writeKeyword("static");
2072+
emitBlockFunctionBody(node.body);
2073+
}
2074+
20662075
function emitConstructor(node: ConstructorDeclaration) {
20672076
emitModifiers(node, node.modifiers);
20682077
writeKeyword("constructor");

src/compiler/factory/nodeFactory.ts

+34
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ namespace ts {
9494
updateConstructSignature,
9595
createIndexSignature,
9696
updateIndexSignature,
97+
createClassStaticBlockDeclaration,
98+
updateClassStaticBlockDeclaration,
9799
createTemplateLiteralTypeSpan,
98100
updateTemplateLiteralTypeSpan,
99101
createKeywordTypeNode,
@@ -1415,6 +1417,38 @@ namespace ts {
14151417
: node;
14161418
}
14171419

1420+
// @api
1421+
function createClassStaticBlockDeclaration(
1422+
decorators: readonly Decorator[] | undefined,
1423+
modifiers: readonly Modifier[] | undefined,
1424+
body: Block
1425+
): ClassStaticBlockDeclaration {
1426+
const node = createBaseGenericNamedDeclaration<ClassStaticBlockDeclaration>(
1427+
SyntaxKind.ClassStaticBlockDeclaration,
1428+
decorators,
1429+
modifiers,
1430+
/*name*/ undefined,
1431+
/*typeParameters*/ undefined
1432+
);
1433+
node.body = body;
1434+
node.transformFlags = propagateChildFlags(body) | TransformFlags.ContainsClassFields;
1435+
return node;
1436+
}
1437+
1438+
// @api
1439+
function updateClassStaticBlockDeclaration(
1440+
node: ClassStaticBlockDeclaration,
1441+
decorators: readonly Decorator[] | undefined,
1442+
modifiers: readonly Modifier[] | undefined,
1443+
body: Block
1444+
): ClassStaticBlockDeclaration {
1445+
return node.decorators !== decorators
1446+
|| node.modifier !== modifiers
1447+
|| node.body !== body
1448+
? update(createClassStaticBlockDeclaration(decorators, modifiers, body), node)
1449+
: node;
1450+
}
1451+
14181452
// @api
14191453
function createConstructorDeclaration(
14201454
decorators: readonly Decorator[] | undefined,

src/compiler/factory/nodeTests.ts

+4
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,10 @@ namespace ts {
182182
return node.kind === SyntaxKind.MethodDeclaration;
183183
}
184184

185+
export function isClassStaticBlockDeclaration(node: Node): node is ClassStaticBlockDeclaration {
186+
return node.kind === SyntaxKind.ClassStaticBlockDeclaration;
187+
}
188+
185189
export function isConstructorDeclaration(node: Node): node is ConstructorDeclaration {
186190
return node.kind === SyntaxKind.Constructor;
187191
}

0 commit comments

Comments
 (0)