Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 3 commits into from Feb 9, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
50 changes: 36 additions & 14 deletions packages/babel-parser/src/plugins/typescript/index.js
Expand Up @@ -31,14 +31,13 @@ import type { ExpressionErrors } from "../../parser/util";
import { PARAM } from "../../util/production-parameter";
import { Errors } from "../../parser/error";

type AccessibilityModifier = "public" | "private" | "protected";
existentialism marked this conversation as resolved.
Show resolved Hide resolved
type TsModifier =
| "readonly"
| "abstract"
| "declare"
| "static"
| "public"
| "private"
| "protected";
| AccessibilityModifier;

function nonNull<T>(x: ?T): T {
if (x == null) {
Expand Down Expand Up @@ -71,6 +70,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.",
Expand Down Expand Up @@ -194,20 +194,32 @@ export default (superClass: Class<Parser>): Class<Parser> =>
* this.tsParseModifiers(node, ["public"]);
* this.tsParseModifiers(node, ["abstract", "readonly"]);
*/
tsParseModifiers<T: TsModifier>(
modified: { [key: TsModifier]: ?true },
allowedModifiers: T[],
tsParseModifiers(
modified: {
[key: TsModifier]: ?true,
accessibility?: AccessibilityModifier,
},
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 (this.tsIsAccessModifier(modifier)) {
if (modified.accessibility) {
this.raise(startPos, TSErrors.DuplicateAccessibilityModifier);
} else {
// $FlowIgnore
existentialism marked this conversation as resolved.
Show resolved Hide resolved
modified.accessibility = modifier;
}
} else {
if (Object.hasOwnProperty.call(modified, modifier)) {
this.raise(startPos, TSErrors.DuplicateModifier, modifier);
}
modified[modifier] = true;
}
modified[modifier] = true;
}
}

Expand Down Expand Up @@ -2125,10 +2137,12 @@ export default (superClass: Class<Parser>): Class<Parser> =>
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);
Expand Down Expand Up @@ -2849,4 +2863,12 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.state.inAbstractClass = oldInAbstractClass;
}
}

tsIsAccessModifier(modifier: string): boolean {
return (
modifier === "private" ||
modifier === "public" ||
modifier === "protected"
);
existentialism marked this conversation as resolved.
Show resolved Hide resolved
}
};
@@ -0,0 +1,7 @@
class Foo {
public public a;
private public b;
protected private c;
public protected d;
public protected private e;
}
@@ -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": []
}
}