Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: arrow-body-style fixer for in wrap (fixes #11849) #13228

Merged
merged 12 commits into from Jul 11, 2020
47 changes: 41 additions & 6 deletions lib/rules/arrow-body-style.js
Expand Up @@ -75,6 +75,7 @@ module.exports = {
const never = options[0] === "never";
const requireReturnForObjectLiteral = options[1] && options[1].requireReturnForObjectLiteral;
const sourceCode = context.getSourceCode();
let funcInfo = null;

/**
* Checks whether the given node has ASI problem or not.
Expand All @@ -99,6 +100,21 @@ module.exports = {
return sourceCode.getTokenAfter(node);
}

/**
* Check whether the node is inside of a for loop's init
* @param {ASTNode} node node is inside for loop
* @returns {boolean} `true` if the node is inside of a for loop, else `false`
*/
function isInsideForLoopInitializer(node) {
if (node && node.parent) {
if (node.parent.type === "ForStatement" && node.parent.init === node) {
return true;
}
return isInsideForLoopInitializer(node.parent);
}
return false;
}

/**
* Determines whether a arrow function body needs braces
* @param {ASTNode} node The arrow function node.
Expand Down Expand Up @@ -178,11 +194,13 @@ module.exports = {
* If the first token of the reutrn value is `{` or the return value is a sequence expression,
* enclose the return value by parentheses to avoid syntax error.
*/
if (astUtils.isOpeningBraceToken(firstValueToken) || blockBody[0].argument.type === "SequenceExpression") {
fixes.push(
fixer.insertTextBefore(firstValueToken, "("),
fixer.insertTextAfter(lastValueToken, ")")
);
if (astUtils.isOpeningBraceToken(firstValueToken) || blockBody[0].argument.type === "SequenceExpression" || (funcInfo.hasInOperator && isInsideForLoopInitializer(node))) {
if (!astUtils.isParenthesised(sourceCode, blockBody[0].argument)) {
fixes.push(
fixer.insertTextBefore(firstValueToken, "("),
fixer.insertTextAfter(lastValueToken, ")")
);
}
}

/*
Expand Down Expand Up @@ -245,7 +263,24 @@ module.exports = {
}

return {
"ArrowFunctionExpression:exit": validate
"BinaryExpression[operator='in']"() {
let info = funcInfo;
anikethsaha marked this conversation as resolved.
Show resolved Hide resolved

while (info) {
info.hasInOperator = true;
info = info.upper;
}
},
ArrowFunctionExpression() {
funcInfo = {
upper: funcInfo,
hasInOperator: false
};
},
"ArrowFunctionExpression:exit"(node) {
validate(node);
funcInfo = funcInfo.upper;
}
};
}
};
242 changes: 238 additions & 4 deletions tests/lib/rules/arrow-body-style.js
Expand Up @@ -45,6 +45,239 @@ ruleTester.run("arrow-body-style", rule, {
{ code: "var foo = () => { return { bar: 0 }; };", options: ["as-needed", { requireReturnForObjectLiteral: true }] }
],
invalid: [
{
code: "for (var foo = () => { return a in b ? bar : () => {} } ;;);",
output: "for (var foo = () => (a in b ? bar : () => {}) ;;);",
options: ["as-needed"],
errors: [
{
line: 1,
column: 22,
messageId: "unexpectedSingleBlock"
}
]
},
{
code: "a in b; for (var f = () => { return c };;);",
output: "a in b; for (var f = () => c;;);",
options: ["as-needed"],
errors: [
{
line: 1,
column: 28,
messageId: "unexpectedSingleBlock"
}
]
},
{
code: "for (a = b => { return c in d ? e : f } ;;);",
output: "for (a = b => (c in d ? e : f) ;;);",
options: ["as-needed"],
errors: [
{
line: 1,
column: 15,
messageId: "unexpectedSingleBlock"
}
]
},
{
code: "for (var f = () => { return a };;);",
output: "for (var f = () => a;;);",
options: ["as-needed"],
errors: [
{
line: 1,
column: 20,
messageId: "unexpectedSingleBlock"
}
]
},
{
code: "for (var f;f = () => { return a };);",
output: "for (var f;f = () => a;);",
options: ["as-needed"],
errors: [
{
line: 1,
column: 22,
messageId: "unexpectedSingleBlock"
}
]
},
{
code: "for (var f = () => { return a in c };;);",
output: "for (var f = () => (a in c);;);",
options: ["as-needed"],
errors: [
{
line: 1,
column: 20,
messageId: "unexpectedSingleBlock"
}
]
},
{
code: "for (var f;f = () => { return a in c };);",
output: "for (var f;f = () => a in c;);",
options: ["as-needed"],
errors: [
{
line: 1,
column: 22,
messageId: "unexpectedSingleBlock"
}
]
},
{
code: "for (;;){var f = () => { return a in c }}",
output: "for (;;){var f = () => a in c}",
options: ["as-needed"],
errors: [
{
line: 1,
column: 24,
messageId: "unexpectedSingleBlock"
}
]
},
{
code: "for (a = b => { return c = d in e } ;;);",
output: "for (a = b => (c = d in e) ;;);",
options: ["as-needed"],
errors: [
{
line: 1,
column: 15,
messageId: "unexpectedSingleBlock"
}
]
},
{
code: "for (var a;;a = b => { return c = d in e } );",
output: "for (var a;;a = b => c = d in e );",
options: ["as-needed"],
errors: [
{
line: 1,
column: 22,
messageId: "unexpectedSingleBlock"
}
]
},
{
code: "for (let a = (b, c, d) => { return vb && c in d; }; ;);",
output: "for (let a = (b, c, d) => (vb && c in d); ;);",
errors: [
{
line: 1,
column: 27,
messageId: "unexpectedSingleBlock"
}
]
},
{
code: "for (let a = (b, c, d) => { return v in b && c in d; }; ;);",
output: "for (let a = (b, c, d) => (v in b && c in d); ;);",
errors: [
{
line: 1,
column: 27,
messageId: "unexpectedSingleBlock"
}
]
},
{
code: "function foo(){ for (let a = (b, c, d) => { return v in b && c in d; }; ;); }",
output: "function foo(){ for (let a = (b, c, d) => (v in b && c in d); ;); }",
errors: [
{
line: 1,
column: 43,
messageId: "unexpectedSingleBlock"
}
]
},
{
code: "for ( a = (b, c, d) => { return v in b && c in d; }; ;);",
output: "for ( a = (b, c, d) => (v in b && c in d); ;);",
errors: [
{
line: 1,
column: 24,
messageId: "unexpectedSingleBlock"
}
]
},
{
code: "for ( a = (b) => { return (c in d) }; ;);",
output: "for ( a = (b) => (c in d); ;);",
errors: [
{
line: 1,
column: 18,
messageId: "unexpectedSingleBlock"
}
]
},
{
code: "for (let a = (b, c, d) => { return vb in dd ; }; ;);",
output: "for (let a = (b, c, d) => (vb in dd ); ;);",
errors: [
{
line: 1,
column: 27,
messageId: "unexpectedSingleBlock"
}
]
},
{
code: "for (let a = (b, c, d) => { return vb in c in dd ; }; ;);",
output: "for (let a = (b, c, d) => (vb in c in dd ); ;);",
errors: [
{
line: 1,
column: 27,
messageId: "unexpectedSingleBlock"
}
]
},
{
code: "do{let a = () => {return f in ff}}while(true){}",
output: "do{let a = () => f in ff}while(true){}",
errors: [{
line: 1,
column: 18,
messageId: "unexpectedSingleBlock"
}]
},
{
code: "do{for (let a = (b, c, d) => { return vb in c in dd ; }; ;);}while(true){}",
output: "do{for (let a = (b, c, d) => (vb in c in dd ); ;);}while(true){}",
errors: [{
line: 1,
column: 30,
messageId: "unexpectedSingleBlock"
}]
},
{
code: "scores.map(score => { return x in +(score / maxScore).toFixed(2)});",
output: "scores.map(score => x in +(score / maxScore).toFixed(2));",
errors: [{
line: 1,
column: 21,
messageId: "unexpectedSingleBlock"
}]
},
{
code: "const fn = (a, b) => { return a + x in Number(b) };",
output: "const fn = (a, b) => a + x in Number(b);",
errors: [{
line: 1,
column: 22,
messageId: "unexpectedSingleBlock"
}]
},
{
code: "var foo = () => 0",
output: "var foo = () => {return 0}",
Expand Down Expand Up @@ -368,8 +601,8 @@ ruleTester.run("arrow-body-style", rule, {

// Not fixed; fixing would cause ASI issues.
code:
"var foo = () => { return bar }\n" +
"[1, 2, 3].map(foo)",
"var foo = () => { return bar }\n" +
"[1, 2, 3].map(foo)",
output: null,
options: ["never"],
errors: [
Expand All @@ -378,10 +611,11 @@ ruleTester.run("arrow-body-style", rule, {
},
{


// Not fixed; fixing would cause ASI issues.
code:
"var foo = () => { return bar }\n" +
"(1).toString();",
"var foo = () => { return bar }\n" +
"(1).toString();",
output: null,
options: ["never"],
errors: [
Expand Down