diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 04801bbd75aa..08a9d0c15b56 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -1503,7 +1503,7 @@ export default class ExpressionParser extends LValParser { let arrowNode = this.startNodeAt(startPos, startLoc); if ( canBeArrow && - this.shouldParseArrow() && + this.shouldParseArrow(exprList) && (arrowNode = this.parseArrow(arrowNode)) ) { this.expressionScope.validateAsPattern(); @@ -1544,7 +1544,8 @@ export default class ExpressionParser extends LValParser { return parenExpression; } - shouldParseArrow(): boolean { + // eslint-disable-next-line no-unused-vars -- `params` is used in typescript plugin + shouldParseArrow(params: Array): boolean { return !this.canInsertSemicolon(); } diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index 4da763b144b4..c6a87facf308 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -60,7 +60,7 @@ export default class LValParser extends NodeUtils { - RestElement is not the last element - Missing `=` in assignment pattern - NOTE: There is a corresponding "isAssignable" method in flow.js. + NOTE: There is a corresponding "isAssignable" method. When this one is updated, please check if also that one needs to be updated. * @param {Node} node The expression atom @@ -100,6 +100,7 @@ export default class LValParser extends NodeUtils { case "ObjectPattern": case "ArrayPattern": case "AssignmentPattern": + case "RestElement": break; case "ObjectExpression": @@ -229,6 +230,50 @@ export default class LValParser extends NodeUtils { return exprList; } + isAssignable(node: Node, isBinding?: boolean): boolean { + switch (node.type) { + case "Identifier": + case "ObjectPattern": + case "ArrayPattern": + case "AssignmentPattern": + case "RestElement": + return true; + + case "ObjectExpression": { + const last = node.properties.length - 1; + return node.properties.every((prop, i) => { + return ( + prop.type !== "ObjectMethod" && + (i === last || prop.type !== "SpreadElement") && + this.isAssignable(prop) + ); + }); + } + + case "ObjectProperty": + return this.isAssignable(node.value); + + case "SpreadElement": + return this.isAssignable(node.argument); + + case "ArrayExpression": + return node.elements.every(element => this.isAssignable(element)); + + case "AssignmentExpression": + return node.operator === "="; + + case "ParenthesizedExpression": + return this.isAssignable(node.expression); + + case "MemberExpression": + case "OptionalMemberExpression": + return !isBinding; + + default: + return false; + } + } + // Convert list of expression atoms to a list of toReferencedList( diff --git a/packages/babel-parser/src/plugins/estree.js b/packages/babel-parser/src/plugins/estree.js index c4dad6e78f49..bbc0b789bb3b 100644 --- a/packages/babel-parser/src/plugins/estree.js +++ b/packages/babel-parser/src/plugins/estree.js @@ -330,6 +330,13 @@ export default (superClass: Class): Class => return (node: any); } + isAssignable(node: N.Node, isBinding?: boolean): boolean { + if (node != null && this.isObjectProperty(node)) { + return this.isAssignable(node.value, isBinding); + } + return super.isAssignable(node, isBinding); + } + toAssignable(node: N.Node, isLHS: boolean = false): N.Node { if (node != null && this.isObjectProperty(node)) { this.toAssignable(node.value, isLHS); diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index d27aaaa0777c..dbb95972673a 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -2257,46 +2257,10 @@ export default (superClass: Class): Class => } isAssignable(node: N.Node, isBinding?: boolean): boolean { - switch (node.type) { - case "Identifier": - case "ObjectPattern": - case "ArrayPattern": - case "AssignmentPattern": - return true; - - case "ObjectExpression": { - const last = node.properties.length - 1; - return node.properties.every((prop, i) => { - return ( - prop.type !== "ObjectMethod" && - (i === last || prop.type === "SpreadElement") && - this.isAssignable(prop) - ); - }); - } - - case "ObjectProperty": - return this.isAssignable(node.value); - - case "SpreadElement": - return this.isAssignable(node.argument); - - case "ArrayExpression": - return node.elements.every(element => this.isAssignable(element)); - - case "AssignmentExpression": - return node.operator === "="; - - case "ParenthesizedExpression": - case "TypeCastExpression": - return this.isAssignable(node.expression); - - case "MemberExpression": - case "OptionalMemberExpression": - return !isBinding; - - default: - return false; + if (node.type === "TypeCastExpression") { + return this.isAssignable(node.expression, isBinding); + } else { + return super.isAssignable(node, isBinding); } } @@ -2989,8 +2953,8 @@ export default (superClass: Class): Class => return super.parseArrow(node); } - shouldParseArrow(): boolean { - return this.match(tt.colon) || super.shouldParseArrow(); + shouldParseArrow(params: Array): boolean { + return this.match(tt.colon) || super.shouldParseArrow(params); } setArrowFunctionParameters( diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 99cc2b1deb0d..54788c355374 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -2877,6 +2877,17 @@ export default (superClass: Class): Class => return param; } + isAssignable(node: N.Node, isBinding?: boolean): boolean { + switch (node.type) { + case "TSTypeCastExpression": + return this.isAssignable(node.expression, isBinding); + case "TSParameterProperty": + return true; + default: + return super.isAssignable(node, isBinding); + } + } + toAssignable(node: N.Node, isLHS: boolean = false): N.Node { switch (node.type) { case "TSTypeCastExpression": @@ -3071,8 +3082,11 @@ export default (superClass: Class): Class => return node.expression; } - shouldParseArrow() { - return this.match(tt.colon) || super.shouldParseArrow(); + shouldParseArrow(params: Array) { + if (this.match(tt.colon)) { + return params.every(expr => this.isAssignable(expr, true)); + } + return super.shouldParseArrow(params); } shouldParseAsyncArrow(): boolean { diff --git a/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-1/input.ts b/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-1/input.ts new file mode 100644 index 000000000000..809d50b62da4 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-1/input.ts @@ -0,0 +1,2 @@ +// https://github.com/babel/babel/issues/11038 +0 ? v => (sum += v) : v => 0; diff --git a/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-1/output.json b/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-1/output.json new file mode 100644 index 000000000000..06f67e0aab1b --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-1/output.json @@ -0,0 +1,100 @@ +{ + "type": "File", + "start":0,"end":76,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":29}}, + "program": { + "type": "Program", + "start":0,"end":76,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":29}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":47,"end":76,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":29}}, + "leadingComments": [ + { + "type": "CommentLine", + "value": " https://github.com/babel/babel/issues/11038", + "start":0,"end":46,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":46}} + } + ], + "expression": { + "type": "ConditionalExpression", + "start":47,"end":75,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":28}}, + "test": { + "type": "NumericLiteral", + "start":47,"end":48,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":1}}, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start":51,"end":66,"loc":{"start":{"line":2,"column":4},"end":{"line":2,"column":19}}, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start":51,"end":52,"loc":{"start":{"line":2,"column":4},"end":{"line":2,"column":5},"identifierName":"v"}, + "name": "v" + } + ], + "body": { + "type": "AssignmentExpression", + "start":57,"end":65,"loc":{"start":{"line":2,"column":10},"end":{"line":2,"column":18}}, + "extra": { + "parenthesized": true, + "parenStart": 56 + }, + "operator": "+=", + "left": { + "type": "Identifier", + "start":57,"end":60,"loc":{"start":{"line":2,"column":10},"end":{"line":2,"column":13},"identifierName":"sum"}, + "name": "sum" + }, + "right": { + "type": "Identifier", + "start":64,"end":65,"loc":{"start":{"line":2,"column":17},"end":{"line":2,"column":18},"identifierName":"v"}, + "name": "v" + } + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start":69,"end":75,"loc":{"start":{"line":2,"column":22},"end":{"line":2,"column":28}}, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start":69,"end":70,"loc":{"start":{"line":2,"column":22},"end":{"line":2,"column":23},"identifierName":"v"}, + "name": "v" + } + ], + "body": { + "type": "NumericLiteral", + "start":74,"end":75,"loc":{"start":{"line":2,"column":27},"end":{"line":2,"column":28}}, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + } + } + } + } + ], + "directives": [] + }, + "comments": [ + { + "type": "CommentLine", + "value": " https://github.com/babel/babel/issues/11038", + "start":0,"end":46,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":46}} + } + ] +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-2/input.ts b/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-2/input.ts new file mode 100644 index 000000000000..831453024659 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-2/input.ts @@ -0,0 +1 @@ +0 ? v => (sum = v) : v => 0; diff --git a/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-2/options.json b/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-2/options.json new file mode 100644 index 000000000000..c5cf536a761e --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-2/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Unexpected token, expected \":\" (1:27)" +} diff --git a/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-3/input.ts b/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-3/input.ts new file mode 100644 index 000000000000..d7db2a7d604f --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-3/input.ts @@ -0,0 +1 @@ +0 ? v => (sum = v) : v => 0 : v => 0; diff --git a/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-3/output.json b/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-3/output.json new file mode 100644 index 000000000000..18ea2ae7c88e --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-3/output.json @@ -0,0 +1,112 @@ +{ + "type": "File", + "start":0,"end":37,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":37}}, + "program": { + "type": "Program", + "start":0,"end":37,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":37}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":37,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":37}}, + "expression": { + "type": "ConditionalExpression", + "start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":36}}, + "test": { + "type": "NumericLiteral", + "start":0,"end":1,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":1}}, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start":4,"end":27,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":27}}, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start":4,"end":5,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":5},"identifierName":"v"}, + "name": "v" + } + ], + "body": { + "type": "ArrowFunctionExpression", + "start":9,"end":27,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":27}}, + "returnType": { + "type": "TSTypeAnnotation", + "start":19,"end":22,"loc":{"start":{"line":1,"column":19},"end":{"line":1,"column":22}}, + "typeAnnotation": { + "type": "TSTypeReference", + "start":21,"end":22,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":22}}, + "typeName": { + "type": "Identifier", + "start":21,"end":22,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":22},"identifierName":"v"}, + "name": "v" + } + } + }, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "AssignmentPattern", + "start":10,"end":17,"loc":{"start":{"line":1,"column":10},"end":{"line":1,"column":17}}, + "left": { + "type": "Identifier", + "start":10,"end":13,"loc":{"start":{"line":1,"column":10},"end":{"line":1,"column":13},"identifierName":"sum"}, + "name": "sum" + }, + "right": { + "type": "Identifier", + "start":16,"end":17,"loc":{"start":{"line":1,"column":16},"end":{"line":1,"column":17},"identifierName":"v"}, + "name": "v" + } + } + ], + "body": { + "type": "NumericLiteral", + "start":26,"end":27,"loc":{"start":{"line":1,"column":26},"end":{"line":1,"column":27}}, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + } + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start":30,"end":36,"loc":{"start":{"line":1,"column":30},"end":{"line":1,"column":36}}, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start":30,"end":31,"loc":{"start":{"line":1,"column":30},"end":{"line":1,"column":31},"identifierName":"v"}, + "name": "v" + } + ], + "body": { + "type": "NumericLiteral", + "start":35,"end":36,"loc":{"start":{"line":1,"column":35},"end":{"line":1,"column":36}}, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + } + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-4/input.ts b/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-4/input.ts new file mode 100644 index 000000000000..7875a506d1be --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-4/input.ts @@ -0,0 +1 @@ +0 ? v => (...rest) : v => 0 : v => 0; diff --git a/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-4/output.json b/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-4/output.json new file mode 100644 index 000000000000..07004892c5e9 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-4/output.json @@ -0,0 +1,107 @@ +{ + "type": "File", + "start":0,"end":37,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":37}}, + "program": { + "type": "Program", + "start":0,"end":37,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":37}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":37,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":37}}, + "expression": { + "type": "ConditionalExpression", + "start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":36}}, + "test": { + "type": "NumericLiteral", + "start":0,"end":1,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":1}}, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start":4,"end":27,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":27}}, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start":4,"end":5,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":5},"identifierName":"v"}, + "name": "v" + } + ], + "body": { + "type": "ArrowFunctionExpression", + "start":9,"end":27,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":27}}, + "returnType": { + "type": "TSTypeAnnotation", + "start":19,"end":22,"loc":{"start":{"line":1,"column":19},"end":{"line":1,"column":22}}, + "typeAnnotation": { + "type": "TSTypeReference", + "start":21,"end":22,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":22}}, + "typeName": { + "type": "Identifier", + "start":21,"end":22,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":22},"identifierName":"v"}, + "name": "v" + } + } + }, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "RestElement", + "start":10,"end":17,"loc":{"start":{"line":1,"column":10},"end":{"line":1,"column":17}}, + "argument": { + "type": "Identifier", + "start":13,"end":17,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":17},"identifierName":"rest"}, + "name": "rest" + } + } + ], + "body": { + "type": "NumericLiteral", + "start":26,"end":27,"loc":{"start":{"line":1,"column":26},"end":{"line":1,"column":27}}, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + } + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start":30,"end":36,"loc":{"start":{"line":1,"column":30},"end":{"line":1,"column":36}}, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start":30,"end":31,"loc":{"start":{"line":1,"column":30},"end":{"line":1,"column":31},"identifierName":"v"}, + "name": "v" + } + ], + "body": { + "type": "NumericLiteral", + "start":35,"end":36,"loc":{"start":{"line":1,"column":35},"end":{"line":1,"column":36}}, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + } + } + } + } + ], + "directives": [] + } +} \ No newline at end of file