From 39a12674b49a1078adced83831cbd0676176f361 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sun, 27 Sep 2020 04:52:12 +0900 Subject: [PATCH] Improve syntax error for class fields in ambient context (#12108) * Improve error messages for ambient context class fields * Modify switching state.isDeclareContext for class fields with declare --- .../src/plugins/typescript/index.js | 37 +++++++--- .../declare-field-initializer/output.json | 2 +- .../class/declare-initializer/input.ts | 3 + .../class/declare-initializer/output.json | 53 +++++++++++++++ .../typescript/declare/module-class/input.ts | 5 ++ .../declare/module-class/options.json | 3 + .../declare/module-class/output.json | 68 +++++++++++++++++++ .../{module => module-function}/input.ts | 0 .../{module => module-function}/output.json | 0 .../declare/namespace-class/input.ts | 6 ++ .../declare/namespace-class/options.json | 3 + .../declare/namespace-class/output.json | 68 +++++++++++++++++++ .../input.ts | 0 .../output.json | 0 14 files changed, 237 insertions(+), 11 deletions(-) create mode 100644 packages/babel-parser/test/fixtures/typescript/class/declare-initializer/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/class/declare-initializer/output.json create mode 100644 packages/babel-parser/test/fixtures/typescript/declare/module-class/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/declare/module-class/options.json create mode 100644 packages/babel-parser/test/fixtures/typescript/declare/module-class/output.json rename packages/babel-parser/test/fixtures/typescript/declare/{module => module-function}/input.ts (100%) rename packages/babel-parser/test/fixtures/typescript/declare/{module => module-function}/output.json (100%) create mode 100644 packages/babel-parser/test/fixtures/typescript/declare/namespace-class/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/declare/namespace-class/options.json create mode 100644 packages/babel-parser/test/fixtures/typescript/declare/namespace-class/output.json rename packages/babel-parser/test/fixtures/typescript/declare/{namespace => namespace-function}/input.ts (100%) rename packages/babel-parser/test/fixtures/typescript/declare/{namespace => namespace-function}/output.json (100%) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 2f48b914d4c7..1b9b7f1f3e83 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -68,7 +68,7 @@ const TSErrors = Object.freeze({ ConstructorHasTypeParameters: "Type parameters cannot appear on a constructor declaration.", DeclareClassFieldHasInitializer: - "'declare' class fields cannot have an initializer", + "Initializers are not allowed in ambient contexts.", DeclareFunctionHasImplementation: "An implementation cannot be declared in ambient contexts.", DuplicateModifier: "Duplicate modifier: '%0'", @@ -1478,10 +1478,7 @@ export default (superClass: Class): Class => kind = "let"; } - const oldIsDeclareContext = this.state.isDeclareContext; - this.state.isDeclareContext = true; - - try { + return this.tsInDeclareContext(() => { switch (starttype) { case tt._function: nany.declare = true; @@ -1519,9 +1516,7 @@ export default (superClass: Class): Class => } } } - } finally { - this.state.isDeclareContext = oldIsDeclareContext; - } + }); } // Note: this won't be called unless the keyword is allowed in `shouldParseExportDeclaration`. @@ -2081,7 +2076,19 @@ export default (superClass: Class): Class => if (accessibility) member.accessibility = accessibility; this.tsParseModifiers(member, ["declare"]); - super.parseClassMember(classBody, member, state, constructorAllowsSuper); + const callParseClassMember = () => { + super.parseClassMember( + classBody, + member, + state, + constructorAllowsSuper, + ); + }; + if (member.declare) { + this.tsInDeclareContext(callParseClassMember); + } else { + callParseClassMember(); + } } parseClassMemberWithIsStatic( @@ -2294,7 +2301,7 @@ export default (superClass: Class): Class => parseClassProperty(node: N.ClassProperty): N.ClassProperty { this.parseClassPropertyAnnotation(node); - if (node.declare && this.match(tt.eq)) { + if (this.state.isDeclareContext && this.match(tt.eq)) { this.raise(this.state.start, TSErrors.DeclareClassFieldHasInitializer); } @@ -2781,4 +2788,14 @@ export default (superClass: Class): Class => return param; } + + tsInDeclareContext(cb: () => T): T { + const oldIsDeclareContext = this.state.isDeclareContext; + this.state.isDeclareContext = true; + try { + return cb(); + } finally { + this.state.isDeclareContext = oldIsDeclareContext; + } + } }; diff --git a/packages/babel-parser/test/fixtures/typescript/class/declare-field-initializer/output.json b/packages/babel-parser/test/fixtures/typescript/class/declare-field-initializer/output.json index a9dd381301a2..c7c1398ea0f3 100644 --- a/packages/babel-parser/test/fixtures/typescript/class/declare-field-initializer/output.json +++ b/packages/babel-parser/test/fixtures/typescript/class/declare-field-initializer/output.json @@ -2,7 +2,7 @@ "type": "File", "start":0,"end":43,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, "errors": [ - "SyntaxError: 'declare' class fields cannot have an initializer (2:22)" + "SyntaxError: Initializers are not allowed in ambient contexts. (2:22)" ], "program": { "type": "Program", diff --git a/packages/babel-parser/test/fixtures/typescript/class/declare-initializer/input.ts b/packages/babel-parser/test/fixtures/typescript/class/declare-initializer/input.ts new file mode 100644 index 000000000000..2e65f447ea37 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/declare-initializer/input.ts @@ -0,0 +1,3 @@ +declare class C { + field = "field"; +} diff --git a/packages/babel-parser/test/fixtures/typescript/class/declare-initializer/output.json b/packages/babel-parser/test/fixtures/typescript/class/declare-initializer/output.json new file mode 100644 index 000000000000..1c94185430d2 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/declare-initializer/output.json @@ -0,0 +1,53 @@ +{ + "type": "File", + "start":0,"end":38,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "errors": [ + "SyntaxError: Initializers are not allowed in ambient contexts. (2:8)" + ], + "program": { + "type": "Program", + "start":0,"end":38,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ClassDeclaration", + "start":0,"end":38,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "declare": true, + "id": { + "type": "Identifier", + "start":14,"end":15,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":15},"identifierName":"C"}, + "name": "C" + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start":16,"end":38,"loc":{"start":{"line":1,"column":16},"end":{"line":3,"column":1}}, + "body": [ + { + "type": "ClassProperty", + "start":20,"end":36,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":18}}, + "static": false, + "key": { + "type": "Identifier", + "start":20,"end":25,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":7},"identifierName":"field"}, + "name": "field" + }, + "computed": false, + "value": { + "type": "StringLiteral", + "start":28,"end":35,"loc":{"start":{"line":2,"column":10},"end":{"line":2,"column":17}}, + "extra": { + "rawValue": "field", + "raw": "\"field\"" + }, + "value": "field" + } + } + ] + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/declare/module-class/input.ts b/packages/babel-parser/test/fixtures/typescript/declare/module-class/input.ts new file mode 100644 index 000000000000..3e9ccd1749f7 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/declare/module-class/input.ts @@ -0,0 +1,5 @@ +declare module m { + class C { + field = "field"; + } +} diff --git a/packages/babel-parser/test/fixtures/typescript/declare/module-class/options.json b/packages/babel-parser/test/fixtures/typescript/declare/module-class/options.json new file mode 100644 index 000000000000..fb92dc3bde16 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/declare/module-class/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["typescript", "classProperties"] +} diff --git a/packages/babel-parser/test/fixtures/typescript/declare/module-class/output.json b/packages/babel-parser/test/fixtures/typescript/declare/module-class/output.json new file mode 100644 index 000000000000..baf67a410c24 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/declare/module-class/output.json @@ -0,0 +1,68 @@ +{ + "type": "File", + "start":0,"end":57,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":1}}, + "errors": [ + "SyntaxError: Initializers are not allowed in ambient contexts. (3:10)" + ], + "program": { + "type": "Program", + "start":0,"end":57,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":1}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "TSModuleDeclaration", + "start":0,"end":57,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":1}}, + "id": { + "type": "Identifier", + "start":15,"end":16,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":16},"identifierName":"m"}, + "name": "m" + }, + "body": { + "type": "TSModuleBlock", + "start":17,"end":57,"loc":{"start":{"line":1,"column":17},"end":{"line":5,"column":1}}, + "body": [ + { + "type": "ClassDeclaration", + "start":21,"end":55,"loc":{"start":{"line":2,"column":2},"end":{"line":4,"column":3}}, + "id": { + "type": "Identifier", + "start":27,"end":28,"loc":{"start":{"line":2,"column":8},"end":{"line":2,"column":9},"identifierName":"C"}, + "name": "C" + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start":29,"end":55,"loc":{"start":{"line":2,"column":10},"end":{"line":4,"column":3}}, + "body": [ + { + "type": "ClassProperty", + "start":35,"end":51,"loc":{"start":{"line":3,"column":4},"end":{"line":3,"column":20}}, + "static": false, + "key": { + "type": "Identifier", + "start":35,"end":40,"loc":{"start":{"line":3,"column":4},"end":{"line":3,"column":9},"identifierName":"field"}, + "name": "field" + }, + "computed": false, + "value": { + "type": "StringLiteral", + "start":43,"end":50,"loc":{"start":{"line":3,"column":12},"end":{"line":3,"column":19}}, + "extra": { + "rawValue": "field", + "raw": "\"field\"" + }, + "value": "field" + } + } + ] + } + } + ] + }, + "declare": true + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/declare/module/input.ts b/packages/babel-parser/test/fixtures/typescript/declare/module-function/input.ts similarity index 100% rename from packages/babel-parser/test/fixtures/typescript/declare/module/input.ts rename to packages/babel-parser/test/fixtures/typescript/declare/module-function/input.ts diff --git a/packages/babel-parser/test/fixtures/typescript/declare/module/output.json b/packages/babel-parser/test/fixtures/typescript/declare/module-function/output.json similarity index 100% rename from packages/babel-parser/test/fixtures/typescript/declare/module/output.json rename to packages/babel-parser/test/fixtures/typescript/declare/module-function/output.json diff --git a/packages/babel-parser/test/fixtures/typescript/declare/namespace-class/input.ts b/packages/babel-parser/test/fixtures/typescript/declare/namespace-class/input.ts new file mode 100644 index 000000000000..fe6e94b55240 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/declare/namespace-class/input.ts @@ -0,0 +1,6 @@ +declare namespace m { + class C { + field = "field"; + } +} + \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/declare/namespace-class/options.json b/packages/babel-parser/test/fixtures/typescript/declare/namespace-class/options.json new file mode 100644 index 000000000000..fb92dc3bde16 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/declare/namespace-class/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["typescript", "classProperties"] +} diff --git a/packages/babel-parser/test/fixtures/typescript/declare/namespace-class/output.json b/packages/babel-parser/test/fixtures/typescript/declare/namespace-class/output.json new file mode 100644 index 000000000000..503a33e792dd --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/declare/namespace-class/output.json @@ -0,0 +1,68 @@ +{ + "type": "File", + "start":0,"end":60,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":1}}, + "errors": [ + "SyntaxError: Initializers are not allowed in ambient contexts. (3:10)" + ], + "program": { + "type": "Program", + "start":0,"end":60,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":1}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "TSModuleDeclaration", + "start":0,"end":60,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":1}}, + "id": { + "type": "Identifier", + "start":18,"end":19,"loc":{"start":{"line":1,"column":18},"end":{"line":1,"column":19},"identifierName":"m"}, + "name": "m" + }, + "body": { + "type": "TSModuleBlock", + "start":20,"end":60,"loc":{"start":{"line":1,"column":20},"end":{"line":5,"column":1}}, + "body": [ + { + "type": "ClassDeclaration", + "start":24,"end":58,"loc":{"start":{"line":2,"column":2},"end":{"line":4,"column":3}}, + "id": { + "type": "Identifier", + "start":30,"end":31,"loc":{"start":{"line":2,"column":8},"end":{"line":2,"column":9},"identifierName":"C"}, + "name": "C" + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start":32,"end":58,"loc":{"start":{"line":2,"column":10},"end":{"line":4,"column":3}}, + "body": [ + { + "type": "ClassProperty", + "start":38,"end":54,"loc":{"start":{"line":3,"column":4},"end":{"line":3,"column":20}}, + "static": false, + "key": { + "type": "Identifier", + "start":38,"end":43,"loc":{"start":{"line":3,"column":4},"end":{"line":3,"column":9},"identifierName":"field"}, + "name": "field" + }, + "computed": false, + "value": { + "type": "StringLiteral", + "start":46,"end":53,"loc":{"start":{"line":3,"column":12},"end":{"line":3,"column":19}}, + "extra": { + "rawValue": "field", + "raw": "\"field\"" + }, + "value": "field" + } + } + ] + } + } + ] + }, + "declare": true + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/declare/namespace/input.ts b/packages/babel-parser/test/fixtures/typescript/declare/namespace-function/input.ts similarity index 100% rename from packages/babel-parser/test/fixtures/typescript/declare/namespace/input.ts rename to packages/babel-parser/test/fixtures/typescript/declare/namespace-function/input.ts diff --git a/packages/babel-parser/test/fixtures/typescript/declare/namespace/output.json b/packages/babel-parser/test/fixtures/typescript/declare/namespace-function/output.json similarity index 100% rename from packages/babel-parser/test/fixtures/typescript/declare/namespace/output.json rename to packages/babel-parser/test/fixtures/typescript/declare/namespace-function/output.json