Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Support for auto-accessor fields from the Stage 3 Decorators proposal (
…#49705)

* Support for auto-accessor fields

* Add tests, ensure accessors are initialized in ctor

* classFields cleanup and PR feedback
  • Loading branch information
rbuckton committed Sep 12, 2022
1 parent 7737473 commit a4cabe7
Show file tree
Hide file tree
Showing 196 changed files with 7,457 additions and 1,227 deletions.
2 changes: 1 addition & 1 deletion scripts/eslint/rules/debug-assert.js
Expand Up @@ -46,7 +46,7 @@ module.exports = createRule({
context.report({ messageId: "secondArgumentDebugAssertError", node: message1Node });
}

if (argsLen < 3) {
if (argsLen !== 3) {
return;
}

Expand Down
5 changes: 4 additions & 1 deletion src/compiler/binder.ts
Expand Up @@ -2732,7 +2732,10 @@ namespace ts {
}

function bindPropertyWorker(node: PropertyDeclaration | PropertySignature) {
return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes);
const isAutoAccessor = isAutoAccessorPropertyDeclaration(node);
const includes = isAutoAccessor ? SymbolFlags.Accessor : SymbolFlags.Property;
const excludes = isAutoAccessor ? SymbolFlags.AccessorExcludes : SymbolFlags.PropertyExcludes;
return bindPropertyOrMethodOrAccessor(node, includes | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), excludes);
}

function bindAnonymousTypeWorker(node: TypeLiteralNode | MappedTypeNode | JSDocTypeLiteral) {
Expand Down
93 changes: 74 additions & 19 deletions src/compiler/checker.ts
Expand Up @@ -9083,7 +9083,7 @@ namespace ts {
return getTypeForBindingElement(declaration as BindingElement);
}

const isProperty = isPropertyDeclaration(declaration) || isPropertySignature(declaration);
const isProperty = isPropertyDeclaration(declaration) && !hasAccessorModifier(declaration) || isPropertySignature(declaration);
const isOptional = includeOptionality && (
isProperty && !!declaration.questionToken ||
isParameter(declaration) && (!!declaration.questionToken || isJSDocOptionalParameter(declaration)) ||
Expand Down Expand Up @@ -9831,21 +9831,25 @@ namespace ts {
return type;
}

function getAnnotatedAccessorTypeNode(accessor: AccessorDeclaration | undefined): TypeNode | undefined {
function getAnnotatedAccessorTypeNode(accessor: AccessorDeclaration | PropertyDeclaration | undefined): TypeNode | undefined {
if (accessor) {
if (accessor.kind === SyntaxKind.GetAccessor) {
const getterTypeAnnotation = getEffectiveReturnTypeNode(accessor);
return getterTypeAnnotation;
}
else {
const setterTypeAnnotation = getEffectiveSetAccessorTypeAnnotationNode(accessor);
return setterTypeAnnotation;
switch (accessor.kind) {
case SyntaxKind.GetAccessor:
const getterTypeAnnotation = getEffectiveReturnTypeNode(accessor);
return getterTypeAnnotation;
case SyntaxKind.SetAccessor:
const setterTypeAnnotation = getEffectiveSetAccessorTypeAnnotationNode(accessor);
return setterTypeAnnotation;
case SyntaxKind.PropertyDeclaration:
Debug.assert(hasAccessorModifier(accessor));
const accessorTypeAnnotation = getEffectiveTypeAnnotationNode(accessor);
return accessorTypeAnnotation;
}
}
return undefined;
}

function getAnnotatedAccessorType(accessor: AccessorDeclaration | undefined): Type | undefined {
function getAnnotatedAccessorType(accessor: AccessorDeclaration | PropertyDeclaration | undefined): Type | undefined {
const node = getAnnotatedAccessorTypeNode(accessor);
return node && getTypeFromTypeNode(node);
}
Expand All @@ -9867,19 +9871,26 @@ namespace ts {
}
const getter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.GetAccessor);
const setter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.SetAccessor);
const accessor = tryCast(getDeclarationOfKind<PropertyDeclaration>(symbol, SyntaxKind.PropertyDeclaration), isAutoAccessorPropertyDeclaration);

// We try to resolve a getter type annotation, a setter type annotation, or a getter function
// body return type inference, in that order.
let type = getter && isInJSFile(getter) && getTypeForDeclarationFromJSDocComment(getter) ||
getAnnotatedAccessorType(getter) ||
getAnnotatedAccessorType(setter) ||
getter && getter.body && getReturnTypeFromBody(getter);
getAnnotatedAccessorType(accessor) ||
getter && getter.body && getReturnTypeFromBody(getter) ||
accessor && accessor.initializer && getWidenedTypeForVariableLikeDeclaration(accessor, /*includeOptionality*/ true);
if (!type) {
if (setter && !isPrivateWithinAmbient(setter)) {
errorOrSuggestion(noImplicitAny, setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation, symbolToString(symbol));
}
else if (getter && !isPrivateWithinAmbient(getter)) {
errorOrSuggestion(noImplicitAny, getter, Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation, symbolToString(symbol));
}
else if (accessor && !isPrivateWithinAmbient(accessor)) {
errorOrSuggestion(noImplicitAny, accessor, Diagnostics.Member_0_implicitly_has_an_1_type, symbolToString(symbol), "any");
}
type = anyType;
}
if (!popTypeResolution()) {
Expand All @@ -9889,6 +9900,9 @@ namespace ts {
else if (getAnnotatedAccessorTypeNode(setter)) {
error(setter, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, symbolToString(symbol));
}
else if (getAnnotatedAccessorTypeNode(accessor)) {
error(setter, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, symbolToString(symbol));
}
else if (getter && noImplicitAny) {
error(getter, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, symbolToString(symbol));
}
Expand All @@ -9905,7 +9919,9 @@ namespace ts {
if (!pushTypeResolution(symbol, TypeSystemPropertyName.WriteType)) {
return errorType;
}
const setter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.SetAccessor);

const setter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.SetAccessor)
?? tryCast(getDeclarationOfKind<PropertyDeclaration>(symbol, SyntaxKind.PropertyDeclaration), isAutoAccessorPropertyDeclaration);
let writeType = getAnnotatedAccessorType(setter);
if (!popTypeResolution()) {
if (getAnnotatedAccessorTypeNode(setter)) {
Expand Down Expand Up @@ -11063,8 +11079,10 @@ namespace ts {
const members = getMembersOfDeclaration(decl);
if (members) {
for (const member of members) {
if (isStatic === hasStaticModifier(member) && hasLateBindableName(member)) {
lateBindMember(symbol, earlySymbols, lateSymbols, member);
if (isStatic === hasStaticModifier(member)) {
if (hasLateBindableName(member)) {
lateBindMember(symbol, earlySymbols, lateSymbols, member);
}
}
}
}
Expand All @@ -11078,8 +11096,10 @@ namespace ts {
|| isBinaryExpression(member) && isPossiblyAliasedThisProperty(member, assignmentKind)
|| assignmentKind === AssignmentDeclarationKind.ObjectDefinePrototypeProperty
|| assignmentKind === AssignmentDeclarationKind.Prototype; // A straight `Prototype` assignment probably can never have a computed name
if (isStatic === !isInstanceMember && hasLateBindableName(member)) {
lateBindMember(symbol, earlySymbols, lateSymbols, member);
if (isStatic === !isInstanceMember) {
if (hasLateBindableName(member)) {
lateBindMember(symbol, earlySymbols, lateSymbols, member);
}
}
}
}
Expand Down Expand Up @@ -12924,7 +12944,7 @@ namespace ts {
}

function isOptionalPropertyDeclaration(node: Declaration) {
return isPropertyDeclaration(node) && node.questionToken;
return isPropertyDeclaration(node) && !hasAccessorModifier(node) && node.questionToken;
}

function isOptionalJSDocPropertyLikeTag(node: Node): node is JSDocPropertyLikeTag {
Expand Down Expand Up @@ -30759,7 +30779,7 @@ namespace ts {
// A method or accessor declaration decorator will have two or three arguments (see
// `PropertyDecorator` and `MethodDecorator` in core.d.ts). If we are emitting decorators
// for ES3, we will only pass two arguments.
const hasPropDesc = parent.kind !== SyntaxKind.PropertyDeclaration && languageVersion !== ScriptTarget.ES3;
const hasPropDesc = languageVersion !== ScriptTarget.ES3 && (!isPropertyDeclaration(parent) || hasAccessorModifier(parent));
return [
createSyntheticExpression(expr, getParentTypeOfClassElement(parent as ClassElement)),
createSyntheticExpression(expr, getClassElementPropertyKeyType(parent as ClassElement)),
Expand All @@ -30778,7 +30798,7 @@ namespace ts {
case SyntaxKind.ClassExpression:
return 1;
case SyntaxKind.PropertyDeclaration:
return 2;
return hasAccessorModifier(node.parent) ? 3 : 2;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
Expand Down Expand Up @@ -44189,6 +44209,9 @@ namespace ts {
else if (flags & ModifierFlags.Readonly) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "override", "readonly");
}
else if (flags & ModifierFlags.Accessor) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "override", "accessor");
}
else if (flags & ModifierFlags.Async) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "override", "async");
}
Expand All @@ -44210,6 +44233,9 @@ namespace ts {
else if (flags & ModifierFlags.Static) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "static");
}
else if (flags & ModifierFlags.Accessor) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "accessor");
}
else if (flags & ModifierFlags.Readonly) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "readonly");
}
Expand Down Expand Up @@ -44243,6 +44269,9 @@ namespace ts {
else if (flags & ModifierFlags.Async) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "static", "async");
}
else if (flags & ModifierFlags.Accessor) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "static", "accessor");
}
else if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_module_or_namespace_element, "static");
}
Expand All @@ -44259,6 +44288,23 @@ namespace ts {
lastStatic = modifier;
break;

case SyntaxKind.AccessorKeyword:
if (flags & ModifierFlags.Accessor) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "accessor");
}
else if (flags & ModifierFlags.Readonly) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, "accessor", "readonly");
}
else if (flags & ModifierFlags.Ambient) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, "accessor", "declare");
}
else if (node.kind !== SyntaxKind.PropertyDeclaration) {
return grammarErrorOnNode(modifier, Diagnostics.accessor_modifier_can_only_appear_on_a_property_declaration);
}

