Skip to content

Commit

Permalink
Disallow #a in #b in c and similar expressions (#13727)
Browse files Browse the repository at this point in the history
* Disallow `#a in #b in c` and similar expressions

* Update packages/babel-parser/src/parser/expression.js

Co-authored-by: Hu谩ng J霉nli脿ng <jlhwung@gmail.com>

* Add test for `await #x in`

* Flow

* Update flow allowlist

* Flow

Co-authored-by: Hu谩ng J霉nli脿ng <jlhwung@gmail.com>
  • Loading branch information
nicolo-ribaudo and JLHwung committed Sep 4, 2021
1 parent 5a361fe commit 44388e6
Show file tree
Hide file tree
Showing 11 changed files with 505 additions and 18 deletions.
59 changes: 44 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,31 @@ 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)) {
// 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 (
// TODO: When migrating to TS, use tt._in.binop!
minPrec >= ((tt._in.binop: any): number) ||
!this.prodParam.hasIn ||
!this.match(tt._in)
) {
this.raise(start, Errors.PrivateInExpectedIn, value);
}

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 +532,7 @@ export default class ExpressionParser extends LValParser {
const startLoc = this.state.startLoc;

return this.parseExprOp(
this.parseMaybeUnary(),
this.parseMaybeUnaryOrPrivate(),
startPos,
startLoc,
op.rightAssociative ? prec - 1 : prec,
Expand Down Expand Up @@ -1213,17 +1241,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 +2933,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);
}
}

0 comments on commit 44388e6

Please sign in to comment.