Allow modifies on type members + introduce "readonly" modifier
This commit is contained in:
parent
911d07a81b
commit
b46efc9b55
|
@ -15675,9 +15675,17 @@ namespace ts {
|
|||
return;
|
||||
}
|
||||
|
||||
let lastStatic: Node, lastPrivate: Node, lastProtected: Node, lastDeclare: Node, lastAsync: Node;
|
||||
let lastStatic: Node, lastPrivate: Node, lastProtected: Node, lastDeclare: Node, lastAsync: Node, lastReadonly: Node;
|
||||
let flags = 0;
|
||||
for (const modifier of node.modifiers) {
|
||||
if (modifier.kind !== SyntaxKind.ReadonlyKeyword) {
|
||||
if (node.kind === SyntaxKind.PropertySignature || node.kind === SyntaxKind.MethodSignature) {
|
||||
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_type_member, tokenToString(modifier.kind));
|
||||
}
|
||||
if (node.kind === SyntaxKind.IndexSignature) {
|
||||
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_an_index_signature, tokenToString(modifier.kind));
|
||||
}
|
||||
}
|
||||
switch (modifier.kind) {
|
||||
case SyntaxKind.ConstKeyword:
|
||||
if (node.kind !== SyntaxKind.EnumDeclaration && node.parent.kind === SyntaxKind.ClassDeclaration) {
|
||||
|
@ -15706,6 +15714,9 @@ namespace ts {
|
|||
else if (flags & NodeFlags.Static) {
|
||||
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "static");
|
||||
}
|
||||
else if (flags & NodeFlags.Readonly) {
|
||||
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "readonly");
|
||||
}
|
||||
else if (flags & NodeFlags.Async) {
|
||||
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "async");
|
||||
}
|
||||
|
@ -15727,6 +15738,9 @@ namespace ts {
|
|||
if (flags & NodeFlags.Static) {
|
||||
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "static");
|
||||
}
|
||||
else if (flags & NodeFlags.Readonly) {
|
||||
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "static", "readonly");
|
||||
}
|
||||
else if (flags & NodeFlags.Async) {
|
||||
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "static", "async");
|
||||
}
|
||||
|
@ -15743,6 +15757,20 @@ namespace ts {
|
|||
lastStatic = modifier;
|
||||
break;
|
||||
|
||||
case SyntaxKind.ReadonlyKeyword:
|
||||
if (flags & NodeFlags.Readonly) {
|
||||
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "readonly");
|
||||
}
|
||||
else if (flags & NodeFlags.Async) {
|
||||
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "readonly", "async");
|
||||
}
|
||||
else if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) {
|
||||
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_module_element, "readonly");
|
||||
}
|
||||
flags |= NodeFlags.Readonly;
|
||||
lastReadonly = modifier;
|
||||
break;
|
||||
|
||||
case SyntaxKind.ExportKeyword:
|
||||
if (flags & NodeFlags.Export) {
|
||||
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "export");
|
||||
|
@ -15839,6 +15867,9 @@ namespace ts {
|
|||
else if (flags & NodeFlags.Async) {
|
||||
return grammarErrorOnNode(lastAsync, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "async");
|
||||
}
|
||||
else if (flags & NodeFlags.Readonly) {
|
||||
return grammarErrorOnNode(lastReadonly, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "readonly");
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if ((node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) && flags & NodeFlags.Ambient) {
|
||||
|
@ -15984,15 +16015,9 @@ namespace ts {
|
|||
}
|
||||
}
|
||||
|
||||
function checkGrammarForIndexSignatureModifier(node: SignatureDeclaration): void {
|
||||
if (node.flags & NodeFlags.Modifier) {
|
||||
grammarErrorOnFirstToken(node, Diagnostics.Modifiers_not_permitted_on_index_signature_members);
|
||||
}
|
||||
}
|
||||
|
||||
function checkGrammarIndexSignature(node: SignatureDeclaration) {
|
||||
// Prevent cascading error by short-circuit
|
||||
return checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarIndexSignatureParameters(node) || checkGrammarForIndexSignatureModifier(node);
|
||||
return checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarIndexSignatureParameters(node);
|
||||
}
|
||||
|
||||
function checkGrammarForAtLeastOneTypeArgument(node: Node, typeArguments: NodeArray<TypeNode>): boolean {
|
||||
|
|
|
@ -203,6 +203,14 @@
|
|||
"category": "Error",
|
||||
"code": 1068
|
||||
},
|
||||
"'{0}' modifier cannot appear on a type member.": {
|
||||
"category": "Error",
|
||||
"code": 1070
|
||||
},
|
||||
"'{0}' modifier cannot appear on an index signature.": {
|
||||
"category": "Error",
|
||||
"code": 1071
|
||||
},
|
||||
"A '{0}' modifier cannot be used with an import declaration.": {
|
||||
"category": "Error",
|
||||
"code": 1079
|
||||
|
@ -423,10 +431,6 @@
|
|||
"category": "Error",
|
||||
"code": 1144
|
||||
},
|
||||
"Modifiers not permitted on index signature members.": {
|
||||
"category": "Error",
|
||||
"code": 1145
|
||||
},
|
||||
"Declaration expected.": {
|
||||
"category": "Error",
|
||||
"code": 1146
|
||||
|
|
|
@ -1192,7 +1192,7 @@ namespace ts {
|
|||
case ParsingContext.SwitchClauses:
|
||||
return token === SyntaxKind.CaseKeyword || token === SyntaxKind.DefaultKeyword;
|
||||
case ParsingContext.TypeMembers:
|
||||
return isStartOfTypeMember();
|
||||
return lookAhead(isTypeMemberStart);
|
||||
case ParsingContext.ClassMembers:
|
||||
// We allow semicolons as class elements (as specified by ES6) as long as we're
|
||||
// not in error recovery. If we're in error recovery, we don't want an errant
|
||||
|
@ -2233,13 +2233,13 @@ namespace ts {
|
|||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parsePropertyOrMethodSignature(): PropertySignature | MethodSignature {
|
||||
const fullStart = scanner.getStartPos();
|
||||
function parsePropertyOrMethodSignature(fullStart: number, modifiers: ModifiersArray): PropertySignature | MethodSignature {
|
||||
const name = parsePropertyName();
|
||||
const questionToken = parseOptionalToken(SyntaxKind.QuestionToken);
|
||||
|
||||
if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) {
|
||||
const method = <MethodSignature>createNode(SyntaxKind.MethodSignature, fullStart);
|
||||
setModifiers(method, modifiers);
|
||||
method.name = name;
|
||||
method.questionToken = questionToken;
|
||||
|
||||
|
@ -2251,6 +2251,7 @@ namespace ts {
|
|||
}
|
||||
else {
|
||||
const property = <PropertySignature>createNode(SyntaxKind.PropertySignature, fullStart);
|
||||
setModifiers(property, modifiers);
|
||||
property.name = name;
|
||||
property.questionToken = questionToken;
|
||||
property.type = parseTypeAnnotation();
|
||||
|
@ -2267,77 +2268,51 @@ namespace ts {
|
|||
}
|
||||
}
|
||||
|
||||
function isStartOfTypeMember(): boolean {
|
||||
switch (token) {
|
||||
case SyntaxKind.OpenParenToken:
|
||||
case SyntaxKind.LessThanToken:
|
||||
case SyntaxKind.OpenBracketToken: // Both for indexers and computed properties
|
||||
return true;
|
||||
default:
|
||||
if (isModifierKind(token)) {
|
||||
const result = lookAhead(isStartOfIndexSignatureDeclaration);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return isLiteralPropertyName() && lookAhead(isTypeMemberWithLiteralPropertyName);
|
||||
function isTypeMemberStart(): boolean {
|
||||
let idToken: SyntaxKind;
|
||||
// Return true if we have the start of a signature member
|
||||
if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function isStartOfIndexSignatureDeclaration() {
|
||||
// Eat up all modifiers, but hold on to the last one in case it is actually an identifier
|
||||
while (isModifierKind(token)) {
|
||||
idToken = token;
|
||||
nextToken();
|
||||
}
|
||||
|
||||
return isIndexSignature();
|
||||
}
|
||||
|
||||
function isTypeMemberWithLiteralPropertyName() {
|
||||
nextToken();
|
||||
return token === SyntaxKind.OpenParenToken ||
|
||||
token === SyntaxKind.LessThanToken ||
|
||||
token === SyntaxKind.QuestionToken ||
|
||||
token === SyntaxKind.ColonToken ||
|
||||
canParseSemicolon();
|
||||
// Index signatures and computed property names are type members
|
||||
if (token === SyntaxKind.OpenBracketToken) {
|
||||
return true;
|
||||
}
|
||||
// Try to get the first property-like token following all modifiers
|
||||
if (isLiteralPropertyName()) {
|
||||
idToken = token;
|
||||
nextToken();
|
||||
}
|
||||
// If we were able to get any potential identifier, check that it is
|
||||
// the start of a member declaration
|
||||
if (idToken) {
|
||||
return token === SyntaxKind.OpenParenToken ||
|
||||
token === SyntaxKind.LessThanToken ||
|
||||
token === SyntaxKind.QuestionToken ||
|
||||
token === SyntaxKind.ColonToken ||
|
||||
canParseSemicolon();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function parseTypeMember(): TypeElement {
|
||||
switch (token) {
|
||||
case SyntaxKind.OpenParenToken:
|
||||
case SyntaxKind.LessThanToken:
|
||||
return parseSignatureMember(SyntaxKind.CallSignature);
|
||||
case SyntaxKind.OpenBracketToken:
|
||||
// Indexer or computed property
|
||||
return isIndexSignature()
|
||||
? parseIndexSignatureDeclaration(scanner.getStartPos(), /*decorators*/ undefined, /*modifiers*/ undefined)
|
||||
: parsePropertyOrMethodSignature();
|
||||
case SyntaxKind.NewKeyword:
|
||||
if (lookAhead(isStartOfConstructSignature)) {
|
||||
return parseSignatureMember(SyntaxKind.ConstructSignature);
|
||||
}
|
||||
// fall through.
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NumericLiteral:
|
||||
return parsePropertyOrMethodSignature();
|
||||
default:
|
||||
// Index declaration as allowed as a type member. But as per the grammar,
|
||||
// they also allow modifiers. So we have to check for an index declaration
|
||||
// that might be following modifiers. This ensures that things work properly
|
||||
// when incrementally parsing as the parser will produce the Index declaration
|
||||
// if it has the same text regardless of whether it is inside a class or an
|
||||
// object type.
|
||||
if (isModifierKind(token)) {
|
||||
const result = tryParse(parseIndexSignatureWithModifiers);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (tokenIsIdentifierOrKeyword(token)) {
|
||||
return parsePropertyOrMethodSignature();
|
||||
}
|
||||
if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) {
|
||||
return parseSignatureMember(SyntaxKind.CallSignature);
|
||||
}
|
||||
if (token === SyntaxKind.NewKeyword && lookAhead(isStartOfConstructSignature)) {
|
||||
return parseSignatureMember(SyntaxKind.ConstructSignature);
|
||||
}
|
||||
const fullStart = getNodePos();
|
||||
const modifiers = parseModifiers();
|
||||
if (isIndexSignature()) {
|
||||
return parseIndexSignatureDeclaration(fullStart, /*decorators*/ undefined, modifiers);
|
||||
}
|
||||
return parsePropertyOrMethodSignature(fullStart, modifiers);
|
||||
}
|
||||
|
||||
function parseIndexSignatureWithModifiers() {
|
||||
|
@ -4423,6 +4398,7 @@ namespace ts {
|
|||
case SyntaxKind.PrivateKeyword:
|
||||
case SyntaxKind.ProtectedKeyword:
|
||||
case SyntaxKind.PublicKeyword:
|
||||
case SyntaxKind.ReadonlyKeyword:
|
||||
nextToken();
|
||||
// ASI takes effect for this modifier.
|
||||
if (scanner.hasPrecedingLineBreak()) {
|
||||
|
@ -4501,6 +4477,7 @@ namespace ts {
|
|||
case SyntaxKind.PrivateKeyword:
|
||||
case SyntaxKind.ProtectedKeyword:
|
||||
case SyntaxKind.StaticKeyword:
|
||||
case SyntaxKind.ReadonlyKeyword:
|
||||
// When these don't start a declaration, they may be the start of a class member if an identifier
|
||||
// immediately follows. Otherwise they're an identifier in an expression statement.
|
||||
return isStartOfDeclaration() || !lookAhead(nextTokenIsIdentifierOrKeywordOnSameLine);
|
||||
|
@ -4582,6 +4559,7 @@ namespace ts {
|
|||
case SyntaxKind.PublicKeyword:
|
||||
case SyntaxKind.AbstractKeyword:
|
||||
case SyntaxKind.StaticKeyword:
|
||||
case SyntaxKind.ReadonlyKeyword:
|
||||
if (isStartOfDeclaration()) {
|
||||
return parseDeclaration();
|
||||
}
|
||||
|
@ -4868,6 +4846,7 @@ namespace ts {
|
|||
case SyntaxKind.PrivateKeyword:
|
||||
case SyntaxKind.ProtectedKeyword:
|
||||
case SyntaxKind.StaticKeyword:
|
||||
case SyntaxKind.ReadonlyKeyword:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
|
|
@ -836,6 +836,7 @@ namespace ts {
|
|||
case SyntaxKind.PublicKeyword:
|
||||
case SyntaxKind.PrivateKeyword:
|
||||
case SyntaxKind.ProtectedKeyword:
|
||||
case SyntaxKind.ReadonlyKeyword:
|
||||
case SyntaxKind.DeclareKeyword:
|
||||
diagnostics.push(createDiagnosticForNode(modifier, Diagnostics._0_can_only_be_used_in_a_ts_file, tokenToString(modifier.kind)));
|
||||
return true;
|
||||
|
|
|
@ -93,6 +93,7 @@ namespace ts {
|
|||
"private": SyntaxKind.PrivateKeyword,
|
||||
"protected": SyntaxKind.ProtectedKeyword,
|
||||
"public": SyntaxKind.PublicKeyword,
|
||||
"readonly": SyntaxKind.ReadonlyKeyword,
|
||||
"require": SyntaxKind.RequireKeyword,
|
||||
"return": SyntaxKind.ReturnKeyword,
|
||||
"set": SyntaxKind.SetKeyword,
|
||||
|
|
|
@ -163,6 +163,7 @@ namespace ts {
|
|||
IsKeyword,
|
||||
ModuleKeyword,
|
||||
NamespaceKeyword,
|
||||
ReadonlyKeyword,
|
||||
RequireKeyword,
|
||||
NumberKeyword,
|
||||
SetKeyword,
|
||||
|
@ -375,24 +376,24 @@ namespace ts {
|
|||
Private = 1 << 4, // Property/Method
|
||||
Protected = 1 << 5, // Property/Method
|
||||
Static = 1 << 6, // Property/Method
|
||||
Abstract = 1 << 7, // Class/Method/ConstructSignature
|
||||
Async = 1 << 8, // Property/Method/Function
|
||||
Default = 1 << 9, // Function/Class (export default declaration)
|
||||
MultiLine = 1 << 10, // Multi-line array or object literal
|
||||
Synthetic = 1 << 11, // Synthetic node (for full fidelity)
|
||||
DeclarationFile = 1 << 12, // Node is a .d.ts file
|
||||
Let = 1 << 13, // Variable declaration
|
||||
Const = 1 << 14, // Variable declaration
|
||||
OctalLiteral = 1 << 15, // Octal numeric literal
|
||||
Namespace = 1 << 16, // Namespace declaration
|
||||
ExportContext = 1 << 17, // Export context (initialized by binding)
|
||||
ContainsThis = 1 << 18, // Interface contains references to "this"
|
||||
HasImplicitReturn = 1 << 19, // If function implicitly returns on one of codepaths (initialized by binding)
|
||||
HasExplicitReturn = 1 << 20, // If function has explicit reachable return on one of codepaths (initialized by binding)
|
||||
Modifier = Export | Ambient | Public | Private | Protected | Static | Abstract | Default | Async,
|
||||
Readonly = 1 << 7, // Property/Method
|
||||
Abstract = 1 << 8, // Class/Method/ConstructSignature
|
||||
Async = 1 << 9, // Property/Method/Function
|
||||
Default = 1 << 10, // Function/Class (export default declaration)
|
||||
MultiLine = 1 << 11, // Multi-line array or object literal
|
||||
Synthetic = 1 << 12, // Synthetic node (for full fidelity)
|
||||
DeclarationFile = 1 << 13, // Node is a .d.ts file
|
||||
Let = 1 << 14, // Variable declaration
|
||||
Const = 1 << 15, // Variable declaration
|
||||
OctalLiteral = 1 << 16, // Octal numeric literal
|
||||
Namespace = 1 << 17, // Namespace declaration
|
||||
ExportContext = 1 << 18, // Export context (initialized by binding)
|
||||
ContainsThis = 1 << 19, // Interface contains references to "this"
|
||||
HasImplicitReturn = 1 << 20, // If function implicitly returns on one of codepaths (initialized by binding)
|
||||
HasExplicitReturn = 1 << 21, // If function has explicit reachable return on one of codepaths (initialized by binding)
|
||||
Modifier = Export | Ambient | Public | Private | Protected | Static | Readonly | Abstract | Default | Async,
|
||||
AccessibilityModifier = Public | Private | Protected,
|
||||
BlockScoped = Let | Const,
|
||||
|
||||
ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn
|
||||
}
|
||||
|
||||
|
|
|
@ -1548,6 +1548,7 @@ namespace ts {
|
|||
case SyntaxKind.PublicKeyword:
|
||||
case SyntaxKind.PrivateKeyword:
|
||||
case SyntaxKind.ProtectedKeyword:
|
||||
case SyntaxKind.ReadonlyKeyword:
|
||||
case SyntaxKind.StaticKeyword:
|
||||
return true;
|
||||
}
|
||||
|
@ -2279,6 +2280,7 @@ namespace ts {
|
|||
case SyntaxKind.ConstKeyword: return NodeFlags.Const;
|
||||
case SyntaxKind.DefaultKeyword: return NodeFlags.Default;
|
||||
case SyntaxKind.AsyncKeyword: return NodeFlags.Async;
|
||||
case SyntaxKind.ReadonlyKeyword: return NodeFlags.Readonly;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue