Skip to content

Proposal class static block support #43370

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 45 commits into from
Jun 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
dde4ca2
Class static block (#9)
Kingwl Mar 19, 2021
188cf27
Add basic transformer
Kingwl Mar 19, 2021
e26d82a
Fix emit behavior
Kingwl Mar 19, 2021
7c50597
Add more tests
Kingwl Mar 19, 2021
6f4b7b3
Add friend tests
Kingwl Mar 19, 2021
3c0b7cf
Update baseline
Kingwl Mar 19, 2021
bafd15c
Fix cr issues
Kingwl Mar 26, 2021
c3a8b27
Accept baseline
Kingwl Mar 26, 2021
4c19c80
Add decorator and modifier check
Kingwl Mar 26, 2021
b4e64c4
Merge branch 'master' into proposal_class_static_block_support
Kingwl Apr 16, 2021
3a61e56
Add functional boundary check
Kingwl Apr 16, 2021
b826bcb
Merge branch 'master' into proposal_class_static_block_support
Kingwl Apr 21, 2021
f31a962
Merge branch 'master' into proposal_class_static_block_support
Kingwl Apr 23, 2021
1bed545
Fix conflict
Kingwl Apr 23, 2021
54bd285
Fix computed prop name within context
Kingwl Apr 23, 2021
efa72d9
Add more tests
Kingwl Apr 23, 2021
561b9d6
Update baseline
Kingwl Apr 23, 2021
8e3543f
Avoid invalid test baseline
Kingwl Apr 27, 2021
c4f71e2
Support use before initialize check
Kingwl Apr 27, 2021
1b2b7c7
Merge branch 'master' into proposal_class_static_block_support
Kingwl May 17, 2021
fb285a1
Merge branch 'master' into proposal_class_static_block_support
Kingwl May 25, 2021
e8cd4f8
wip
Kingwl Jun 6, 2021
16a3bcf
Fix class static block context
Kingwl Jun 11, 2021
3c71d54
Merge branch 'main' into proposal_class_static_block_support
Kingwl Jun 11, 2021
192fd33
Fix checks
Kingwl Jun 11, 2021
bcb3ddb
Fix missing case
Kingwl Jun 16, 2021
d1b5908
Merge branch 'proposal_class_static_block_support' of github.com:King…
Kingwl Jun 16, 2021
6d0a1e5
Improve assert message
Kingwl Jun 16, 2021
141d67a
Merge branch 'main' into proposal_class_static_block_support
Kingwl Jun 22, 2021
61e67fd
Accept baseline
Kingwl Jun 22, 2021
e608531
Avoid new context
Kingwl Jun 22, 2021
7fb981d
Update diagnostic message
Kingwl Jun 22, 2021
dabdce6
Fix name collision
Kingwl Jun 22, 2021
4f54d71
Fix targets
Kingwl Jun 22, 2021
1b33a04
Avoid unnecessary files
Kingwl Jun 22, 2021
bf77656
Add more case
Kingwl Jun 22, 2021
045a9e8
Add more test cases
Kingwl Jun 22, 2021
4278c0c
Fix strict mode function declaration
Kingwl Jun 22, 2021
34dce46
Avoid private fields initializer if no private identifier references
Kingwl Jun 22, 2021
307c1d9
Avoid private fields and add more test case
Kingwl Jun 23, 2021
f3f8ac2
Merge branch 'main' into proposal_class_static_block_support
Kingwl Jun 23, 2021
1f173a9
Add more case
Kingwl Jun 23, 2021
b409d8c
Add tests and support for related services functionality
rbuckton Jun 24, 2021
aa28986
Fix this reference in static block
Kingwl Jun 24, 2021
6234640
Split parser diagnostic and binder diagnostic
Kingwl Jun 24, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1832,6 +1832,7 @@ namespace ts {
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.ConstructorType:
case SyntaxKind.ClassStaticBlockDeclaration:
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike;

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

return ContainerFlags.None;
Expand Down Expand Up @@ -1929,6 +1930,7 @@ namespace ts {
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.JSDocTypedefTag:
case SyntaxKind.JSDocCallbackTag:
case SyntaxKind.ClassStaticBlockDeclaration:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.MappedType:
// All the children of these container types are never visible through another
Expand Down Expand Up @@ -2328,7 +2330,7 @@ namespace ts {
// Report error if function is not top level function declaration
if (blockScopeContainer.kind !== SyntaxKind.SourceFile &&
blockScopeContainer.kind !== SyntaxKind.ModuleDeclaration &&
!isFunctionLike(blockScopeContainer)) {
!isFunctionLikeOrClassStaticBlockDeclaration(blockScopeContainer)) {
// We check first if the name is inside class declaration or class expression; if so give explicit message
// otherwise report generic error message.
const errorSpan = getErrorSpanForNode(file, node);
Expand Down Expand Up @@ -2712,7 +2714,7 @@ namespace ts {
updateStrictModeStatementList((node as SourceFile).statements);
return bindSourceFileIfExternalModule();
case SyntaxKind.Block:
if (!isFunctionLike(node.parent)) {
if (!isFunctionLikeOrClassStaticBlockDeclaration(node.parent)) {
return;
}
// falls through
Expand Down
75 changes: 53 additions & 22 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24185,7 +24185,7 @@ namespace ts {
// To avoid that we will give an error to users if they use arguments objects in arrow function so that they
// can explicitly bound arguments objects
if (symbol === argumentsSymbol) {
if (isInPropertyInitializer(node)) {
if (isInPropertyInitializerOrClassStaticBlock(node)) {
error(node, Diagnostics.arguments_cannot_be_referenced_in_property_initializers);
return errorType;
}
Expand Down Expand Up @@ -24543,6 +24543,9 @@ namespace ts {
// do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks
}
break;
case SyntaxKind.ClassStaticBlockDeclaration:
error(node, Diagnostics.this_cannot_be_referenced_in_current_location);
break;
case SyntaxKind.ComputedPropertyName:
error(node, Diagnostics.this_cannot_be_referenced_in_a_computed_property_name);
break;
Expand Down Expand Up @@ -24601,7 +24604,8 @@ namespace ts {

if (isClassLike(container.parent)) {
const symbol = getSymbolOfNode(container.parent);
const type = hasSyntacticModifier(container, ModifierFlags.Static) ? getTypeOfSymbol(symbol) : (getDeclaredTypeOfSymbol(symbol) as InterfaceType).thisType!;
const isStatic = hasSyntacticModifier(container, ModifierFlags.Static) || isClassStaticBlockDeclaration(container);
const type = isStatic ? getTypeOfSymbol(symbol) : (getDeclaredTypeOfSymbol(symbol) as InterfaceType).thisType!;
return getFlowTypeOfReference(node, type);
}

Expand Down Expand Up @@ -27564,7 +27568,7 @@ namespace ts {

let diagnosticMessage;
const declarationName = idText(right);
if (isInPropertyInitializer(node)
if (isInPropertyInitializerOrClassStaticBlock(node)
&& !isOptionalPropertyDeclaration(valueDeclaration)
&& !(isAccessExpression(node) && isAccessExpression(node.expression))
&& !isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right)
Expand All @@ -27585,7 +27589,7 @@ namespace ts {
}
}

function isInPropertyInitializer(node: Node): boolean {
function isInPropertyInitializerOrClassStaticBlock(node: Node): boolean {
return !!findAncestor(node, node => {
switch (node.kind) {
case SyntaxKind.PropertyDeclaration:
Expand All @@ -27605,6 +27609,8 @@ namespace ts {
case SyntaxKind.ExpressionWithTypeArguments:
case SyntaxKind.HeritageClause:
return false;
case SyntaxKind.ExpressionStatement:
return isBlock(node.parent) && isClassStaticBlockDeclaration(node.parent.parent) ? true : "quit";
default:
return isExpressionNode(node) ? false : "quit";
}
Expand Down Expand Up @@ -31269,7 +31275,11 @@ namespace ts {
function checkAwaitExpression(node: AwaitExpression): Type {
// Grammar checking
if (produceDiagnostics) {
if (!(node.flags & NodeFlags.AwaitContext)) {
const container = getContainingFunctionOrClassStaticBlock(node);
if (container && isClassStaticBlockDeclaration(container)) {
error(node, Diagnostics.Await_expression_cannot_be_used_inside_a_class_static_block);
}
else if (!(node.flags & NodeFlags.AwaitContext)) {
if (isInTopLevelContext(node)) {
const sourceFile = getSourceFileOfNode(node);
if (!hasParseDiagnostics(sourceFile)) {
Expand All @@ -31294,9 +31304,8 @@ namespace ts {
if (!hasParseDiagnostics(sourceFile)) {
const span = getSpanOfTokenAtPosition(sourceFile, node.pos);
const diagnostic = createFileDiagnostic(sourceFile, span.start, span.length, Diagnostics.await_expressions_are_only_allowed_within_async_functions_and_at_the_top_levels_of_modules);
const func = getContainingFunction(node);
if (func && func.kind !== SyntaxKind.Constructor && (getFunctionFlags(func) & FunctionFlags.Async) === 0) {
const relatedInfo = createDiagnosticForNode(func, Diagnostics.Did_you_mean_to_mark_this_function_as_async);
if (container && container.kind !== SyntaxKind.Constructor && (getFunctionFlags(container) & FunctionFlags.Async) === 0) {
const relatedInfo = createDiagnosticForNode(container, Diagnostics.Did_you_mean_to_mark_this_function_as_async);
addRelatedInfo(diagnostic, relatedInfo);
}
diagnostics.add(diagnostic);
Expand Down Expand Up @@ -33455,6 +33464,12 @@ namespace ts {
}
}

function checkClassStaticBlockDeclaration(node: ClassStaticBlockDeclaration) {
checkGrammarDecoratorsAndModifiers(node);

forEachChild(node, checkSourceElement);
}

function checkConstructorDeclaration(node: ConstructorDeclaration) {
// Grammar check on signature of constructor and modifier of the constructor is done in checkSignatureDeclaration function.
checkSignatureDeclaration(node);
Expand Down Expand Up @@ -35062,10 +35077,11 @@ namespace ts {
break;
case SyntaxKind.IndexSignature:
case SyntaxKind.SemicolonClassElement:
case SyntaxKind.ClassStaticBlockDeclaration:
// Can't be private
break;
default:
Debug.fail();
Debug.fail("Unexpected class member");
}
}
}
Expand Down Expand Up @@ -35497,6 +35513,7 @@ namespace ts {
if (!node.name) {
return;
}

// For a computed property, just check the initializer and exit
// Do not use hasDynamicName here, because that returns false for well known symbols.
// We want to perform checkComputedPropertyName for all computed properties, including
Expand Down Expand Up @@ -35873,11 +35890,17 @@ namespace ts {
function checkForOfStatement(node: ForOfStatement): void {
checkGrammarForInOrForOfStatement(node);

const container = getContainingFunctionOrClassStaticBlock(node);
if (node.awaitModifier) {
const functionFlags = getFunctionFlags(getContainingFunction(node));
if ((functionFlags & (FunctionFlags.Invalid | FunctionFlags.Async)) === FunctionFlags.Async && languageVersion < ScriptTarget.ESNext) {
// for..await..of in an async function or async generator function prior to ESNext requires the __asyncValues helper
checkExternalEmitHelpers(node, ExternalEmitHelpers.ForAwaitOfIncludes);
if (container && isClassStaticBlockDeclaration(container)) {
grammarErrorOnNode(node.awaitModifier, Diagnostics.For_await_loops_cannot_be_used_inside_a_class_static_block);
}
else {
const functionFlags = getFunctionFlags(container);
if ((functionFlags & (FunctionFlags.Invalid | FunctionFlags.Async)) === FunctionFlags.Async && languageVersion < ScriptTarget.ESNext) {
// for..await..of in an async function or async generator function prior to ESNext requires the __asyncValues helper
checkExternalEmitHelpers(node, ExternalEmitHelpers.ForAwaitOfIncludes);
}
}
}
else if (compilerOptions.downlevelIteration && languageVersion < ScriptTarget.ES2015) {
Expand Down Expand Up @@ -36757,28 +36780,33 @@ namespace ts {
return;
}

const func = getContainingFunction(node);
if (!func) {
const container = getContainingFunctionOrClassStaticBlock(node);
if(container && isClassStaticBlockDeclaration(container)) {
grammarErrorOnFirstToken(node, Diagnostics.A_return_statement_cannot_be_used_inside_a_class_static_block);
return;
}

if (!container) {
grammarErrorOnFirstToken(node, Diagnostics.A_return_statement_can_only_be_used_within_a_function_body);
return;
}

const signature = getSignatureFromDeclaration(func);
const signature = getSignatureFromDeclaration(container);
const returnType = getReturnTypeOfSignature(signature);
const functionFlags = getFunctionFlags(func);
const functionFlags = getFunctionFlags(container);
if (strictNullChecks || node.expression || returnType.flags & TypeFlags.Never) {
const exprType = node.expression ? checkExpressionCached(node.expression) : undefinedType;
if (func.kind === SyntaxKind.SetAccessor) {
if (container.kind === SyntaxKind.SetAccessor) {
if (node.expression) {
error(node, Diagnostics.Setters_cannot_return_a_value);
}
}
else if (func.kind === SyntaxKind.Constructor) {
else if (container.kind === SyntaxKind.Constructor) {
if (node.expression && !checkTypeAssignableToAndOptionallyElaborate(exprType, returnType, node, node.expression)) {
error(node, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class);
}
}
else if (getReturnTypeFromAnnotation(func)) {
else if (getReturnTypeFromAnnotation(container)) {
const unwrappedReturnType = unwrapReturnType(returnType, functionFlags) ?? returnType;
const unwrappedExprType = functionFlags & FunctionFlags.Async
? 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)
Expand All @@ -36791,7 +36819,7 @@ namespace ts {
}
}
}
else if (func.kind !== SyntaxKind.Constructor && compilerOptions.noImplicitReturns && !isUnwrappedReturnTypeVoidOrAny(func, returnType)) {
else if (container.kind !== SyntaxKind.Constructor && compilerOptions.noImplicitReturns && !isUnwrappedReturnTypeVoidOrAny(container, returnType)) {
// The function has a return type, but the return statement doesn't have an expression.
error(node, Diagnostics.Not_all_code_paths_return_a_value);
}
Expand Down Expand Up @@ -38621,6 +38649,8 @@ namespace ts {
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
return checkMethodDeclaration(node as MethodDeclaration | MethodSignature);
case SyntaxKind.ClassStaticBlockDeclaration:
return checkClassStaticBlockDeclaration(node as ClassStaticBlockDeclaration);
case SyntaxKind.Constructor:
return checkConstructorDeclaration(node as ConstructorDeclaration);
case SyntaxKind.GetAccessor:
Expand Down Expand Up @@ -41098,6 +41128,7 @@ namespace ts {
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.VariableStatement:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.ClassStaticBlockDeclaration:
return true;
case SyntaxKind.EnumDeclaration:
return nodeHasAnyModifiersExcept(node, SyntaxKind.ConstKeyword);
Expand Down Expand Up @@ -41835,7 +41866,7 @@ namespace ts {
function checkGrammarBreakOrContinueStatement(node: BreakOrContinueStatement): boolean {
let current: Node = node;
while (current) {
if (isFunctionLike(current)) {
if (isFunctionLikeOrClassStaticBlockDeclaration(current)) {
return grammarErrorOnNode(node, Diagnostics.Jump_target_cannot_cross_function_boundary);
}

Expand Down
16 changes: 16 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -7154,5 +7154,21 @@
"Class decorators can't be used with static private identifier. Consider removing the experimental decorator.": {
"category": "Error",
"code": 18036
},
"Await expression cannot be used inside a class static block.": {
"category": "Error",
"code": 18037
},
"'For await' loops cannot be used inside a class static block.": {
"category": "Error",
"code": 18038
},
"Invalid use of '{0}'. It cannot be used inside a class static block.": {
"category": "Error",
"code": 18039
},
"A 'return' statement cannot be used inside a class static block.": {
"category": "Error",
"code": 18041
}
}
9 changes: 9 additions & 0 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1351,6 +1351,8 @@ namespace ts {
return emitMethodSignature(node as MethodSignature);
case SyntaxKind.MethodDeclaration:
return emitMethodDeclaration(node as MethodDeclaration);
case SyntaxKind.ClassStaticBlockDeclaration:
return emitClassStaticBlockDeclaration(node as ClassStaticBlockDeclaration);
case SyntaxKind.Constructor:
return emitConstructor(node as ConstructorDeclaration);
case SyntaxKind.GetAccessor:
Expand Down Expand Up @@ -2063,6 +2065,13 @@ namespace ts {
emitSignatureAndBody(node, emitSignatureHead);
}

function emitClassStaticBlockDeclaration(node: ClassStaticBlockDeclaration) {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
writeKeyword("static");
emitBlockFunctionBody(node.body);
}

function emitConstructor(node: ConstructorDeclaration) {
emitModifiers(node, node.modifiers);
writeKeyword("constructor");
Expand Down
34 changes: 34 additions & 0 deletions src/compiler/factory/nodeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ namespace ts {
updateConstructSignature,
createIndexSignature,
updateIndexSignature,
createClassStaticBlockDeclaration,
updateClassStaticBlockDeclaration,
createTemplateLiteralTypeSpan,
updateTemplateLiteralTypeSpan,
createKeywordTypeNode,
Expand Down Expand Up @@ -1415,6 +1417,38 @@ namespace ts {
: node;
}

// @api
function createClassStaticBlockDeclaration(
decorators: readonly Decorator[] | undefined,
modifiers: readonly Modifier[] | undefined,
body: Block
): ClassStaticBlockDeclaration {
const node = createBaseGenericNamedDeclaration<ClassStaticBlockDeclaration>(
SyntaxKind.ClassStaticBlockDeclaration,
decorators,
modifiers,
/*name*/ undefined,
/*typeParameters*/ undefined
);
node.body = body;
node.transformFlags = propagateChildFlags(body) | TransformFlags.ContainsClassFields;
return node;
}

// @api
function updateClassStaticBlockDeclaration(
node: ClassStaticBlockDeclaration,
decorators: readonly Decorator[] | undefined,
modifiers: readonly Modifier[] | undefined,
body: Block
): ClassStaticBlockDeclaration {
return node.decorators !== decorators
|| node.modifier !== modifiers
|| node.body !== body
? update(createClassStaticBlockDeclaration(decorators, modifiers, body), node)
: node;
}

// @api
function createConstructorDeclaration(
decorators: readonly Decorator[] | undefined,
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/factory/nodeTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ namespace ts {
return node.kind === SyntaxKind.MethodDeclaration;
}

export function isClassStaticBlockDeclaration(node: Node): node is ClassStaticBlockDeclaration {
return node.kind === SyntaxKind.ClassStaticBlockDeclaration;
}

export function isConstructorDeclaration(node: Node): node is ConstructorDeclaration {
return node.kind === SyntaxKind.Constructor;
}
Expand Down
Loading