Fix binary op as body in arrow expression (#921)
* Implement new logic for wrapping binary op in arrowFunctionExpression. * Add new test cases. * Reuse new helper function in order to fix #917. * Add new test case. * Extend heuristic to dive deeper into mixed types. * Add new test. * Enhance logic to cover more cases. * Add new test cases. * Disable Flow as it gets BindExpression as an unexpected token. * Simplify getCombinedDeepest function. * Add missing case. * Extract all conditions in switch cases to one top level condition. * Refactor implementation to make it cleaner and also handle ExpressionStatement. * Update related test cases. * Add new test case. * Make condition less expensive. * Clean up unecessary conditions, simplify condition involving startsWithOpenCurlyBrace. * Update and add new test cases for better coverage. * Remove unecessary condition, refactor canBeFirstInStatement to drop some useless parens. * Update test cases accordingly 🚀.master
parent
513b57db3a
commit
1b6ddf9a7f
|
@ -245,14 +245,16 @@ FPp.needsParens = function(assumeExpressionContext) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
((parent.type === "ArrowFunctionExpression" && parent.body === node) ||
|
||||||
|
parent.type === "ExpressionStatement") &&
|
||||||
|
startsWithOpenCurlyBrace(node)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
switch (node.type) {
|
switch (node.type) {
|
||||||
case "CallExpression":
|
case "CallExpression":
|
||||||
if (
|
|
||||||
node.callee.type === "ObjectExpression" &&
|
|
||||||
parent.type === "ArrowFunctionExpression"
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case "SpreadElement":
|
case "SpreadElement":
|
||||||
|
@ -305,13 +307,6 @@ FPp.needsParens = function(assumeExpressionContext) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
node.operator === "instanceof" &&
|
|
||||||
parent.type === "ArrowFunctionExpression"
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "LogicalExpression":
|
case "LogicalExpression":
|
||||||
switch (parent.type) {
|
switch (parent.type) {
|
||||||
case "CallExpression":
|
case "CallExpression":
|
||||||
|
@ -525,17 +520,6 @@ FPp.needsParens = function(assumeExpressionContext) {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case "ObjectExpression":
|
|
||||||
if (parent.type === "ArrowFunctionExpression" && name === "body") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (parent.type === "TaggedTemplateExpression") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (parent.type === "MemberExpression") {
|
|
||||||
return name === "object" && parent.object === node;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "StringLiteral":
|
case "StringLiteral":
|
||||||
if (parent.type === "ExpressionStatement") {
|
if (parent.type === "ExpressionStatement") {
|
||||||
return true;
|
return true;
|
||||||
|
@ -583,12 +567,41 @@ function containsCallExpression(node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function startsWithOpenCurlyBrace(node) {
|
||||||
|
node = getLeftMost(node);
|
||||||
|
switch (node.type) {
|
||||||
|
case "ObjectExpression":
|
||||||
|
return true;
|
||||||
|
case "MemberExpression":
|
||||||
|
return startsWithOpenCurlyBrace(node.object);
|
||||||
|
case "TaggedTemplateExpression":
|
||||||
|
return startsWithOpenCurlyBrace(node.tag);
|
||||||
|
case "CallExpression":
|
||||||
|
return startsWithOpenCurlyBrace(node.callee);
|
||||||
|
case "ConditionalExpression":
|
||||||
|
return startsWithOpenCurlyBrace(node.test);
|
||||||
|
case "UpdateExpression":
|
||||||
|
return !node.prefix && startsWithOpenCurlyBrace(node.argument);
|
||||||
|
case "BindExpression":
|
||||||
|
return node.object && startsWithOpenCurlyBrace(node.object);
|
||||||
|
case "SequenceExpression":
|
||||||
|
return startsWithOpenCurlyBrace(node.expressions[0])
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLeftMost(node) {
|
||||||
|
if (node.left) {
|
||||||
|
return getLeftMost(node.left);
|
||||||
|
} else {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FPp.canBeFirstInStatement = function() {
|
FPp.canBeFirstInStatement = function() {
|
||||||
var node = this.getNode();
|
const node = this.getNode();
|
||||||
return !n.FunctionExpression.check(node) &&
|
return !n.FunctionExpression.check(node) && !n.ClassExpression.check(node);
|
||||||
!n.ObjectExpression.check(node) &&
|
|
||||||
!n.ClassExpression.check(node) &&
|
|
||||||
!(n.AssignmentExpression.check(node) && n.ObjectPattern.check(node.left));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
FPp.firstInStatement = function() {
|
FPp.firstInStatement = function() {
|
||||||
|
|
|
@ -10,6 +10,23 @@ new (() => {});
|
||||||
if ((() => {}) ? 1 : 0) {}
|
if ((() => {}) ? 1 : 0) {}
|
||||||
let f = () => ({}())
|
let f = () => ({}())
|
||||||
let a = () => ({} instanceof a);
|
let a = () => ({} instanceof a);
|
||||||
|
a = () => ({} && a);
|
||||||
|
a = () => ({}() && a);
|
||||||
|
a = () => ({} && a && b);
|
||||||
|
a = () => ({} + a);
|
||||||
|
a = () => ({}()() && a);
|
||||||
|
a = () => ({}.b && a);
|
||||||
|
a = () => ({}[b] && a);
|
||||||
|
a = () => ({}\`\` && a);
|
||||||
|
a = () => ({} = 0);
|
||||||
|
a = () => ({}, a);
|
||||||
|
a => a instanceof {};
|
||||||
|
a => ({}().b && 0)
|
||||||
|
a => ({}::b()\`\`[''].c++ && 0 ? 0 : 0)
|
||||||
|
a => ({}().c = 0)
|
||||||
|
x => ({}()())
|
||||||
|
x => ({}()\`\`)
|
||||||
|
x => ({}().b)
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
(a => {}).length;
|
(a => {}).length;
|
||||||
typeof (() => {});
|
typeof (() => {});
|
||||||
|
@ -21,6 +38,23 @@ if ((() => {}) ? 1 : 0) {
|
||||||
}
|
}
|
||||||
let f = () => ({}());
|
let f = () => ({}());
|
||||||
let a = () => ({} instanceof a);
|
let a = () => ({} instanceof a);
|
||||||
|
a = () => ({} && a);
|
||||||
|
a = () => ({}() && a);
|
||||||
|
a = () => ({} && a && b);
|
||||||
|
a = () => ({} + a);
|
||||||
|
a = () => ({}()() && a);
|
||||||
|
a = () => ({}.b && a);
|
||||||
|
a = () => ({}[b] && a);
|
||||||
|
a = () => ({}\`\` && a);
|
||||||
|
a = () => ({} = 0);
|
||||||
|
a = () => ({}, a);
|
||||||
|
(a => a instanceof {});
|
||||||
|
(a => ({}().b && 0));
|
||||||
|
(a => ({}::b()\`\`[\\"\\"].c++ && 0 ? 0 : 0));
|
||||||
|
(a => ({}().c = 0));
|
||||||
|
(x => ({}()()));
|
||||||
|
(x => ({}()\`\`));
|
||||||
|
(x => ({}().b));
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
@ -7,3 +7,20 @@ new (() => {});
|
||||||
if ((() => {}) ? 1 : 0) {}
|
if ((() => {}) ? 1 : 0) {}
|
||||||
let f = () => ({}())
|
let f = () => ({}())
|
||||||
let a = () => ({} instanceof a);
|
let a = () => ({} instanceof a);
|
||||||
|
a = () => ({} && a);
|
||||||
|
a = () => ({}() && a);
|
||||||
|
a = () => ({} && a && b);
|
||||||
|
a = () => ({} + a);
|
||||||
|
a = () => ({}()() && a);
|
||||||
|
a = () => ({}.b && a);
|
||||||
|
a = () => ({}[b] && a);
|
||||||
|
a = () => ({}`` && a);
|
||||||
|
a = () => ({} = 0);
|
||||||
|
a = () => ({}, a);
|
||||||
|
a => a instanceof {};
|
||||||
|
a => ({}().b && 0)
|
||||||
|
a => ({}::b()``[''].c++ && 0 ? 0 : 0)
|
||||||
|
a => ({}().c = 0)
|
||||||
|
x => ({}()())
|
||||||
|
x => ({}()``)
|
||||||
|
x => ({}().b)
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
run_spec(__dirname);
|
run_spec(__dirname, {parser: 'babylon'});
|
||||||
|
|
|
@ -162,7 +162,7 @@ let tests = [
|
||||||
x + \\"\\"; // error
|
x + \\"\\"; // error
|
||||||
\\"\\" + x; // error
|
\\"\\" + x; // error
|
||||||
x + {}; // error
|
x + {}; // error
|
||||||
({}) + x; // error
|
({} + x); // error
|
||||||
},
|
},
|
||||||
|
|
||||||
// when one side is a string or number and the other is invalid, we
|
// when one side is a string or number and the other is invalid, we
|
||||||
|
@ -310,10 +310,10 @@ let tests = [
|
||||||
\\"foo\\" < 1; // error
|
\\"foo\\" < 1; // error
|
||||||
\\"foo\\" < \\"bar\\";
|
\\"foo\\" < \\"bar\\";
|
||||||
1 < { foo: 1 }; // error
|
1 < { foo: 1 }; // error
|
||||||
({ foo: 1 }) < 1; // error
|
({ foo: 1 } < 1); // error
|
||||||
({ foo: 1 }) < { foo: 1 }; // error
|
({ foo: 1 } < { foo: 1 }); // error
|
||||||
\\"foo\\" < { foo: 1 }; // error
|
\\"foo\\" < { foo: 1 }; // error
|
||||||
({ foo: 1 }) < \\"foo\\"; // error
|
({ foo: 1 } < \\"foo\\"); // error
|
||||||
|
|
||||||
var x = (null: ?number);
|
var x = (null: ?number);
|
||||||
1 < x; // 2 errors: null !~> number; undefined !~> number
|
1 < x; // 2 errors: null !~> number; undefined !~> number
|
||||||
|
|
|
@ -92,7 +92,7 @@ let tests = [
|
||||||
function() {
|
function() {
|
||||||
null in {}; // error
|
null in {}; // error
|
||||||
void 0 in {}; // error
|
void 0 in {}; // error
|
||||||
({}) in {}; // error
|
({} in {}); // error
|
||||||
[] in {}; // error
|
[] in {}; // error
|
||||||
false in []; // error
|
false in []; // error
|
||||||
},
|
},
|
||||||
|
|
|
@ -126,7 +126,7 @@ function foo() {
|
||||||
|
|
||||||
function bar(f: () => void) {
|
function bar(f: () => void) {
|
||||||
f(); // passing global object as \`this\`
|
f(); // passing global object as \`this\`
|
||||||
({ f }).f(); // passing container object as \`this\`
|
({ f }.f()); // passing container object as \`this\`
|
||||||
}
|
}
|
||||||
|
|
||||||
bar(foo); // error, since \`this\` is used non-trivially in \`foo\`
|
bar(foo); // error, since \`this\` is used non-trivially in \`foo\`
|
||||||
|
|
|
@ -2582,7 +2582,7 @@ let tests = [
|
||||||
function(x: A) {
|
function(x: A) {
|
||||||
if (x.kind === null.toString()) {
|
if (x.kind === null.toString()) {
|
||||||
} // error, method on null
|
} // error, method on null
|
||||||
if (({ kind: 1 }).kind === null.toString()) {
|
if ({ kind: 1 }.kind === null.toString()) {
|
||||||
} // error, method on null
|
} // error, method on null
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -295,8 +295,8 @@ exports[`test.js 1`] = `
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
method().then(x => x)[\\"abc\\"](x => x)[abc](x => x);
|
method().then(x => x)[\\"abc\\"](x => x)[abc](x => x);
|
||||||
|
|
||||||
({}).a().b();
|
({}.a().b());
|
||||||
({}).a().b();
|
({}.a().b());
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,22 @@ exports[`expression.js 1`] = `
|
||||||
"() => ({}\`\`);
|
"() => ({}\`\`);
|
||||||
({})\`\`;
|
({})\`\`;
|
||||||
a = () => ({}).x;
|
a = () => ({}).x;
|
||||||
|
({} && a, b);
|
||||||
|
({}::b, 0);
|
||||||
|
({}::b()\`\`[''].c++ && 0 ? 0 : 0, 0);
|
||||||
|
({}(), 0);
|
||||||
|
({} = 0);
|
||||||
|
(({} = 0), 1);
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
(() => ({})\`\`);
|
(() => ({}\`\`));
|
||||||
({})\`\`;
|
({}\`\`);
|
||||||
a = () => ({}).x;
|
a = () => ({}.x);
|
||||||
|
({} && a, b);
|
||||||
|
({}::b, 0);
|
||||||
|
({}::b()\`\`[\\"\\"].c++ && 0 ? 0 : 0, 0);
|
||||||
|
({}(), 0);
|
||||||
|
({} = 0);
|
||||||
|
({} = 0), 1;
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
() => ({}``);
|
() => ({}``);
|
||||||
({})``;
|
({})``;
|
||||||
a = () => ({}).x;
|
a = () => ({}).x;
|
||||||
|
({} && a, b);
|
||||||
|
({}::b, 0);
|
||||||
|
({}::b()``[''].c++ && 0 ? 0 : 0, 0);
|
||||||
|
({}(), 0);
|
||||||
|
({} = 0);
|
||||||
|
(({} = 0), 1);
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
run_spec(__dirname);
|
run_spec(__dirname, {parser: 'babylon'});
|
||||||
|
|
|
@ -125,7 +125,7 @@ b.c\`\`;
|
||||||
new B()\`\`;
|
new B()\`\`;
|
||||||
|
|
||||||
// \\"ObjectExpression\\"
|
// \\"ObjectExpression\\"
|
||||||
({})\`\`;
|
({}\`\`);
|
||||||
|
|
||||||
// \\"SequenceExpression\\"
|
// \\"SequenceExpression\\"
|
||||||
(b, c)\`\`;
|
(b, c)\`\`;
|
||||||
|
|
Loading…
Reference in New Issue