diff --git a/src/diagnosticMessages.generated.ts b/src/diagnosticMessages.generated.ts index 2db65faca4..08f802e245 100644 --- a/src/diagnosticMessages.generated.ts +++ b/src/diagnosticMessages.generated.ts @@ -65,6 +65,7 @@ export enum DiagnosticCode { A_rest_parameter_must_be_last_in_a_parameter_list = 1014, Parameter_cannot_have_question_mark_and_initializer = 1015, A_required_parameter_cannot_follow_an_optional_parameter = 1016, + _0_modifier_cannot_appear_on_class_elements_of_this_kind = 1031, Statements_are_not_allowed_in_ambient_contexts = 1036, Initializers_are_not_allowed_in_ambient_contexts = 1039, _0_modifier_cannot_be_used_here = 1042, @@ -249,6 +250,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string { case 1014: return "A rest parameter must be last in a parameter list."; case 1015: return "Parameter cannot have question mark and initializer."; case 1016: return "A required parameter cannot follow an optional parameter."; + case 1031: return "'{0}' modifier cannot appear on class elements of this kind."; case 1036: return "Statements are not allowed in ambient contexts."; case 1039: return "Initializers are not allowed in ambient contexts."; case 1042: return "'{0}' modifier cannot be used here."; diff --git a/src/diagnosticMessages.json b/src/diagnosticMessages.json index 0b6d18ab7b..80ad2ef498 100644 --- a/src/diagnosticMessages.json +++ b/src/diagnosticMessages.json @@ -62,6 +62,7 @@ "A rest parameter must be last in a parameter list.": 1014, "Parameter cannot have question mark and initializer.": 1015, "A required parameter cannot follow an optional parameter.": 1016, + "'{0}' modifier cannot appear on class elements of this kind.": 1031, "Statements are not allowed in ambient contexts.": 1036, "Initializers are not allowed in ambient contexts.": 1039, "'{0}' modifier cannot be used here.": 1042, diff --git a/src/extra/ast.ts b/src/extra/ast.ts index ffa272d1a8..cbffac40fb 100644 --- a/src/extra/ast.ts +++ b/src/extra/ast.ts @@ -1677,6 +1677,9 @@ export class ASTBuilder { serializeAccessModifiers(node: DeclarationStatement): void { var sb = this.sb; + if (node.is(CommonFlags.DECLARE)) { + sb.push("declare "); + } if (node.is(CommonFlags.PUBLIC)) { sb.push("public "); } else if (node.is(CommonFlags.PRIVATE)) { diff --git a/src/parser.ts b/src/parser.ts index 53bbdd0562..60510cdcd0 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -1839,6 +1839,7 @@ export class Parser extends DiagnosticEmitter { ): Node | null { // before: + // 'declare'? // ('public' | 'private' | 'protected')? // ('static' | 'abstract')? // 'readonly'? @@ -1870,6 +1871,32 @@ export class Parser extends DiagnosticEmitter { // implemented methods are virtual if (isInterface) flags |= CommonFlags.VIRTUAL; + var declareStart = 0; + var declareEnd = 0; + var contextIsAmbient = parent.is(CommonFlags.AMBIENT); + if (tn.skip(Token.DECLARE)) { + if (isInterface) { + this.error( + DiagnosticCode._0_modifier_cannot_be_used_here, + tn.range(), "declare" + ); + } else { + if (contextIsAmbient) { + this.error( + DiagnosticCode.A_declare_modifier_cannot_be_used_in_an_already_ambient_context, + tn.range() + ); // recoverable + } else { + flags |= CommonFlags.DECLARE | CommonFlags.AMBIENT; + declareStart = tn.tokenPos; + declareEnd = tn.pos; + } + } + if (!startPos) startPos = tn.tokenPos; + } else if (contextIsAmbient) { + flags |= CommonFlags.AMBIENT; + } + var accessStart = 0; var accessEnd = 0; if (tn.skip(Token.PUBLIC)) { @@ -2108,6 +2135,13 @@ export class Parser extends DiagnosticEmitter { // method: '(' Parameters (':' Type)? '{' Statement* '}' ';'? if (tn.skip(Token.OPENPAREN)) { + if (flags & CommonFlags.DECLARE) { + this.error( + DiagnosticCode._0_modifier_cannot_appear_on_class_elements_of_this_kind, + tn.range(declareStart, declareEnd), "declare" + ); // recoverable + } + let signatureStart = tn.tokenPos; let parameters = this.parseParameters(tn, isConstructor); if (!parameters) return null; @@ -2249,6 +2283,13 @@ export class Parser extends DiagnosticEmitter { // field: (':' Type)? ('=' Expression)? ';'? } else { + if (flags & CommonFlags.DECLARE) { + this.error( + DiagnosticCode.Not_implemented_0, + tn.range(declareStart, declareEnd), "Ambient fields" + ); // recoverable + } + if (flags & CommonFlags.ABSTRACT) { this.error( DiagnosticCode._0_modifier_cannot_be_used_here, @@ -2291,6 +2332,12 @@ export class Parser extends DiagnosticEmitter { } let initializer: Expression | null = null; if (tn.skip(Token.EQUALS)) { + if (flags & CommonFlags.AMBIENT) { + this.error( + DiagnosticCode.Initializers_are_not_allowed_in_ambient_contexts, + tn.range() + ); // recoverable + } initializer = this.parseExpression(tn); if (!initializer) return null; } diff --git a/tests/parser/also-identifier.ts b/tests/parser/also-identifier.ts index 39b5477bba..702cead65b 100644 --- a/tests/parser/also-identifier.ts +++ b/tests/parser/also-identifier.ts @@ -1,6 +1,6 @@ class Foo { as: i32; - declare: i32; + //declare: i32; delete: i32; from: i32; for: i32; @@ -19,7 +19,7 @@ class Foo { var as: i32; var constructor: i32; -var declare: i32; +//var declare: i32; var from: i32; var get: i32; var is: i32; diff --git a/tests/parser/also-identifier.ts.fixture.ts b/tests/parser/also-identifier.ts.fixture.ts index 8de8b51707..8d3188b81e 100644 --- a/tests/parser/also-identifier.ts.fixture.ts +++ b/tests/parser/also-identifier.ts.fixture.ts @@ -1,6 +1,5 @@ class Foo { as: i32; - declare: i32; delete: i32; from: i32; for: i32; @@ -18,7 +17,6 @@ class Foo { } var as: i32; var constructor: i32; -var declare: i32; var from: i32; var get: i32; var is: i32; diff --git a/tests/parser/class.ts b/tests/parser/class.ts index e80a34b527..b55a2880e6 100644 --- a/tests/parser/class.ts +++ b/tests/parser/class.ts @@ -26,4 +26,15 @@ export class Invalid { // 1049: A 'set' accessor must have exactly one parameter. // 1095: A 'set' accessor cannot have a return type annotation. set instanceSetter(): i32 {} + + // 100: Not implemented: Ambient fields + declare declareField: i32; + + // 100: Not implemented: Ambient fields + // 1039: Initializers are not allowed in ambient contexts. + declare declareInitializer: i32 = 0; + + // 1031: 'declare' modifier cannot appear on class elements of this kind. + // 1183: An implementation cannot be declared in ambient contexts. + declare declareMethod(): i32 {} } diff --git a/tests/parser/class.ts.fixture.ts b/tests/parser/class.ts.fixture.ts index dfdd064c5b..9d25f7a31d 100644 --- a/tests/parser/class.ts.fixture.ts +++ b/tests/parser/class.ts.fixture.ts @@ -14,6 +14,9 @@ export class Invalid { instanceFunction() {} get instanceGetter(a: i32) {} set instanceSetter() {} + declare declareField: i32; + declare declareInitializer: i32 = 0; + declare declareMethod(): i32 {} } // ERROR 1092: "Type parameters cannot appear on a constructor declaration." in class.ts(15,14+3) // ERROR 1110: "Type expected." in class.ts(18,21+0) @@ -23,3 +26,8 @@ export class Invalid { // ERROR 1094: "An accessor cannot have type parameters." in class.ts(28,21+3) // ERROR 1049: "A 'set' accessor must have exactly one parameter." in class.ts(28,7+14) // ERROR 1095: "A 'set' accessor cannot have a return type annotation." in class.ts(28,26+1) +// ERROR 100: "Not implemented: Ambient fields" in class.ts(31,3+7) +// ERROR 100: "Not implemented: Ambient fields" in class.ts(35,3+7) +// ERROR 1039: "Initializers are not allowed in ambient contexts." in class.ts(35,35+1) +// ERROR 1031: "'declare' modifier cannot appear on class elements of this kind." in class.ts(39,3+7) +// ERROR 1183: "An implementation cannot be declared in ambient contexts." in class.ts(39,32+1)