flags |= ModifierFlags.Accessor;
break;

case SyntaxKind.ReadonlyKeyword:
if (flags & ModifierFlags.Readonly) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "readonly");
Expand Down Expand Up @@ -44355,6 +44401,9 @@ namespace ts {
if (flags & ModifierFlags.Override) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "abstract", "override");
}
if (flags & ModifierFlags.Accessor) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "abstract", "accessor");
}
}
if (isNamedDeclaration(node) && node.name.kind === SyntaxKind.PrivateIdentifier) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_a_private_identifier, "abstract");
Expand Down Expand Up @@ -45553,6 +45602,12 @@ namespace ts {
if (languageVersion < ScriptTarget.ES2015 && isPrivateIdentifier(node.name)) {
return grammarErrorOnNode(node.name, Diagnostics.Private_identifiers_are_only_available_when_targeting_ECMAScript_2015_and_higher);
}
if (languageVersion < ScriptTarget.ES2015 && isAutoAccessorPropertyDeclaration(node)) {
return grammarErrorOnNode(node.name, Diagnostics.Properties_with_the_accessor_modifier_are_only_available_when_targeting_ECMAScript_2015_and_higher);
}
if (isAutoAccessorPropertyDeclaration(node) && checkGrammarForInvalidQuestionMark(node.questionToken, Diagnostics.An_accessor_property_cannot_be_declared_optional)) {
return true;
}
}
else if (node.parent.kind === SyntaxKind.InterfaceDeclaration) {
if (checkGrammarForInvalidDynamicName(node.name, Diagnostics.A_computed_property_name_in_an_interface_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type)) {
Expand Down
12 changes: 12 additions & 0 deletions src/compiler/diagnosticMessages.json
Expand Up @@ -899,6 +899,14 @@
"category": "Error",
"code": 1274
},
"'accessor' modifier can only appear on a property declaration.": {
"category": "Error",
"code": 1275
},
"An 'accessor' property cannot be declared optional.": {
"category": "Error",
"code": 1276
},

"'with' statements are not allowed in an async function block.": {
"category": "Error",
Expand Down Expand Up @@ -7485,5 +7493,9 @@
"'{0}' is automatically exported here.": {
"category": "Message",
"code": 18044
},
"Properties with the 'accessor' modifier are only available when targeting ECMAScript 2015 and higher.": {
"category": "Error",
"code": 18045
}
}

0 comments on commit a4cabe7

Please sign in to comment.