diff --git a/lib/rules/arrow-body-style.js b/lib/rules/arrow-body-style.js index 5954cf4a18d..7b318ea8b3a 100644 --- a/lib/rules/arrow-body-style.js +++ b/lib/rules/arrow-body-style.js @@ -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. @@ -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. @@ -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, ")") + ); + } } /* @@ -245,7 +263,24 @@ module.exports = { } return { - "ArrowFunctionExpression:exit": validate + "BinaryExpression[operator='in']"() { + let info = funcInfo; + + while (info) { + info.hasInOperator = true; + info = info.upper; + } + }, + ArrowFunctionExpression() { + funcInfo = { + upper: funcInfo, + hasInOperator: false + }; + }, + "ArrowFunctionExpression:exit"(node) { + validate(node); + funcInfo = funcInfo.upper; + } }; } }; diff --git a/tests/lib/rules/arrow-body-style.js b/tests/lib/rules/arrow-body-style.js index c624a847c6a..7a8de4fe5ef 100644 --- a/tests/lib/rules/arrow-body-style.js +++ b/tests/lib/rules/arrow-body-style.js @@ -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}", @@ -370,8 +603,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: [ @@ -380,10 +613,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: [