From 8b353c8ec0865fde11b5fbfd5807f19ed8a521fd Mon Sep 17 00:00:00 2001 From: Federico Ciardi Date: Tue, 13 Apr 2021 08:52:28 +0200 Subject: [PATCH 1/2] fix: raise `SyntaxError` for `declare` before getter/setter --- .../babel-parser/src/parser/error-message.js | 1 + packages/babel-parser/src/parser/statement.js | 9 ++- .../src/plugins/typescript/index.js | 14 ++++ .../class/declare-accessor/input.ts | 4 + .../class/declare-accessor/output.json | 73 +++++++++++++++++++ 5 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 packages/babel-parser/test/fixtures/typescript/class/declare-accessor/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/class/declare-accessor/output.json diff --git a/packages/babel-parser/src/parser/error-message.js b/packages/babel-parser/src/parser/error-message.js index 2a46b8b7be00..422b98432bc7 100644 --- a/packages/babel-parser/src/parser/error-message.js +++ b/packages/babel-parser/src/parser/error-message.js @@ -32,6 +32,7 @@ export const ErrorMessages = Object.freeze({ ConstructorIsAsync: "Constructor can't be an async function", ConstructorIsGenerator: "Constructor can't be a generator", DeclarationMissingInitializer: "%0 require an initialization value", + DeclareAccessor: "'declare' is not allowed in %0ters.", DecoratorBeforeExport: "Decorators must be placed *before* the 'export' keyword. You can set the 'decoratorsBeforeExport' option to false to use the 'export @decorator class {}' syntax", DecoratorConstructor: diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 28813bb6de8b..13662e4c5446 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -1509,7 +1509,14 @@ export default class StatementParser extends ExpressionParser { // https://tc39.es/proposal-class-fields/#prod-ClassElementName parseClassElementName(member: N.ClassMember): N.Expression | N.Identifier { const key = this.parsePropertyName(member, /* isPrivateNameAllowed */ true); + this.checkClassElementName(member, key); + return key; + } + checkClassElementName( + member: N.ClassMember, + key: N.Expression | N.Identifier, + ) { if ( !member.computed && member.static && @@ -1525,8 +1532,6 @@ export default class StatementParser extends ExpressionParser { ) { this.raise(key.start, Errors.ConstructorClassPrivateField); } - - return key; } parseClassStaticBlock( diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 737b174e7367..215713de157c 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -2191,6 +2191,20 @@ export default (superClass: Class): Class => return this.tsParseModifier(["public", "protected", "private"]); } + checkClassElementName( + member: N.ClassMember, + key: N.Expression | N.Identifier, + ) { + if ( + // $FlowIgnore + member.declare && + (key.name === "get" || key.name === "set") + ) { + this.raise(member.start, Errors.DeclareAccessor, key.name); + } + super.checkClassElementName(member, key); + } + parseClassMember( classBody: N.ClassBody, member: any, diff --git a/packages/babel-parser/test/fixtures/typescript/class/declare-accessor/input.ts b/packages/babel-parser/test/fixtures/typescript/class/declare-accessor/input.ts new file mode 100644 index 000000000000..6ca324a281db --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/declare-accessor/input.ts @@ -0,0 +1,4 @@ +class Foo { + declare get foo() + declare set foo(v) +} diff --git a/packages/babel-parser/test/fixtures/typescript/class/declare-accessor/output.json b/packages/babel-parser/test/fixtures/typescript/class/declare-accessor/output.json new file mode 100644 index 000000000000..5cd5577a3aa0 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/declare-accessor/output.json @@ -0,0 +1,73 @@ +{ + "type": "File", + "start":0,"end":54,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}}, + "errors": [ + "SyntaxError: 'declare' is not allowed in getters. (2:2)", + "SyntaxError: 'declare' is not allowed in setters. (3:2)" + ], + "program": { + "type": "Program", + "start":0,"end":54,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ClassDeclaration", + "start":0,"end":54,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}}, + "id": { + "type": "Identifier", + "start":6,"end":9,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":9},"identifierName":"Foo"}, + "name": "Foo" + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start":10,"end":54,"loc":{"start":{"line":1,"column":10},"end":{"line":4,"column":1}}, + "body": [ + { + "type": "TSDeclareMethod", + "start":14,"end":31,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":19}}, + "declare": true, + "static": false, + "key": { + "type": "Identifier", + "start":26,"end":29,"loc":{"start":{"line":2,"column":14},"end":{"line":2,"column":17},"identifierName":"foo"}, + "name": "foo" + }, + "computed": false, + "kind": "get", + "id": null, + "generator": false, + "async": false, + "params": [] + }, + { + "type": "TSDeclareMethod", + "start":34,"end":52,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":20}}, + "declare": true, + "static": false, + "key": { + "type": "Identifier", + "start":46,"end":49,"loc":{"start":{"line":3,"column":14},"end":{"line":3,"column":17},"identifierName":"foo"}, + "name": "foo" + }, + "computed": false, + "kind": "set", + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start":50,"end":51,"loc":{"start":{"line":3,"column":18},"end":{"line":3,"column":19},"identifierName":"v"}, + "name": "v" + } + ] + } + ] + } + } + ], + "directives": [] + } +} \ No newline at end of file From 2055132c1bb0bcabe02abd6a3a6dc694624710a3 Mon Sep 17 00:00:00 2001 From: Federico Ciardi Date: Sat, 17 Apr 2021 17:08:11 +0200 Subject: [PATCH 2/2] fix: allow `declare` when class property name is `get` or `set` --- .../babel-parser/src/parser/error-message.js | 1 - packages/babel-parser/src/parser/statement.js | 9 +-- .../src/plugins/typescript/index.js | 20 ++---- .../class/declare-get-set-field/input.ts | 4 ++ .../class/declare-get-set-field/output.json | 71 +++++++++++++++++++ 5 files changed, 83 insertions(+), 22 deletions(-) create mode 100644 packages/babel-parser/test/fixtures/typescript/class/declare-get-set-field/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/class/declare-get-set-field/output.json diff --git a/packages/babel-parser/src/parser/error-message.js b/packages/babel-parser/src/parser/error-message.js index 422b98432bc7..2a46b8b7be00 100644 --- a/packages/babel-parser/src/parser/error-message.js +++ b/packages/babel-parser/src/parser/error-message.js @@ -32,7 +32,6 @@ export const ErrorMessages = Object.freeze({ ConstructorIsAsync: "Constructor can't be an async function", ConstructorIsGenerator: "Constructor can't be a generator", DeclarationMissingInitializer: "%0 require an initialization value", - DeclareAccessor: "'declare' is not allowed in %0ters.", DecoratorBeforeExport: "Decorators must be placed *before* the 'export' keyword. You can set the 'decoratorsBeforeExport' option to false to use the 'export @decorator class {}' syntax", DecoratorConstructor: diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 13662e4c5446..28813bb6de8b 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -1509,14 +1509,7 @@ export default class StatementParser extends ExpressionParser { // https://tc39.es/proposal-class-fields/#prod-ClassElementName parseClassElementName(member: N.ClassMember): N.Expression | N.Identifier { const key = this.parsePropertyName(member, /* isPrivateNameAllowed */ true); - this.checkClassElementName(member, key); - return key; - } - checkClassElementName( - member: N.ClassMember, - key: N.Expression | N.Identifier, - ) { if ( !member.computed && member.static && @@ -1532,6 +1525,8 @@ export default class StatementParser extends ExpressionParser { ) { this.raise(key.start, Errors.ConstructorClassPrivateField); } + + return key; } parseClassStaticBlock( diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 215713de157c..0ec491241272 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -67,6 +67,7 @@ const TSErrors = Object.freeze({ ClassMethodHasReadonly: "Class methods cannot have the 'readonly' modifier", ConstructorHasTypeParameters: "Type parameters cannot appear on a constructor declaration.", + DeclareAccessor: "'declare' is not allowed in %0ters.", DeclareClassFieldHasInitializer: "Initializers are not allowed in ambient contexts.", DeclareFunctionHasImplementation: @@ -2191,20 +2192,6 @@ export default (superClass: Class): Class => return this.tsParseModifier(["public", "protected", "private"]); } - checkClassElementName( - member: N.ClassMember, - key: N.Expression | N.Identifier, - ) { - if ( - // $FlowIgnore - member.declare && - (key.name === "get" || key.name === "set") - ) { - this.raise(member.start, Errors.DeclareAccessor, key.name); - } - super.checkClassElementName(member, key); - } - parseClassMember( classBody: N.ClassBody, member: any, @@ -2484,6 +2471,11 @@ export default (superClass: Class): Class => if (typeParameters && isConstructor) { this.raise(typeParameters.start, TSErrors.ConstructorHasTypeParameters); } + + // $FlowIgnore + if (method.declare && (method.kind === "get" || method.kind === "set")) { + this.raise(method.start, TSErrors.DeclareAccessor, method.kind); + } if (typeParameters) method.typeParameters = typeParameters; super.pushClassMethod( classBody, diff --git a/packages/babel-parser/test/fixtures/typescript/class/declare-get-set-field/input.ts b/packages/babel-parser/test/fixtures/typescript/class/declare-get-set-field/input.ts new file mode 100644 index 000000000000..fe1189b6ea44 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/declare-get-set-field/input.ts @@ -0,0 +1,4 @@ +class C { + declare get: string + declare set: string; +} diff --git a/packages/babel-parser/test/fixtures/typescript/class/declare-get-set-field/output.json b/packages/babel-parser/test/fixtures/typescript/class/declare-get-set-field/output.json new file mode 100644 index 000000000000..5c5e5704397b --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/declare-get-set-field/output.json @@ -0,0 +1,71 @@ +{ + "type": "File", + "start":0,"end":56,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}}, + "program": { + "type": "Program", + "start":0,"end":56,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ClassDeclaration", + "start":0,"end":56,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}}, + "id": { + "type": "Identifier", + "start":6,"end":7,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":7},"identifierName":"C"}, + "name": "C" + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start":8,"end":56,"loc":{"start":{"line":1,"column":8},"end":{"line":4,"column":1}}, + "body": [ + { + "type": "ClassProperty", + "start":12,"end":31,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":21}}, + "declare": true, + "static": false, + "key": { + "type": "Identifier", + "start":20,"end":23,"loc":{"start":{"line":2,"column":10},"end":{"line":2,"column":13},"identifierName":"get"}, + "name": "get" + }, + "computed": false, + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start":23,"end":31,"loc":{"start":{"line":2,"column":13},"end":{"line":2,"column":21}}, + "typeAnnotation": { + "type": "TSStringKeyword", + "start":25,"end":31,"loc":{"start":{"line":2,"column":15},"end":{"line":2,"column":21}} + } + }, + "value": null + }, + { + "type": "ClassProperty", + "start":34,"end":54,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":22}}, + "declare": true, + "static": false, + "key": { + "type": "Identifier", + "start":42,"end":45,"loc":{"start":{"line":3,"column":10},"end":{"line":3,"column":13},"identifierName":"set"}, + "name": "set" + }, + "computed": false, + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start":45,"end":53,"loc":{"start":{"line":3,"column":13},"end":{"line":3,"column":21}}, + "typeAnnotation": { + "type": "TSStringKeyword", + "start":47,"end":53,"loc":{"start":{"line":3,"column":15},"end":{"line":3,"column":21}} + } + }, + "value": null + } + ] + } + } + ], + "directives": [] + } +} \ No newline at end of file