Skip to content

Commit

Permalink
fix: disallow all parenthesized pattern except parsing LHS
Browse files Browse the repository at this point in the history
  • Loading branch information
JLHwung committed Nov 7, 2020
1 parent 0641a15 commit f4faefc
Show file tree
Hide file tree
Showing 13 changed files with 323 additions and 42 deletions.
16 changes: 2 additions & 14 deletions packages/babel-parser/src/parser/expression.js
Expand Up @@ -278,7 +278,7 @@ export default class ExpressionParser extends LValParser {
node.operator = operator;

if (this.match(tt.eq)) {
node.left = this.toAssignable(left);
node.left = this.toAssignable(left, /* isLHS */ true);
refExpressionErrors.doubleProto = -1; // reset because double __proto__ is valid in assignment expression
} else {
node.left = left;
Expand Down Expand Up @@ -842,7 +842,6 @@ export default class ExpressionParser extends LValParser {
nodeForExtra?: ?N.Node,
): $ReadOnlyArray<?N.Expression> {
const elts = [];
let innerParenStart;
let first = true;
const oldInFSharpPipelineDirectBody = this.state.inFSharpPipelineDirectBody;
this.state.inFSharpPipelineDirectBody = false;
Expand Down Expand Up @@ -875,12 +874,6 @@ export default class ExpressionParser extends LValParser {
}
}

// we need to make sure that if this is an async arrow functions,
// that we don't allow inner parens inside the params
if (this.match(tt.parenL) && !innerParenStart) {
innerParenStart = this.state.start;
}

elts.push(
this.parseExprListItem(
false,
Expand All @@ -891,11 +884,6 @@ export default class ExpressionParser extends LValParser {
);
}

// we found an async arrow function so let's not allow any inner parens
if (possibleAsyncArrow && innerParenStart && this.shouldParseAsyncArrow()) {
this.unexpected();
}

this.state.inFSharpPipelineDirectBody = oldInFSharpPipelineDirectBody;

return elts;
Expand Down Expand Up @@ -2056,7 +2044,7 @@ export default class ExpressionParser extends LValParser {
params: N.Expression[],
trailingCommaPos: ?number,
): void {
node.params = this.toAssignableList(params, trailingCommaPos);
node.params = this.toAssignableList(params, trailingCommaPos, false);
}

parseFunctionBodyAndFinish(
Expand Down
53 changes: 36 additions & 17 deletions packages/babel-parser/src/parser/lval.js
Expand Up @@ -51,18 +51,32 @@ export default class LValParser extends NodeUtils {
+parseDecorator: () => Decorator;
*/

// Convert existing expression atom to assignable pattern
// if possible.
// NOTE: There is a corresponding "isAssignable" method in flow.js.
// When this one is updated, please check if also that one needs to be updated.
/**
* Convert existing expression atom to assignable pattern
* if possible. Also checks invalid destructuring targets:
toAssignable(node: Node): Node {
- Parenthesized Destructuring patterns
- RestElement is not the last element
- Missing `=` in assignment pattern
NOTE: There is a corresponding "isAssignable" method in flow.js.
When this one is updated, please check if also that one needs to be updated.
* @param {Node} node The expression atom
* @param {boolean} [isLHS=false] Whether we are parsing a LeftHandSideExpression. If isLHS is `true`, the following cases are allowed:
`[(a)] = [0]`, `[(a.b)] = [0]`
* @returns {Node} The converted assignable pattern
* @memberof LValParser
*/
toAssignable(node: Node, isLHS: boolean = false): Node {
let parenthesized = undefined;
if (node.type === "ParenthesizedExpression" || node.extra?.parenthesized) {
parenthesized = unwrapParenthesizedExpression(node);
if (
parenthesized.type !== "Identifier" &&
parenthesized.type !== "MemberExpression"
!isLHS ||
(parenthesized.type !== "Identifier" &&
parenthesized.type !== "MemberExpression")
) {
this.raise(node.start, Errors.InvalidParenthesizedAssignment);
}
Expand All @@ -84,7 +98,7 @@ export default class LValParser extends NodeUtils {
) {
const prop = node.properties[i];
const isLast = i === last;
this.toAssignableObjectExpressionProp(prop, isLast);
this.toAssignableObjectExpressionProp(prop, isLast, isLHS);

if (
isLast &&
Expand All @@ -97,21 +111,21 @@ export default class LValParser extends NodeUtils {
break;

case "ObjectProperty":
this.toAssignable(node.value);
this.toAssignable(node.value, isLHS);
break;

case "SpreadElement": {
this.checkToRestConversion(node);

node.type = "RestElement";
const arg = node.argument;
this.toAssignable(arg);
this.toAssignable(arg, isLHS);
break;
}

case "ArrayExpression":
node.type = "ArrayPattern";
this.toAssignableList(node.elements, node.extra?.trailingComma);
this.toAssignableList(node.elements, node.extra?.trailingComma, isLHS);
break;

case "AssignmentExpression":
Expand All @@ -121,11 +135,11 @@ export default class LValParser extends NodeUtils {

node.type = "AssignmentPattern";
delete node.operator;
this.toAssignable(node.left);
this.toAssignable(node.left, isLHS);
break;

case "ParenthesizedExpression":
this.toAssignable(((parenthesized: any): Expression));
this.toAssignable(((parenthesized: any): Expression), isLHS);
break;

default:
Expand All @@ -135,7 +149,11 @@ export default class LValParser extends NodeUtils {
return node;
}

toAssignableObjectExpressionProp(prop: Node, isLast: boolean) {
toAssignableObjectExpressionProp(
prop: Node,
isLast: boolean,
isLHS: boolean,
) {
if (prop.type === "ObjectMethod") {
const error =
prop.kind === "get" || prop.kind === "set"
Expand All @@ -148,7 +166,7 @@ export default class LValParser extends NodeUtils {
} else if (prop.type === "SpreadElement" && !isLast) {
this.raiseRestNotLast(prop.start);
} else {
this.toAssignable(prop);
this.toAssignable(prop, isLHS);
}
}

Expand All @@ -157,6 +175,7 @@ export default class LValParser extends NodeUtils {
toAssignableList(
exprList: Expression[],
trailingCommaPos?: ?number,
isLHS: boolean,
): $ReadOnlyArray<Pattern> {
let end = exprList.length;
if (end) {
Expand All @@ -166,7 +185,7 @@ export default class LValParser extends NodeUtils {
} else if (last?.type === "SpreadElement") {
last.type = "RestElement";
const arg = last.argument;
this.toAssignable(arg);
this.toAssignable(arg, isLHS);
if (
arg.type !== "Identifier" &&
arg.type !== "MemberExpression" &&
Expand All @@ -186,7 +205,7 @@ export default class LValParser extends NodeUtils {
for (let i = 0; i < end; i++) {
const elt = exprList[i];
if (elt) {
this.toAssignable(elt);
this.toAssignable(elt, isLHS);
if (elt.type === "RestElement") {
this.raiseRestNotLast(elt.start);
}
Expand Down
12 changes: 8 additions & 4 deletions packages/babel-parser/src/plugins/estree.js
Expand Up @@ -341,23 +341,27 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return (node: any);
}

toAssignable(node: N.Node): N.Node {
toAssignable(node: N.Node, isLHS: boolean = false): N.Node {
if (isSimpleProperty(node)) {
this.toAssignable(node.value);

return node;
}

return super.toAssignable(node);
return super.toAssignable(node, isLHS);
}

toAssignableObjectExpressionProp(prop: N.Node, isLast: boolean) {
toAssignableObjectExpressionProp(
prop: N.Node,
isLast: boolean,
isLHS: boolean,
) {
if (prop.kind === "get" || prop.kind === "set") {
throw this.raise(prop.key.start, Errors.PatternHasAccessor);
} else if (prop.method) {
throw this.raise(prop.key.start, Errors.PatternHasMethod);
} else {
super.toAssignableObjectExpressionProp(prop, isLast);
super.toAssignableObjectExpressionProp(prop, isLast, isLHS);
}
}

Expand Down
9 changes: 5 additions & 4 deletions packages/babel-parser/src/plugins/flow.js
Expand Up @@ -2192,26 +2192,27 @@ export default (superClass: Class<Parser>): Class<Parser> =>
}
}

toAssignable(node: N.Node): N.Node {
toAssignable(node: N.Node, isLHS: boolean = false): N.Node {
if (node.type === "TypeCastExpression") {
return super.toAssignable(this.typeCastToParameter(node));
return super.toAssignable(this.typeCastToParameter(node), isLHS);
} else {
return super.toAssignable(node);
return super.toAssignable(node, isLHS);
}
}

// turn type casts that we found in function parameter head into type annotated params
toAssignableList(
exprList: N.Expression[],
trailingCommaPos?: ?number,
isLHS: boolean = true,
): $ReadOnlyArray<N.Pattern> {
for (let i = 0; i < exprList.length; i++) {
const expr = exprList[i];
if (expr?.type === "TypeCastExpression") {
exprList[i] = this.typeCastToParameter(expr);
}
}
return super.toAssignableList(exprList, trailingCommaPos);
return super.toAssignableList(exprList, trailingCommaPos, isLHS);
}

// this is a list of nodes, from something like a call expression, we need to filter the
Expand Down
@@ -0,0 +1 @@
([(a)]) => {}
@@ -0,0 +1,50 @@
{
"type": "File",
"start":0,"end":13,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":13}},
"errors": [
"SyntaxError: Invalid parenthesized assignment pattern (1:3)"
],
"program": {
"type": "Program",
"start":0,"end":13,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":13}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ExpressionStatement",
"start":0,"end":13,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":13}},
"expression": {
"type": "ArrowFunctionExpression",
"start":0,"end":13,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":13}},
"id": null,
"generator": false,
"async": false,
"params": [
{
"type": "ArrayPattern",
"start":1,"end":6,"loc":{"start":{"line":1,"column":1},"end":{"line":1,"column":6}},
"elements": [
{
"type": "Identifier",
"start":3,"end":4,"loc":{"start":{"line":1,"column":3},"end":{"line":1,"column":4},"identifierName":"a"},
"extra": {
"parenthesized": true,
"parenStart": 2
},
"name": "a"
}
]
}
],
"body": {
"type": "BlockStatement",
"start":11,"end":13,"loc":{"start":{"line":1,"column":11},"end":{"line":1,"column":13}},
"body": [],
"directives": []
}
}
}
],
"directives": []
}
}
@@ -0,0 +1 @@
(a = function (a) { [(a)] = [0] }) => {}

0 comments on commit f4faefc

Please sign in to comment.