Repository: eslint/eslint (js)

Issue:
Crash with `"arrow-body-style": ["error", "always"]`
### Environment Info
Node version: v12.19.0
npm version: v6.14.8
Local ESLint version: v7.27.0 (Currently used)
Global ESLint version: Not found
Operating System: darwin 20.4.0

### Parser
`"@babel/eslint-parser": "7.14.3"`

### Description
ESLint breaks using rule `"arrow-body-style": ["error", "always"]` on a very specific function signature.

In my case it's `const createMarker = (color) => ({ latitude, longitude }, index) => {};`.

Other variations of `arrow-body-style` (`never` or `as-needed`) work fine.

[DEMO](https://eslint.org/demo#eyJ0ZXh0IjoiLyogZXNsaW50ICBcImFycm93LWJvZHktc3R5bGVcIjogW1wiZXJyb3JcIiwgXCJhbHdheXNcIl0gKi9cblxuY29uc3QgY3JlYXRlTWFya2VyID0gKGNvbG9yKSA9PiAoeyBsYXRpdHVkZSwgbG9uZ2l0dWRlIH0sIGluZGV4KSA9PiB7fTsiLCJvcHRpb25zIjp7InBhcnNlck9wdGlvbnMiOnsiZWNtYVZlcnNpb24iOjgsInNvdXJjZVR5cGUiOiJzY3JpcHQiLCJlY21hRmVhdHVyZXMiOnt9fSwicnVsZXMiOnt9LCJlbnYiOnt9fX0=)


Pull request description:
Fix: arrow-body-style crash with object pattern (fixes #14633)
<!--
    Thank you for contributing!

    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

#### Prerequisites checklist

- [x] I have read the [contributing guidelines](https://github.com/eslint/eslint/blob/master/CONTRIBUTING.md).

#### What is the purpose of this pull request? (put an "X" next to an item)

<!--
    The following template is intentionally not a markdown checkbox list for the reasons
    explained in https://github.com/eslint/eslint/pull/12848#issuecomment-580302888
-->

[ ] Documentation update
[X] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-proposal.md))
[ ] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-change-proposal.md))
[ ] Add autofixing to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:

fixes #14633

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (https://eslint.org/docs/developer-guide/contributing/pull-requests)
    - Include tests for this change
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

#### What changes did you make? (Give an overview)

Fixed the logic in `arrow-body-style` rule that assumed that `{` token at a certain position must be the start of an `ObjectExpression`, but in some cases it can be `ObjectPattern`.

#### Is there anything you'd like reviewers to focus on?


Patch:
diff --git a/lib/rules/arrow-body-style.js b/lib/rules/arrow-body-style.js
index b2167fde7..5b8a5f011 100644
--- a/lib/rules/arrow-body-style.js
+++ b/lib/rules/arrow-body-style.js
@@ -87,17 +87,17 @@ module.exports = {
         }
 
         /**
-         * Gets the closing parenthesis which is the pair of the given opening parenthesis.
-         * @param {Token} token The opening parenthesis token to get.
+         * Gets the closing parenthesis by the given node.
+         * @param {ASTNode} node first node after an opening parenthesis.
          * @returns {Token} The found closing parenthesis token.
          */
-        function findClosingParen(token) {
-            let node = sourceCode.getNodeByRangeIndex(token.range[0]);
+        function findClosingParen(node) {
+            let nodeToCheck = node;
 
-            while (!astUtils.isParenthesised(sourceCode, node)) {
-                node = node.parent;
+            while (!astUtils.isParenthesised(sourceCode, nodeToCheck)) {
+                nodeToCheck = nodeToCheck.parent;
             }
-            return sourceCode.getTokenAfter(node);
+            return sourceCode.getTokenAfter(nodeToCheck);
         }
 
         /**
@@ -226,12 +226,22 @@ module.exports = {
                             const arrowToken = sourceCode.getTokenBefore(arrowBody, astUtils.isArrowToken);
                             const [firstTokenAfterArrow, secondTokenAfterArrow] = sourceCode.getTokensAfter(arrowToken, { count: 2 });
                             const lastToken = sourceCode.getLastToken(node);
-                            const isParenthesisedObjectLiteral =
+
+                            let parenthesisedObjectLiteral = null;
+
+                            if (
                                 astUtils.isOpeningParenToken(firstTokenAfterArrow) &&
-                                astUtils.isOpeningBraceToken(secondTokenAfterArrow);
+                                astUtils.isOpeningBraceToken(secondTokenAfterArrow)
+                            ) {
+                                const braceNode = sourceCode.getNodeByRangeIndex(secondTokenAfterArrow.range[0]);
+
+                                if (braceNode.type === "ObjectExpression") {
+                                    parenthesisedObjectLiteral = braceNode;
+                                }
+                            }
 
                             // If the value is object literal, remove parentheses which were forced by syntax.
-                            if (isParenthesisedObjectLiteral) {
+                            if (parenthesisedObjectLiteral) {
                                 const openingParenToken = firstTokenAfterArrow;
                                 const openingBraceToken = secondTokenAfterArrow;
 
@@ -247,7 +257,7 @@ module.exports = {
                                 }
 
                                 // Closing paren for the object doesn't have to be lastToken, e.g.: () => ({}).foo()
-                                fixes.push(fixer.remove(findClosingParen(openingBraceToken)));
+                                fixes.push(fixer.remove(findClosingParen(parenthesisedObjectLiteral)));
                                 fixes.push(fixer.insertTextAfter(lastToken, "}"));
 
                             } else {


Test patch:
diff --git a/tests/lib/rules/arrow-body-style.js b/tests/lib/rules/arrow-body-style.js
index 7a8de4fe5..0ccb440d3 100644
--- a/tests/lib/rules/arrow-body-style.js
+++ b/tests/lib/rules/arrow-body-style.js
@@ -810,6 +810,14 @@ ruleTester.run("arrow-body-style", rule, {
             `,
             options: ["always"],
             errors: [{ messageId: "expectedBlock" }]
+        },
+
+        // https://github.com/eslint/eslint/issues/14633
+        {
+            code: "const createMarker = (color) => ({ latitude, longitude }, index) => {};",
+            output: "const createMarker = (color) => {return ({ latitude, longitude }, index) => {}};",
+            options: ["always"],
+            errors: [{ messageId: "expectedBlock" }]
         }
     ]
 });