diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 9447dd03256e..8083de418fbd 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -36,9 +36,7 @@ type TsModifier = | "abstract" | "declare" | "static" - | "public" - | "private" - | "protected"; + | N.Accessibility; function nonNull(x: ?T): T { if (x == null) { @@ -71,6 +69,7 @@ const TSErrors = Object.freeze({ DeclareFunctionHasImplementation: "An implementation cannot be declared in ambient contexts.", DuplicateModifier: "Duplicate modifier: '%0'", + DuplicateAccessibilityModifier: "Accessibility modifier already seen.", EmptyHeritageClauseType: "'%0' list cannot be empty.", EmptyTypeArguments: "Type argument list cannot be empty.", EmptyTypeParameters: "Type parameter list cannot be empty.", @@ -144,6 +143,12 @@ function keywordTypeFromName( } } +function tsIsAccessModifier(modifier: string): boolean %checks { + return ( + modifier === "private" || modifier === "public" || modifier === "protected" + ); +} + export default (superClass: Class): Class => class extends superClass { getScopeHandler(): Class { @@ -194,20 +199,31 @@ export default (superClass: Class): Class => * this.tsParseModifiers(node, ["public"]); * this.tsParseModifiers(node, ["abstract", "readonly"]); */ - tsParseModifiers( - modified: { [key: TsModifier]: ?true }, - allowedModifiers: T[], + tsParseModifiers( + modified: { + [key: TsModifier]: ?true, + accessibility?: N.Accessibility, + }, + allowedModifiers: TsModifier[], ): void { for (;;) { const startPos = this.state.start; - const modifier: ?T = this.tsParseModifier(allowedModifiers); + const modifier: ?TsModifier = this.tsParseModifier(allowedModifiers); if (!modifier) break; - if (Object.hasOwnProperty.call(modified, modifier)) { - this.raise(startPos, TSErrors.DuplicateModifier, modifier); + if (tsIsAccessModifier(modifier)) { + if (modified.accessibility) { + this.raise(startPos, TSErrors.DuplicateAccessibilityModifier); + } else { + modified.accessibility = modifier; + } + } else { + if (Object.hasOwnProperty.call(modified, modifier)) { + this.raise(startPos, TSErrors.DuplicateModifier, modifier); + } + modified[modifier] = true; } - modified[modifier] = true; } } @@ -2125,10 +2141,12 @@ export default (superClass: Class): Class => member: any, state: N.ParseClassMemberState, ): void { - this.tsParseModifiers(member, ["declare"]); - const accessibility = this.parseAccessModifier(); - if (accessibility) member.accessibility = accessibility; - this.tsParseModifiers(member, ["declare"]); + this.tsParseModifiers(member, [ + "declare", + "private", + "public", + "protected", + ]); const callParseClassMember = () => { super.parseClassMember(classBody, member, state); diff --git a/packages/babel-parser/test/fixtures/typescript/class/duplicates-accessibility/input.ts b/packages/babel-parser/test/fixtures/typescript/class/duplicates-accessibility/input.ts new file mode 100644 index 000000000000..fa01be69f2c4 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/duplicates-accessibility/input.ts @@ -0,0 +1,7 @@ +class Foo { + public public a; + private public b; + protected private c; + public protected d; + public protected private e; +} diff --git a/packages/babel-parser/test/fixtures/typescript/class/duplicates-accessibility/output.json b/packages/babel-parser/test/fixtures/typescript/class/duplicates-accessibility/output.json new file mode 100644 index 000000000000..8f1fde109674 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/duplicates-accessibility/output.json @@ -0,0 +1,102 @@ +{ + "type": "File", + "start":0,"end":127,"loc":{"start":{"line":1,"column":0},"end":{"line":7,"column":1}}, + "errors": [ + "SyntaxError: Accessibility modifier already seen. (2:9)", + "SyntaxError: Accessibility modifier already seen. (3:10)", + "SyntaxError: Accessibility modifier already seen. (4:12)", + "SyntaxError: Accessibility modifier already seen. (5:9)", + "SyntaxError: Accessibility modifier already seen. (6:9)", + "SyntaxError: Accessibility modifier already seen. (6:19)" + ], + "program": { + "type": "Program", + "start":0,"end":127,"loc":{"start":{"line":1,"column":0},"end":{"line":7,"column":1}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ClassDeclaration", + "start":0,"end":127,"loc":{"start":{"line":1,"column":0},"end":{"line":7,"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":127,"loc":{"start":{"line":1,"column":10},"end":{"line":7,"column":1}}, + "body": [ + { + "type": "ClassProperty", + "start":14,"end":30,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":18}}, + "accessibility": "public", + "static": false, + "key": { + "type": "Identifier", + "start":28,"end":29,"loc":{"start":{"line":2,"column":16},"end":{"line":2,"column":17},"identifierName":"a"}, + "name": "a" + }, + "computed": false, + "value": null + }, + { + "type": "ClassProperty", + "start":33,"end":50,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":19}}, + "accessibility": "private", + "static": false, + "key": { + "type": "Identifier", + "start":48,"end":49,"loc":{"start":{"line":3,"column":17},"end":{"line":3,"column":18},"identifierName":"b"}, + "name": "b" + }, + "computed": false, + "value": null + }, + { + "type": "ClassProperty", + "start":53,"end":73,"loc":{"start":{"line":4,"column":2},"end":{"line":4,"column":22}}, + "accessibility": "protected", + "static": false, + "key": { + "type": "Identifier", + "start":71,"end":72,"loc":{"start":{"line":4,"column":20},"end":{"line":4,"column":21},"identifierName":"c"}, + "name": "c" + }, + "computed": false, + "value": null + }, + { + "type": "ClassProperty", + "start":76,"end":95,"loc":{"start":{"line":5,"column":2},"end":{"line":5,"column":21}}, + "accessibility": "public", + "static": false, + "key": { + "type": "Identifier", + "start":93,"end":94,"loc":{"start":{"line":5,"column":19},"end":{"line":5,"column":20},"identifierName":"d"}, + "name": "d" + }, + "computed": false, + "value": null + }, + { + "type": "ClassProperty", + "start":98,"end":125,"loc":{"start":{"line":6,"column":2},"end":{"line":6,"column":29}}, + "accessibility": "public", + "static": false, + "key": { + "type": "Identifier", + "start":123,"end":124,"loc":{"start":{"line":6,"column":27},"end":{"line":6,"column":28},"identifierName":"e"}, + "name": "e" + }, + "computed": false, + "value": null + } + ] + } + } + ], + "directives": [] + } +} \ No newline at end of file