ts: Throw recoverable for duplicates access modifier (#12775)

* Support parsing for duplicates access modifiers

Support parsing for duplicates access modifiers

* Fix lint problems

* Address reviews
This commit is contained in:
Sosuke Suzuki 2021-02-09 17:59:06 +09:00 committed by GitHub
parent d242ea04c8
commit f1a327506e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 141 additions and 14 deletions

View File

@ -36,9 +36,7 @@ type TsModifier =
| "abstract" | "abstract"
| "declare" | "declare"
| "static" | "static"
| "public" | N.Accessibility;
| "private"
| "protected";
function nonNull<T>(x: ?T): T { function nonNull<T>(x: ?T): T {
if (x == null) { if (x == null) {
@ -71,6 +69,7 @@ const TSErrors = Object.freeze({
DeclareFunctionHasImplementation: DeclareFunctionHasImplementation:
"An implementation cannot be declared in ambient contexts.", "An implementation cannot be declared in ambient contexts.",
DuplicateModifier: "Duplicate modifier: '%0'", DuplicateModifier: "Duplicate modifier: '%0'",
DuplicateAccessibilityModifier: "Accessibility modifier already seen.",
EmptyHeritageClauseType: "'%0' list cannot be empty.", EmptyHeritageClauseType: "'%0' list cannot be empty.",
EmptyTypeArguments: "Type argument list cannot be empty.", EmptyTypeArguments: "Type argument list cannot be empty.",
EmptyTypeParameters: "Type parameter list cannot be empty.", EmptyTypeParameters: "Type parameter list cannot be empty.",
@ -146,6 +145,12 @@ function keywordTypeFromName(
} }
} }
function tsIsAccessModifier(modifier: string): boolean %checks {
return (
modifier === "private" || modifier === "public" || modifier === "protected"
);
}
export default (superClass: Class<Parser>): Class<Parser> => export default (superClass: Class<Parser>): Class<Parser> =>
class extends superClass { class extends superClass {
getScopeHandler(): Class<TypeScriptScopeHandler> { getScopeHandler(): Class<TypeScriptScopeHandler> {
@ -196,20 +201,31 @@ export default (superClass: Class<Parser>): Class<Parser> =>
* this.tsParseModifiers(node, ["public"]); * this.tsParseModifiers(node, ["public"]);
* this.tsParseModifiers(node, ["abstract", "readonly"]); * this.tsParseModifiers(node, ["abstract", "readonly"]);
*/ */
tsParseModifiers<T: TsModifier>( tsParseModifiers(
modified: { [key: TsModifier]: ?true }, modified: {
allowedModifiers: T[], [key: TsModifier]: ?true,
accessibility?: N.Accessibility,
},
allowedModifiers: TsModifier[],
): void { ): void {
for (;;) { for (;;) {
const startPos = this.state.start; const startPos = this.state.start;
const modifier: ?T = this.tsParseModifier(allowedModifiers); const modifier: ?TsModifier = this.tsParseModifier(allowedModifiers);
if (!modifier) break; if (!modifier) break;
if (Object.hasOwnProperty.call(modified, modifier)) { if (tsIsAccessModifier(modifier)) {
this.raise(startPos, TSErrors.DuplicateModifier, 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;
} }
} }
@ -2120,10 +2136,12 @@ export default (superClass: Class<Parser>): Class<Parser> =>
member: any, member: any,
state: N.ParseClassMemberState, state: N.ParseClassMemberState,
): void { ): void {
this.tsParseModifiers(member, ["declare"]); this.tsParseModifiers(member, [
const accessibility = this.parseAccessModifier(); "declare",
if (accessibility) member.accessibility = accessibility; "private",
this.tsParseModifiers(member, ["declare"]); "public",
"protected",
]);
const callParseClassMember = () => { const callParseClassMember = () => {
super.parseClassMember(classBody, member, state); super.parseClassMember(classBody, member, state);

View File

@ -0,0 +1,7 @@
class Foo {
public public a;
private public b;
protected private c;
public protected d;
public protected private e;
}

View File

@ -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": []
}
}