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

Disallow #a in #b in c and similar expressions #13727

Merged
merged 6 commits into from Sep 4, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
62 changes: 47 additions & 15 deletions packages/babel-parser/src/parser/expression.js
Expand Up @@ -364,14 +364,22 @@ export default class ExpressionParser extends LValParser {
return expr;
}

parseMaybeUnaryOrPrivate(
refExpressionErrors?: ExpressionErrors,
): N.Expression | N.PrivateName {
return this.match(tt.privateName)
? this.parsePrivateName()
: this.parseMaybeUnary(refExpressionErrors);
}

// Start the precedence parser.
// https://tc39.es/ecma262/#prod-ShortCircuitExpression

parseExprOps(refExpressionErrors: ExpressionErrors): N.Expression {
const startPos = this.state.start;
const startLoc = this.state.startLoc;
const potentialArrowAt = this.state.potentialArrowAt;
const expr = this.parseMaybeUnary(refExpressionErrors);
const expr = this.parseMaybeUnaryOrPrivate(refExpressionErrors);

if (this.shouldExitDescending(expr, potentialArrowAt)) {
return expr;
Expand All @@ -387,11 +395,34 @@ export default class ExpressionParser extends LValParser {
// operator that has a lower precedence than the set it is parsing.

parseExprOp(
left: N.Expression,
left: N.Expression | N.PrivateName,
leftStartPos: number,
leftStartLoc: Position,
minPrec: number,
): N.Expression {
if (this.isPrivateName(left)) {
JLHwung marked this conversation as resolved.
Show resolved Hide resolved
// https://tc39.es/proposal-private-fields-in-in
// RelationalExpression [In, Yield, Await]
// [+In] PrivateIdentifier in ShiftExpression[?Yield, ?Await]

const value = this.getPrivateNameSV(left);
const { start } = left;

if (
minPrec >= tt._in.binop ||
!this.prodParam.hasIn ||
!this.match(tt._in)
) {
this.raise(
left.start,
Errors.PrivateInExpectedIn,
this.getPrivateNameSV(left),
);
nicolo-ribaudo marked this conversation as resolved.
Show resolved Hide resolved
}

this.classScope.usePrivateName(value, start);
}

let prec = this.state.type.binop;
if (prec != null && (this.prodParam.hasIn || !this.match(tt._in))) {
if (prec > minPrec) {
Expand Down Expand Up @@ -504,7 +535,7 @@ export default class ExpressionParser extends LValParser {
const startLoc = this.state.startLoc;

return this.parseExprOp(
this.parseMaybeUnary(),
this.parseMaybeUnaryOrPrivate(),

This comment was marked as resolved.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

startPos,
startLoc,
op.rightAssociative ? prec - 1 : prec,
Expand Down Expand Up @@ -1213,17 +1244,18 @@ export default class ExpressionParser extends LValParser {
}

case tt.privateName: {
// https://tc39.es/proposal-private-fields-in-in
// RelationalExpression [In, Yield, Await]
// [+In] PrivateIdentifier in ShiftExpression[?Yield, ?Await]
const { value, start } = this.state;
node = this.parsePrivateName();
if (this.match(tt._in)) {
this.classScope.usePrivateName(value, start);
} else {
this.raise(start, Errors.PrivateInExpectedIn, value);
}
return node;
// Standalone private names are only allowed in "#x in obj"
// expressions, and they are directly handled by callers of
// parseExprOp. If we reach this, the input is always invalid.
// We can throw a better error message and recover, rather than
// just throwing "Unexpected token" (which is the default
// behavior of this big switch statement).
this.raise(
this.state.start,
Errors.PrivateInExpectedIn,
this.state.value,
);
return this.parsePrivateName();
}

case tt.moduloAssign:
Expand Down Expand Up @@ -2904,7 +2936,7 @@ export default class ExpressionParser extends LValParser {
this.state.inFSharpPipelineDirectBody = true;

const ret = this.parseExprOp(
this.parseMaybeUnary(),
this.parseMaybeUnaryOrPrivate(),
startPos,
startLoc,
prec,
Expand Down
@@ -1,3 +1,3 @@
{
"throws": "Unexpected token (3:9)"
"throws": "Unexpected token (3:6)"
}
@@ -0,0 +1,7 @@
class Foo {
#a;
#b;
method() {
#a in #b in c
}
}
@@ -0,0 +1,122 @@
{
"type": "File",
"start":0,"end":60,"loc":{"start":{"line":1,"column":0},"end":{"line":7,"column":1}},
"errors": [
"SyntaxError: Private names are only allowed in property accesses (`obj.#b`) or in `in` expressions (`#b in obj`). (5:10)"
],
"program": {
"type": "Program",
"start":0,"end":60,"loc":{"start":{"line":1,"column":0},"end":{"line":7,"column":1}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":60,"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":60,"loc":{"start":{"line":1,"column":10},"end":{"line":7,"column":1}},
"body": [
{
"type": "ClassPrivateProperty",
"start":14,"end":17,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":5}},
"static": false,
"key": {
"type": "PrivateName",
"start":14,"end":16,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":4}},
"id": {
"type": "Identifier",
"start":15,"end":16,"loc":{"start":{"line":2,"column":3},"end":{"line":2,"column":4},"identifierName":"a"},
"name": "a"
}
},
"value": null
},
{
"type": "ClassPrivateProperty",
"start":20,"end":23,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":5}},
"static": false,
"key": {
"type": "PrivateName",
"start":20,"end":22,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":4}},
"id": {
"type": "Identifier",
"start":21,"end":22,"loc":{"start":{"line":3,"column":3},"end":{"line":3,"column":4},"identifierName":"b"},
"name": "b"
}
},
"value": null
},
{
"type": "ClassMethod",
"start":26,"end":58,"loc":{"start":{"line":4,"column":2},"end":{"line":6,"column":3}},
"static": false,
"key": {
"type": "Identifier",
"start":26,"end":32,"loc":{"start":{"line":4,"column":2},"end":{"line":4,"column":8},"identifierName":"method"},
"name": "method"
},
"computed": false,
"kind": "method",
"id": null,
"generator": false,
"async": false,
"params": [],
"body": {
"type": "BlockStatement",
"start":35,"end":58,"loc":{"start":{"line":4,"column":11},"end":{"line":6,"column":3}},
"body": [
{
"type": "ExpressionStatement",
"start":41,"end":54,"loc":{"start":{"line":5,"column":4},"end":{"line":5,"column":17}},
"expression": {
"type": "BinaryExpression",
"start":41,"end":54,"loc":{"start":{"line":5,"column":4},"end":{"line":5,"column":17}},
"left": {
"type": "BinaryExpression",
"start":41,"end":49,"loc":{"start":{"line":5,"column":4},"end":{"line":5,"column":12}},
"left": {
"type": "PrivateName",
"start":41,"end":43,"loc":{"start":{"line":5,"column":4},"end":{"line":5,"column":6}},
"id": {
"type": "Identifier",
"start":42,"end":43,"loc":{"start":{"line":5,"column":5},"end":{"line":5,"column":6},"identifierName":"a"},
"name": "a"
}
},
"operator": "in",
"right": {
"type": "PrivateName",
"start":47,"end":49,"loc":{"start":{"line":5,"column":10},"end":{"line":5,"column":12}},
"id": {
"type": "Identifier",
"start":48,"end":49,"loc":{"start":{"line":5,"column":11},"end":{"line":5,"column":12},"identifierName":"b"},
"name": "b"
}
}
},
"operator": "in",
"right": {
"type": "Identifier",
"start":53,"end":54,"loc":{"start":{"line":5,"column":16},"end":{"line":5,"column":17},"identifierName":"c"},
"name": "c"
}
}
}
],
"directives": []
}
}
]
}
}
],
"directives": []
}
}
@@ -0,0 +1,6 @@
class Foo {
#a;
method() {
1 + #a in b
}
}
@@ -0,0 +1,107 @@
{
"type": "File",
"start":0,"end":52,"loc":{"start":{"line":1,"column":0},"end":{"line":6,"column":1}},
"errors": [
"SyntaxError: Private names are only allowed in property accesses (`obj.#a`) or in `in` expressions (`#a in obj`). (4:8)"
],
"program": {
"type": "Program",
"start":0,"end":52,"loc":{"start":{"line":1,"column":0},"end":{"line":6,"column":1}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":52,"loc":{"start":{"line":1,"column":0},"end":{"line":6,"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":52,"loc":{"start":{"line":1,"column":10},"end":{"line":6,"column":1}},
"body": [
{
"type": "ClassPrivateProperty",
"start":14,"end":17,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":5}},
"static": false,
"key": {
"type": "PrivateName",
"start":14,"end":16,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":4}},
"id": {
"type": "Identifier",
"start":15,"end":16,"loc":{"start":{"line":2,"column":3},"end":{"line":2,"column":4},"identifierName":"a"},
"name": "a"
}
},
"value": null
},
{
"type": "ClassMethod",
"start":20,"end":50,"loc":{"start":{"line":3,"column":2},"end":{"line":5,"column":3}},
"static": false,
"key": {
"type": "Identifier",
"start":20,"end":26,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":8},"identifierName":"method"},
"name": "method"
},
"computed": false,
"kind": "method",
"id": null,
"generator": false,
"async": false,
"params": [],
"body": {
"type": "BlockStatement",
"start":29,"end":50,"loc":{"start":{"line":3,"column":11},"end":{"line":5,"column":3}},
"body": [
{
"type": "ExpressionStatement",
"start":35,"end":46,"loc":{"start":{"line":4,"column":4},"end":{"line":4,"column":15}},
"expression": {
"type": "BinaryExpression",
"start":35,"end":46,"loc":{"start":{"line":4,"column":4},"end":{"line":4,"column":15}},
"left": {
"type": "BinaryExpression",
"start":35,"end":41,"loc":{"start":{"line":4,"column":4},"end":{"line":4,"column":10}},
"left": {
"type": "NumericLiteral",
"start":35,"end":36,"loc":{"start":{"line":4,"column":4},"end":{"line":4,"column":5}},
"extra": {
"rawValue": 1,
"raw": "1"
},
"value": 1
},
"operator": "+",
"right": {
"type": "PrivateName",
"start":39,"end":41,"loc":{"start":{"line":4,"column":8},"end":{"line":4,"column":10}},
"id": {
"type": "Identifier",
"start":40,"end":41,"loc":{"start":{"line":4,"column":9},"end":{"line":4,"column":10},"identifierName":"a"},
"name": "a"
}
}
},
"operator": "in",
"right": {
"type": "Identifier",
"start":45,"end":46,"loc":{"start":{"line":4,"column":14},"end":{"line":4,"column":15},"identifierName":"b"},
"name": "b"
}
}
}
],
"directives": []
}
}
]
}
}
],
"directives": []
}
}
@@ -0,0 +1,7 @@
class Foo {
#a;

method() {
for (var x = #a in y);
}
}