diff --git a/packages/babel-generator/test/fixtures/comments/create-parenthesized-expressions/input.js b/packages/babel-generator/test/fixtures/comments/create-parenthesized-expressions/input.js new file mode 100644 index 0000000000..753a9cfbb2 --- /dev/null +++ b/packages/babel-generator/test/fixtures/comments/create-parenthesized-expressions/input.js @@ -0,0 +1,12 @@ +// One +(1); + +/* Two */ +(2); + +( + // Three + 3 +); + +(/* Four */ 4); diff --git a/packages/babel-generator/test/fixtures/comments/create-parenthesized-expressions/options.json b/packages/babel-generator/test/fixtures/comments/create-parenthesized-expressions/options.json new file mode 100644 index 0000000000..7c49b91ec9 --- /dev/null +++ b/packages/babel-generator/test/fixtures/comments/create-parenthesized-expressions/options.json @@ -0,0 +1,3 @@ +{ + "parserOpts": {"createParenthesizedExpressions": true} +} diff --git a/packages/babel-generator/test/fixtures/comments/create-parenthesized-expressions/output.js b/packages/babel-generator/test/fixtures/comments/create-parenthesized-expressions/output.js new file mode 100644 index 0000000000..b78500e801 --- /dev/null +++ b/packages/babel-generator/test/fixtures/comments/create-parenthesized-expressions/output.js @@ -0,0 +1,10 @@ +// One +(1); +/* Two */ + +(2); +( // Three +3); +( +/* Four */ +4); \ No newline at end of file diff --git a/packages/babel-generator/test/index.js b/packages/babel-generator/test/index.js index 0574528850..4781339b28 100644 --- a/packages/babel-generator/test/index.js +++ b/packages/babel-generator/test/index.js @@ -492,6 +492,7 @@ suites.forEach(function(testSuite) { strictMode: task.options.strictMode === false ? false : true, sourceType: "module", sourceMaps: !!task.sourceMap, + ...task.options.parserOpts, }); const options = { sourceFileName: path.relative(__dirname, actual.loc), diff --git a/packages/babel-parser/ast/spec.md b/packages/babel-parser/ast/spec.md index b4f5372cb4..34c04919cb 100644 --- a/packages/babel-parser/ast/spec.md +++ b/packages/babel-parser/ast/spec.md @@ -79,6 +79,7 @@ These are the core @babel/parser (babylon) AST node types. - [CallExpression](#callexpression) - [NewExpression](#newexpression) - [SequenceExpression](#sequenceexpression) + - [ParenthesizedExpression](#parenthesizedexpression) - [DoExpression](#doexpression) - [Template Literals](#template-literals) - [TemplateLiteral](#templateliteral) @@ -945,6 +946,17 @@ interface SequenceExpression <: Expression { A sequence expression, i.e., a comma-separated sequence of expressions. +## ParenthesizedExpression + +```js +interface ParenthesizedExpression <: Expression { + type "ParenthesizedExpression"; + expression: Expression; +} +``` + +An expression wrapped by parentheses. + ## DoExpression ```js diff --git a/packages/babel-parser/src/options.js b/packages/babel-parser/src/options.js index c11dd55cca..5a615ccddf 100755 --- a/packages/babel-parser/src/options.js +++ b/packages/babel-parser/src/options.js @@ -19,6 +19,7 @@ export type Options = { strictMode: ?boolean, ranges: boolean, tokens: boolean, + createParenthesizedExpressions: boolean, }; export const defaultOptions: Options = { @@ -55,6 +56,9 @@ export const defaultOptions: Options = { ranges: false, // Adds all parsed tokens to a `tokens` property on the `File` node tokens: false, + // Whether to create ParenthesizedExpression AST nodes (if false + // the parser sets extra.parenthesized on the expression nodes instead). + createParenthesizedExpressions: false, }; // Interpret and default an options object diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 1483f90612..71ce823720 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -40,6 +40,12 @@ import { SCOPE_PROGRAM, } from "../util/scopeflags"; +const unwrapParenthesizedExpression = node => { + return node.type === "ParenthesizedExpression" + ? unwrapParenthesizedExpression(node.expression) + : node; +}; + export default class ExpressionParser extends LValParser { // Forward-declaration: defined in statement.js +parseBlock: ( @@ -204,16 +210,22 @@ export default class ExpressionParser extends LValParser { this.checkLVal(left, undefined, undefined, "assignment expression"); + const maybePattern = unwrapParenthesizedExpression(left); + let patternErrorMsg; - if (left.type === "ObjectPattern") { + if (maybePattern.type === "ObjectPattern") { patternErrorMsg = "`({a}) = 0` use `({a} = 0)`"; - } else if (left.type === "ArrayPattern") { + } else if (maybePattern.type === "ArrayPattern") { patternErrorMsg = "`([a]) = 0` use `([a] = 0)`"; } - if (patternErrorMsg && left.extra && left.extra.parenthesized) { + if ( + patternErrorMsg && + ((left.extra && left.extra.parenthesized) || + left.type === "ParenthesizedExpression") + ) { this.raise( - left.start, + maybePattern.start, `You're trying to assign to a parenthesized expression, eg. instead of ${patternErrorMsg}`, ); } @@ -326,7 +338,8 @@ export default class ExpressionParser extends LValParser { if ( operator === "**" && left.type === "UnaryExpression" && - !(left.extra && left.extra.parenthesized) + (this.options.createParenthesizedExpressions || + !(left.extra && left.extra.parenthesized)) ) { this.raise( left.argument.start, @@ -1161,13 +1174,6 @@ export default class ExpressionParser extends LValParser { return this.finishNode(node, type); } - parseParenExpression(): N.Expression { - this.expect(tt.parenL); - const val = this.parseExpression(); - this.expect(tt.parenR); - return val; - } - parseParenAndDistinguishExpression(canBeArrow: boolean): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; @@ -1278,10 +1284,16 @@ export default class ExpressionParser extends LValParser { val = exprList[0]; } - this.addExtra(val, "parenthesized", true); - this.addExtra(val, "parenStart", startPos); + if (!this.options.createParenthesizedExpressions) { + this.addExtra(val, "parenthesized", true); + this.addExtra(val, "parenStart", startPos); + return val; + } - return val; + const parenExpression = this.startNodeAt(startPos, startLoc); + parenExpression.expression = val; + this.finishNode(parenExpression, "ParenthesizedExpression"); + return parenExpression; } shouldParseArrow(): boolean { diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index dd02c8c974..6db5558a8f 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -93,6 +93,14 @@ export default class LValParser extends NodeUtils { } break; + case "ParenthesizedExpression": + node.expression = this.toAssignable( + node.expression, + isBinding, + contextDescription, + ); + break; + case "MemberExpression": if (!isBinding) break; @@ -413,6 +421,15 @@ export default class LValParser extends NodeUtils { ); break; + case "ParenthesizedExpression": + this.checkLVal( + expr.expression, + bindingType, + checkClashes, + "parenthesized expression", + ); + break; + default: { const message = (bindingType === BIND_NONE diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index d97d4986a0..dd46a44df3 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -453,6 +453,13 @@ export default class StatementParser extends ExpressionParser { return this.finishNode(node, "DebuggerStatement"); } + parseHeaderExpression(): N.Expression { + this.expect(tt.parenL); + const val = this.parseExpression(); + this.expect(tt.parenR); + return val; + } + parseDoStatement(node: N.DoWhileStatement): N.DoWhileStatement { this.next(); this.state.labels.push(loopLabel); @@ -469,7 +476,7 @@ export default class StatementParser extends ExpressionParser { this.state.labels.pop(); this.expect(tt._while); - node.test = this.parseParenExpression(); + node.test = this.parseHeaderExpression(); this.eat(tt.semi); return this.finishNode(node, "DoWhileStatement"); } @@ -567,7 +574,7 @@ export default class StatementParser extends ExpressionParser { parseIfStatement(node: N.IfStatement): N.IfStatement { this.next(); - node.test = this.parseParenExpression(); + node.test = this.parseHeaderExpression(); node.consequent = this.parseStatement("if"); node.alternate = this.eat(tt._else) ? this.parseStatement("if") : null; return this.finishNode(node, "IfStatement"); @@ -596,7 +603,7 @@ export default class StatementParser extends ExpressionParser { parseSwitchStatement(node: N.SwitchStatement): N.SwitchStatement { this.next(); - node.discriminant = this.parseParenExpression(); + node.discriminant = this.parseHeaderExpression(); const cases = (node.cases = []); this.expect(tt.braceL); this.state.labels.push(switchLabel); @@ -715,7 +722,7 @@ export default class StatementParser extends ExpressionParser { parseWhileStatement(node: N.WhileStatement): N.WhileStatement { this.next(); - node.test = this.parseParenExpression(); + node.test = this.parseHeaderExpression(); this.state.labels.push(loopLabel); node.body = @@ -737,7 +744,7 @@ export default class StatementParser extends ExpressionParser { this.raise(this.state.start, "'with' in strict mode"); } this.next(); - node.object = this.parseParenExpression(); + node.object = this.parseHeaderExpression(); node.body = // For the smartPipelines plugin: diff --git a/packages/babel-parser/src/types.js b/packages/babel-parser/src/types.js index e3a584ddc3..cc1fd01f30 100644 --- a/packages/babel-parser/src/types.js +++ b/packages/babel-parser/src/types.js @@ -574,6 +574,11 @@ export type SequenceExpression = NodeBase & { expressions: $ReadOnlyArray, }; +export type ParenthesizedExpression = NodeBase & { + type: "ParenthesizedExpression", + expression: Expression, +}; + // Pipelines export type PipelineBody = NodeBase & { diff --git a/packages/babel-parser/test/fixtures/comments/basic/create-parenthesized-expressions/input.js b/packages/babel-parser/test/fixtures/comments/basic/create-parenthesized-expressions/input.js new file mode 100644 index 0000000000..753a9cfbb2 --- /dev/null +++ b/packages/babel-parser/test/fixtures/comments/basic/create-parenthesized-expressions/input.js @@ -0,0 +1,12 @@ +// One +(1); + +/* Two */ +(2); + +( + // Three + 3 +); + +(/* Four */ 4); diff --git a/packages/babel-parser/test/fixtures/comments/basic/create-parenthesized-expressions/options.json b/packages/babel-parser/test/fixtures/comments/basic/create-parenthesized-expressions/options.json new file mode 100644 index 0000000000..0861962d88 --- /dev/null +++ b/packages/babel-parser/test/fixtures/comments/basic/create-parenthesized-expressions/options.json @@ -0,0 +1,3 @@ +{ + "createParenthesizedExpressions": true +} diff --git a/packages/babel-parser/test/fixtures/comments/basic/create-parenthesized-expressions/output.json b/packages/babel-parser/test/fixtures/comments/basic/create-parenthesized-expressions/output.json new file mode 100644 index 0000000000..7614637908 --- /dev/null +++ b/packages/babel-parser/test/fixtures/comments/basic/create-parenthesized-expressions/output.json @@ -0,0 +1,391 @@ +{ + "type": "File", + "start": 0, + "end": 65, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 12, + "column": 15 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 65, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 12, + "column": 15 + } + }, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start": 7, + "end": 11, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 4 + } + }, + "expression": { + "type": "ParenthesizedExpression", + "start": 7, + "end": 10, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 3 + } + }, + "expression": { + "type": "NumericLiteral", + "start": 8, + "end": 9, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 2 + } + }, + "extra": { + "rawValue": 1, + "raw": "1" + }, + "value": 1 + } + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " One", + "start": 0, + "end": 6, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 6 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentBlock", + "value": " Two ", + "start": 13, + "end": 22, + "loc": { + "start": { + "line": 4, + "column": 0 + }, + "end": { + "line": 4, + "column": 9 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 23, + "end": 27, + "loc": { + "start": { + "line": 5, + "column": 0 + }, + "end": { + "line": 5, + "column": 4 + } + }, + "expression": { + "type": "ParenthesizedExpression", + "start": 23, + "end": 26, + "loc": { + "start": { + "line": 5, + "column": 0 + }, + "end": { + "line": 5, + "column": 3 + } + }, + "expression": { + "type": "NumericLiteral", + "start": 24, + "end": 25, + "loc": { + "start": { + "line": 5, + "column": 1 + }, + "end": { + "line": 5, + "column": 2 + } + }, + "extra": { + "rawValue": 2, + "raw": "2" + }, + "value": 2 + } + }, + "leadingComments": [ + { + "type": "CommentBlock", + "value": " Two ", + "start": 13, + "end": 22, + "loc": { + "start": { + "line": 4, + "column": 0 + }, + "end": { + "line": 4, + "column": 9 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 29, + "end": 48, + "loc": { + "start": { + "line": 7, + "column": 0 + }, + "end": { + "line": 10, + "column": 2 + } + }, + "expression": { + "type": "ParenthesizedExpression", + "start": 29, + "end": 47, + "loc": { + "start": { + "line": 7, + "column": 0 + }, + "end": { + "line": 10, + "column": 1 + } + }, + "expression": { + "type": "NumericLiteral", + "start": 44, + "end": 45, + "loc": { + "start": { + "line": 9, + "column": 2 + }, + "end": { + "line": 9, + "column": 3 + } + }, + "extra": { + "rawValue": 3, + "raw": "3" + }, + "value": 3, + "leadingComments": [ + { + "type": "CommentLine", + "value": " Three", + "start": 33, + "end": 41, + "loc": { + "start": { + "line": 8, + "column": 2 + }, + "end": { + "line": 8, + "column": 10 + } + } + } + ] + } + } + }, + { + "type": "ExpressionStatement", + "start": 50, + "end": 65, + "loc": { + "start": { + "line": 12, + "column": 0 + }, + "end": { + "line": 12, + "column": 15 + } + }, + "expression": { + "type": "ParenthesizedExpression", + "start": 50, + "end": 64, + "loc": { + "start": { + "line": 12, + "column": 0 + }, + "end": { + "line": 12, + "column": 14 + } + }, + "expression": { + "type": "NumericLiteral", + "start": 62, + "end": 63, + "loc": { + "start": { + "line": 12, + "column": 12 + }, + "end": { + "line": 12, + "column": 13 + } + }, + "extra": { + "rawValue": 4, + "raw": "4" + }, + "value": 4, + "leadingComments": [ + { + "type": "CommentBlock", + "value": " Four ", + "start": 51, + "end": 61, + "loc": { + "start": { + "line": 12, + "column": 1 + }, + "end": { + "line": 12, + "column": 11 + } + } + } + ] + } + } + } + ], + "directives": [] + }, + "comments": [ + { + "type": "CommentLine", + "value": " One", + "start": 0, + "end": 6, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 6 + } + } + }, + { + "type": "CommentBlock", + "value": " Two ", + "start": 13, + "end": 22, + "loc": { + "start": { + "line": 4, + "column": 0 + }, + "end": { + "line": 4, + "column": 9 + } + } + }, + { + "type": "CommentLine", + "value": " Three", + "start": 33, + "end": 41, + "loc": { + "start": { + "line": 8, + "column": 2 + }, + "end": { + "line": 8, + "column": 10 + } + } + }, + { + "type": "CommentBlock", + "value": " Four ", + "start": 51, + "end": 61, + "loc": { + "start": { + "line": 12, + "column": 1 + }, + "end": { + "line": 12, + "column": 11 + } + } + } + ] +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/core/uncategorised/556/input.js b/packages/babel-parser/test/fixtures/core/uncategorised/556/input.js new file mode 100644 index 0000000000..13cfa4015d --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/uncategorised/556/input.js @@ -0,0 +1 @@ +({x}) = {x: 1}; diff --git a/packages/babel-parser/test/fixtures/core/uncategorised/556/options.json b/packages/babel-parser/test/fixtures/core/uncategorised/556/options.json new file mode 100644 index 0000000000..eff49cbfba --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/uncategorised/556/options.json @@ -0,0 +1,4 @@ +{ + "createParenthesizedExpressions": true, + "throws": "You're trying to assign to a parenthesized expression, eg. instead of `({a}) = 0` use `({a} = 0)` (1:1)" +} diff --git a/packages/babel-parser/test/fixtures/core/uncategorised/557/input.js b/packages/babel-parser/test/fixtures/core/uncategorised/557/input.js new file mode 100644 index 0000000000..40d925520d --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/uncategorised/557/input.js @@ -0,0 +1 @@ +("hello"); diff --git a/packages/babel-parser/test/fixtures/core/uncategorised/557/options.json b/packages/babel-parser/test/fixtures/core/uncategorised/557/options.json new file mode 100644 index 0000000000..0861962d88 --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/uncategorised/557/options.json @@ -0,0 +1,3 @@ +{ + "createParenthesizedExpressions": true +} diff --git a/packages/babel-parser/test/fixtures/core/uncategorised/557/output.json b/packages/babel-parser/test/fixtures/core/uncategorised/557/output.json new file mode 100644 index 0000000000..b1f49f00e9 --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/uncategorised/557/output.json @@ -0,0 +1,85 @@ +{ + "type": "File", + "start": 0, + "end": 10, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 10 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 10, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 10 + } + }, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 10, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 10 + } + }, + "expression": { + "type": "ParenthesizedExpression", + "start": 0, + "end": 9, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 9 + } + }, + "expression": { + "type": "StringLiteral", + "start": 1, + "end": 8, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 8 + } + }, + "extra": { + "rawValue": "hello", + "raw": "\"hello\"" + }, + "value": "hello" + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/core/uncategorised/558/input.js b/packages/babel-parser/test/fixtures/core/uncategorised/558/input.js new file mode 100644 index 0000000000..2cf717d47d --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/uncategorised/558/input.js @@ -0,0 +1 @@ +([a]) = [] diff --git a/packages/babel-parser/test/fixtures/core/uncategorised/558/options.json b/packages/babel-parser/test/fixtures/core/uncategorised/558/options.json new file mode 100644 index 0000000000..f37c4bc95c --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/uncategorised/558/options.json @@ -0,0 +1,4 @@ +{ + "createParenthesizedExpressions": true, + "throws": "You're trying to assign to a parenthesized expression, eg. instead of `([a]) = 0` use `([a] = 0)` (1:1)" +} diff --git a/packages/babel-parser/test/fixtures/es2015/arrow-functions/create-parenthesized-expressions/input.js b/packages/babel-parser/test/fixtures/es2015/arrow-functions/create-parenthesized-expressions/input.js new file mode 100644 index 0000000000..6626a35389 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/arrow-functions/create-parenthesized-expressions/input.js @@ -0,0 +1 @@ +(foo) => {} diff --git a/packages/babel-parser/test/fixtures/es2015/arrow-functions/create-parenthesized-expressions/options.json b/packages/babel-parser/test/fixtures/es2015/arrow-functions/create-parenthesized-expressions/options.json new file mode 100644 index 0000000000..0861962d88 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/arrow-functions/create-parenthesized-expressions/options.json @@ -0,0 +1,3 @@ +{ + "createParenthesizedExpressions": true +} diff --git a/packages/babel-parser/test/fixtures/es2015/arrow-functions/create-parenthesized-expressions/output.json b/packages/babel-parser/test/fixtures/es2015/arrow-functions/create-parenthesized-expressions/output.json new file mode 100644 index 0000000000..09568c70bb --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/arrow-functions/create-parenthesized-expressions/output.json @@ -0,0 +1,104 @@ +{ + "type": "File", + "start": 0, + "end": 11, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 11 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 11, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 11 + } + }, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 11, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 11 + } + }, + "expression": { + "type": "ArrowFunctionExpression", + "start": 0, + "end": 11, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 11 + } + }, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1, + "end": 4, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 4 + }, + "identifierName": "foo" + }, + "name": "foo" + } + ], + "body": { + "type": "BlockStatement", + "start": 9, + "end": 11, + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 1, + "column": 11 + } + }, + "body": [], + "directives": [] + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2015/uncategorised/394/input.js b/packages/babel-parser/test/fixtures/es2015/uncategorised/394/input.js new file mode 100644 index 0000000000..d6a05dcf09 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/uncategorised/394/input.js @@ -0,0 +1,4 @@ +var a; +(a) = {}; +(a.b) = {}; +(a['c']) = {}; diff --git a/packages/babel-parser/test/fixtures/es2015/uncategorised/394/options.json b/packages/babel-parser/test/fixtures/es2015/uncategorised/394/options.json new file mode 100644 index 0000000000..0861962d88 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/uncategorised/394/options.json @@ -0,0 +1,3 @@ +{ + "createParenthesizedExpressions": true +} diff --git a/packages/babel-parser/test/fixtures/es2015/uncategorised/394/output.json b/packages/babel-parser/test/fixtures/es2015/uncategorised/394/output.json new file mode 100644 index 0000000000..3eccd63cbd --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/uncategorised/394/output.json @@ -0,0 +1,392 @@ +{ + "type": "File", + "start": 0, + "end": 43, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 14 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 43, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 14 + } + }, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "VariableDeclaration", + "start": 0, + "end": 6, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 6 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 4, + "end": 5, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 5 + } + }, + "id": { + "type": "Identifier", + "start": 4, + "end": 5, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 5 + }, + "identifierName": "a" + }, + "name": "a" + }, + "init": null + } + ], + "kind": "var" + }, + { + "type": "ExpressionStatement", + "start": 7, + "end": 16, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 9 + } + }, + "expression": { + "type": "AssignmentExpression", + "start": 7, + "end": 15, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 8 + } + }, + "operator": "=", + "left": { + "type": "ParenthesizedExpression", + "start": 7, + "end": 10, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 3 + } + }, + "expression": { + "type": "Identifier", + "start": 8, + "end": 9, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 2 + }, + "identifierName": "a" + }, + "name": "a" + } + }, + "right": { + "type": "ObjectExpression", + "start": 13, + "end": 15, + "loc": { + "start": { + "line": 2, + "column": 6 + }, + "end": { + "line": 2, + "column": 8 + } + }, + "properties": [] + } + } + }, + { + "type": "ExpressionStatement", + "start": 17, + "end": 28, + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 11 + } + }, + "expression": { + "type": "AssignmentExpression", + "start": 17, + "end": 27, + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 10 + } + }, + "operator": "=", + "left": { + "type": "ParenthesizedExpression", + "start": 17, + "end": 22, + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 5 + } + }, + "expression": { + "type": "MemberExpression", + "start": 18, + "end": 21, + "loc": { + "start": { + "line": 3, + "column": 1 + }, + "end": { + "line": 3, + "column": 4 + } + }, + "object": { + "type": "Identifier", + "start": 18, + "end": 19, + "loc": { + "start": { + "line": 3, + "column": 1 + }, + "end": { + "line": 3, + "column": 2 + }, + "identifierName": "a" + }, + "name": "a" + }, + "property": { + "type": "Identifier", + "start": 20, + "end": 21, + "loc": { + "start": { + "line": 3, + "column": 3 + }, + "end": { + "line": 3, + "column": 4 + }, + "identifierName": "b" + }, + "name": "b" + }, + "computed": false + } + }, + "right": { + "type": "ObjectExpression", + "start": 25, + "end": 27, + "loc": { + "start": { + "line": 3, + "column": 8 + }, + "end": { + "line": 3, + "column": 10 + } + }, + "properties": [] + } + } + }, + { + "type": "ExpressionStatement", + "start": 29, + "end": 43, + "loc": { + "start": { + "line": 4, + "column": 0 + }, + "end": { + "line": 4, + "column": 14 + } + }, + "expression": { + "type": "AssignmentExpression", + "start": 29, + "end": 42, + "loc": { + "start": { + "line": 4, + "column": 0 + }, + "end": { + "line": 4, + "column": 13 + } + }, + "operator": "=", + "left": { + "type": "ParenthesizedExpression", + "start": 29, + "end": 37, + "loc": { + "start": { + "line": 4, + "column": 0 + }, + "end": { + "line": 4, + "column": 8 + } + }, + "expression": { + "type": "MemberExpression", + "start": 30, + "end": 36, + "loc": { + "start": { + "line": 4, + "column": 1 + }, + "end": { + "line": 4, + "column": 7 + } + }, + "object": { + "type": "Identifier", + "start": 30, + "end": 31, + "loc": { + "start": { + "line": 4, + "column": 1 + }, + "end": { + "line": 4, + "column": 2 + }, + "identifierName": "a" + }, + "name": "a" + }, + "property": { + "type": "StringLiteral", + "start": 32, + "end": 35, + "loc": { + "start": { + "line": 4, + "column": 3 + }, + "end": { + "line": 4, + "column": 6 + } + }, + "extra": { + "rawValue": "c", + "raw": "'c'" + }, + "value": "c" + }, + "computed": true + } + }, + "right": { + "type": "ObjectExpression", + "start": 40, + "end": 42, + "loc": { + "start": { + "line": 4, + "column": 11 + }, + "end": { + "line": 4, + "column": 13 + } + }, + "properties": [] + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2015/uncategorised/395/input.js b/packages/babel-parser/test/fixtures/es2015/uncategorised/395/input.js new file mode 100644 index 0000000000..874dec75a9 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/uncategorised/395/input.js @@ -0,0 +1 @@ +([a.a]) => 42 \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2015/uncategorised/395/options.json b/packages/babel-parser/test/fixtures/es2015/uncategorised/395/options.json new file mode 100644 index 0000000000..db72a6af95 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/uncategorised/395/options.json @@ -0,0 +1,4 @@ +{ + "createParenthesizedExpressions": true, + "throws": "Invalid left-hand side in arrow function parameters (1:2)" +} diff --git a/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/15/input.js b/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/15/input.js new file mode 100644 index 0000000000..63fce35a8a --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/15/input.js @@ -0,0 +1 @@ +-(5) ** 6; diff --git a/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/15/options.json b/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/15/options.json new file mode 100644 index 0000000000..c694133bf2 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/15/options.json @@ -0,0 +1,4 @@ +{ + "createParenthesizedExpressions": true, + "throws": "Illegal expression. Wrap left hand side or entire exponentiation in parentheses. (1:1)" +} diff --git a/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/16/input.js b/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/16/input.js new file mode 100644 index 0000000000..47b91d35a8 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/16/input.js @@ -0,0 +1 @@ +(-5 ** 6); diff --git a/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/16/options.json b/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/16/options.json new file mode 100644 index 0000000000..19687fbe19 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/16/options.json @@ -0,0 +1,4 @@ +{ + "createParenthesizedExpressions": true, + "throws": "Illegal expression. Wrap left hand side or entire exponentiation in parentheses. (1:2)" +} diff --git a/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/2/output.json b/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/2/output.json index e4e9ecd32a..e840aecbd1 100644 --- a/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/2/output.json +++ b/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/2/output.json @@ -124,4 +124,4 @@ ], "directives": [] } -} \ No newline at end of file +} diff --git a/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/7/output.json b/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/7/output.json index c4f295eacf..2fb85f363b 100644 --- a/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/7/output.json +++ b/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/7/output.json @@ -160,4 +160,4 @@ ], "directives": [] } -} \ No newline at end of file +} diff --git a/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/8/output.json b/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/8/output.json index fd863ac0f6..56c71d7d25 100644 --- a/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/8/output.json +++ b/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/8/output.json @@ -156,4 +156,4 @@ ], "directives": [] } -} \ No newline at end of file +} diff --git a/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/9/output.json b/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/9/output.json index c3f4e42e1c..2691c64337 100644 --- a/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/9/output.json +++ b/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/9/output.json @@ -124,4 +124,4 @@ ], "directives": [] } -} \ No newline at end of file +} diff --git a/packages/babel-parser/typings/babel-parser.d.ts b/packages/babel-parser/typings/babel-parser.d.ts index b3f745f61a..deba6be0e6 100644 --- a/packages/babel-parser/typings/babel-parser.d.ts +++ b/packages/babel-parser/typings/babel-parser.d.ts @@ -78,6 +78,14 @@ export interface ParserOptions { * Adds all parsed tokens to a tokens property on the File node. */ tokens?: boolean; + + /** + * By default, the parser adds information about parentheses by setting + * `extra.parenthesized` to `true` as needed. + * When this option is `true` the parser creates `ParenthesizedExpression` + * AST nodes instead of using the `extra` property. + */ + createParenthesizedExpressions?: boolean; } export type ParserPlugin = diff --git a/packages/babel-traverse/src/path/inference/inferers.js b/packages/babel-traverse/src/path/inference/inferers.js index 2f8ab65838..fb9a467b1f 100644 --- a/packages/babel-traverse/src/path/inference/inferers.js +++ b/packages/babel-traverse/src/path/inference/inferers.js @@ -102,6 +102,10 @@ export function SequenceExpression() { .getTypeAnnotation(); } +export function ParenthesizedExpression() { + return this.get("expression").getTypeAnnotation(); +} + export function AssignmentExpression() { return this.get("right").getTypeAnnotation(); } diff --git a/packages/babel-types/src/asserts/generated/index.js b/packages/babel-types/src/asserts/generated/index.js index 34a6a125a2..75fcbd6a8d 100644 --- a/packages/babel-types/src/asserts/generated/index.js +++ b/packages/babel-types/src/asserts/generated/index.js @@ -162,6 +162,12 @@ export function assertSequenceExpression( ): void { assert("SequenceExpression", node, opts); } +export function assertParenthesizedExpression( + node: Object, + opts?: Object = {}, +): void { + assert("ParenthesizedExpression", node, opts); +} export function assertSwitchCase(node: Object, opts?: Object = {}): void { assert("SwitchCase", node, opts); } @@ -654,12 +660,6 @@ export function assertJSXClosingFragment( export function assertNoop(node: Object, opts?: Object = {}): void { assert("Noop", node, opts); } -export function assertParenthesizedExpression( - node: Object, - opts?: Object = {}, -): void { - assert("ParenthesizedExpression", node, opts); -} export function assertArgumentPlaceholder( node: Object, opts?: Object = {}, diff --git a/packages/babel-types/src/builders/generated/index.js b/packages/babel-types/src/builders/generated/index.js index 6b787e0bb3..e7af718d9c 100644 --- a/packages/babel-types/src/builders/generated/index.js +++ b/packages/babel-types/src/builders/generated/index.js @@ -161,6 +161,10 @@ export function SequenceExpression(...args: Array): Object { return builder("SequenceExpression", ...args); } export { SequenceExpression as sequenceExpression }; +export function ParenthesizedExpression(...args: Array): Object { + return builder("ParenthesizedExpression", ...args); +} +export { ParenthesizedExpression as parenthesizedExpression }; export function SwitchCase(...args: Array): Object { return builder("SwitchCase", ...args); } @@ -592,10 +596,6 @@ export function Noop(...args: Array): Object { return builder("Noop", ...args); } export { Noop as noop }; -export function ParenthesizedExpression(...args: Array): Object { - return builder("ParenthesizedExpression", ...args); -} -export { ParenthesizedExpression as parenthesizedExpression }; export function ArgumentPlaceholder(...args: Array): Object { return builder("ArgumentPlaceholder", ...args); } diff --git a/packages/babel-types/src/definitions/core.js b/packages/babel-types/src/definitions/core.js index 214fa6ccd4..1e4de3ebbf 100644 --- a/packages/babel-types/src/definitions/core.js +++ b/packages/babel-types/src/definitions/core.js @@ -736,6 +736,16 @@ defineType("SequenceExpression", { aliases: ["Expression"], }); +defineType("ParenthesizedExpression", { + visitor: ["expression"], + aliases: ["Expression", "ExpressionWrapper"], + fields: { + expression: { + validate: assertNodeType("Expression"), + }, + }, +}); + defineType("SwitchCase", { visitor: ["test", "consequent"], fields: { diff --git a/packages/babel-types/src/definitions/misc.js b/packages/babel-types/src/definitions/misc.js index 2fc1334aac..6bc22babc0 100644 --- a/packages/babel-types/src/definitions/misc.js +++ b/packages/babel-types/src/definitions/misc.js @@ -1,16 +1,6 @@ // @flow -import defineType, { assertNodeType } from "./utils"; +import defineType from "./utils"; defineType("Noop", { visitor: [], }); - -defineType("ParenthesizedExpression", { - visitor: ["expression"], - aliases: ["Expression", "ExpressionWrapper"], - fields: { - expression: { - validate: assertNodeType("Expression"), - }, - }, -}); diff --git a/packages/babel-types/src/validators/generated/index.js b/packages/babel-types/src/validators/generated/index.js index c23ddcf11d..969c6cdc2d 100644 --- a/packages/babel-types/src/validators/generated/index.js +++ b/packages/babel-types/src/validators/generated/index.js @@ -551,6 +551,23 @@ export function isSequenceExpression(node: ?Object, opts?: Object): boolean { return false; } +export function isParenthesizedExpression( + node: ?Object, + opts?: Object, +): boolean { + if (!node) return false; + + const nodeType = node.type; + if (nodeType === "ParenthesizedExpression") { + if (typeof opts === "undefined") { + return true; + } else { + return shallowEqual(node, opts); + } + } + + return false; +} export function isSwitchCase(node: ?Object, opts?: Object): boolean { if (!node) return false; @@ -2076,23 +2093,6 @@ export function isNoop(node: ?Object, opts?: Object): boolean { return false; } -export function isParenthesizedExpression( - node: ?Object, - opts?: Object, -): boolean { - if (!node) return false; - - const nodeType = node.type; - if (nodeType === "ParenthesizedExpression") { - if (typeof opts === "undefined") { - return true; - } else { - return shallowEqual(node, opts); - } - } - - return false; -} export function isArgumentPlaceholder(node: ?Object, opts?: Object): boolean { if (!node) return false; @@ -3256,6 +3256,7 @@ export function isExpression(node: ?Object, opts?: Object): boolean { "NewExpression" === nodeType || "ObjectExpression" === nodeType || "SequenceExpression" === nodeType || + "ParenthesizedExpression" === nodeType || "ThisExpression" === nodeType || "UnaryExpression" === nodeType || "UpdateExpression" === nodeType || @@ -3269,7 +3270,6 @@ export function isExpression(node: ?Object, opts?: Object): boolean { "TypeCastExpression" === nodeType || "JSXElement" === nodeType || "JSXFragment" === nodeType || - "ParenthesizedExpression" === nodeType || "AwaitExpression" === nodeType || "BindExpression" === nodeType || "OptionalMemberExpression" === nodeType || @@ -3559,8 +3559,8 @@ export function isExpressionWrapper(node: ?Object, opts?: Object): boolean { if ( nodeType === "ExpressionWrapper" || "ExpressionStatement" === nodeType || - "TypeCastExpression" === nodeType || - "ParenthesizedExpression" === nodeType + "ParenthesizedExpression" === nodeType || + "TypeCastExpression" === nodeType ) { if (typeof opts === "undefined") { return true;