Repository: prettier/prettier (JavaScript)

Issue:
Prettier parses `({ methodName() })` incorrectly
**Prettier 2.6.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAKYACAtnGALCAEwDkBDHVASgwF9KQAaECABxgEtoBnZUUgJ34QA7gAUBCHilIAbYaQCePJgCN+pMAGtcAZXJwAMuyhxkAM1lc4q9Vt0sNxgObIY-AK7WQcLCriFCfwNSKCd3Uic4ADEIfixSGA5Q5BBSdxgIRhA8GCwZAHU8dnguBzA4HUli9gA3YoUUsC5lEGMrfhhRdSd480svACsuAA8dZxk4AEV3CHg+mSsmB352lJgFFjguMH52NiyWXdh89kJ8ZAAOAAYloSt89RYUw624fhrTJgBHGfgu1ikqS4AFoTP5-Fl+HAfuwoV0Ir0kBYFl4rFh2K4PKjxlNfqYkf0mDBSCoTmc8MgAExE9TsGTOADCECwiO8XAArFl3FYACokqTIxYgGqeACSUECsB0Oz2MAAghKdOsJvMrDQaEA)
<!-- prettier-ignore -->
```sh
--parser typescript
```

**Input:**
<!-- prettier-ignore -->
```tsx
({ methodName() })
```

**Output:**
<!-- prettier-ignore -->
```tsx
({ methodName(); });

```

**Expected behavior:**
The code inputted is invalid and shouldn't be parsed. Prettier recognizes this as an object because it can be turned into a multiline object and Prettier still has invalid output.

**Prettier 2.6.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAKYAdKACbBbOGACwgBMA5AQwNQEosBfWkAGhAgAcYBLaAZ2ShKAJ2EQA7gAURCASkoAbcZQCeAtgCNhlMAGtCAZWpwAMtyhxkAM0V84m7XsMcd5gObIYwgK72QcPA04UlJgk0ooN29KNzgAMQhhPEoYHkjkEEpvGAhWECIYPAUAdSJueD4XMDgDWXLuADdylQywPnUQczthGEltN2TrWz8AKz4ADwN3BTgARW8IeCGFOzYXYW6MmBUOOD4wYW4uPI5D2GLuUmJkAA4ABjWxO2LtDgzTvbhhBss2AEcFvA+pw5Jk+ABaCzBYJ5YRwAHcOF9GKDJA2FZ+Ox4bieHyY6ZzQGWNHDNgwSgaC5XIjIABMZO03AU7gAwhA8Kj-HwAKx5bx2AAqFLk6NWIAavgAklBQrADAcjjAAIIygzbGbLOwMBhAA)
<!-- prettier-ignore -->
```sh
--parser typescript
```

**Input:**
<!-- prettier-ignore -->
```tsx
({
  methodName()
})
```

**Output:**
<!-- prettier-ignore -->
```tsx
({
  methodName();,
});

```



Hints:
By the way, testing shows that this doesn't happen with the `babel`, `babel-ts`, `babel-flow`, `acorn`, `espree`, or `meriyah` parsers, so it could be an upstream bug with TypeScript's parser. This might be because of TypeScript's function overloading not working properly in object literals, but I'm not sure.
I think that the typescript parser properly rejects this as malformed, but the typescript-eslint parser emits a `TSEmptyBodyFunctionExpression` AST node.

Related issue: https://github.com/prettier/prettier/issues/8416

Patch:
diff --git a/src/language-js/parse/postprocess/index.js b/src/language-js/parse/postprocess/index.js
--- a/src/language-js/parse/postprocess/index.js
+++ b/src/language-js/parse/postprocess/index.js
@@ -6,6 +6,7 @@ const isTypeCastComment = require("../../utils/is-type-cast-comment.js");
 const getLast = require("../../../utils/get-last.js");
 const visitNode = require("./visit-node.js");
 const { throwErrorForInvalidNodes } = require("./typescript.js");
+const throwSyntaxError = require("./throw-syntax-error.js");
 
 function postprocess(ast, options) {
   if (
@@ -102,6 +103,20 @@ function postprocess(ast, options) {
           };
         }
         break;
+      case "ObjectExpression":
+        // #12963
+        if (options.parser === "typescript") {
+          const invalidProperty = node.properties.find(
+            (property) =>
+              property.type === "Property" &&
+              property.value.type === "TSEmptyBodyFunctionExpression"
+          );
+          if (invalidProperty) {
+            throwSyntaxError(invalidProperty.value, "Unexpected token.");
+          }
+        }
+        break;
+
       case "SequenceExpression": {
         // Babel (unlike other parsers) includes spaces and comments in the range. Let's unify this.
         const lastExpression = getLast(node.expressions);
diff --git a/src/language-js/parse/postprocess/throw-syntax-error.js b/src/language-js/parse/postprocess/throw-syntax-error.js
new file mode 100644
--- /dev/null
+++ b/src/language-js/parse/postprocess/throw-syntax-error.js
@@ -0,0 +1,12 @@
+"use strict";
+const createError = require("../../../common/parser-create-error.js");
+
+function throwSyntaxError(node, message) {
+  const { start, end } = node.loc;
+  throw createError(message, {
+    start: { line: start.line, column: start.column + 1 },
+    end: { line: end.line, column: end.column + 1 },
+  });
+}
+
+module.exports = throwSyntaxError;
diff --git a/src/language-js/parse/postprocess/typescript.js b/src/language-js/parse/postprocess/typescript.js
--- a/src/language-js/parse/postprocess/typescript.js
+++ b/src/language-js/parse/postprocess/typescript.js
@@ -1,15 +1,7 @@
 "use strict";
 
-const createError = require("../../../common/parser-create-error.js");
 const visitNode = require("./visit-node.js");
-
-function throwSyntaxError(node, message) {
-  const { start, end } = node.loc;
-  throw createError(message, {
-    start: { line: start.line, column: start.column + 1 },
-    end: { line: end.line, column: end.column + 1 },
-  });
-}
+const throwSyntaxError = require("./throw-syntax-error.js");
 
 // Invalid decorators are removed since `@typescript-eslint/typescript-estree` v4
 // https://github.com/typescript-eslint/typescript-eslint/pull/2375
@@ -73,6 +65,7 @@ function throwErrorForInvalidNodes(ast, options) {
     if (esTreeNode !== node) {
       return;
     }
+
     throwErrorForInvalidDecorator(tsNode, esTreeNode, tsNodeToESTreeNodeMap);
     throwErrorForInvalidAbstractProperty(tsNode, esTreeNode);
   });


Test patch:
diff --git a/tests/format/misc/errors/invalid/__snapshots__/jsfmt.spec.js.snap b/tests/format/misc/errors/invalid/__snapshots__/jsfmt.spec.js.snap
--- a/tests/format/misc/errors/invalid/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/format/misc/errors/invalid/__snapshots__/jsfmt.spec.js.snap
@@ -156,92 +156,236 @@ exports[`snippet: #1 [typescript] format 1`] = `
     |       ^"
 `;
 
+exports[`snippet: #2 [acorn] format 1`] = `
+"Unexpected token (1:13)
+> 1 | ({ method() })
+    |             ^"
+`;
+
 exports[`snippet: #2 [babel] format 1`] = `
+"Unexpected token, expected "{" (1:13)
+> 1 | ({ method() })
+    |             ^"
+`;
+
+exports[`snippet: #2 [babel] format 2`] = `
 "Invalid parenthesized assignment pattern. (1:1)
 > 1 | (a = 1) = 2
     | ^"
 `;
 
 exports[`snippet: #2 [babel-flow] format 1`] = `
+"Unexpected token, expected "{" (1:13)
+> 1 | ({ method() })
+    |             ^"
+`;
+
+exports[`snippet: #2 [babel-flow] format 2`] = `
 "Invalid parenthesized assignment pattern. (1:1)
 > 1 | (a = 1) = 2
     | ^"
 `;
 
 exports[`snippet: #2 [babel-ts] format 1`] = `
+"Unexpected token, expected "{" (1:13)
+> 1 | ({ method() })
+    |             ^"
+`;
+
+exports[`snippet: #2 [babel-ts] format 2`] = `
 "Invalid parenthesized assignment pattern. (1:1)
 > 1 | (a = 1) = 2
     | ^"
 `;
 
+exports[`snippet: #2 [espree] format 1`] = `
+"Unexpected token } (1:13)
+> 1 | ({ method() })
+    |             ^"
+`;
+
 exports[`snippet: #2 [flow] format 1`] = `
+"Unexpected token \`}\`, expected the token \`{\` (1:13)
+> 1 | ({ method() })
+    |             ^"
+`;
+
+exports[`snippet: #2 [flow] format 2`] = `
 "Invalid left-hand side in assignment (1:2)
 > 1 | (a = 1) = 2
     |  ^^^^^"
 `;
 
 exports[`snippet: #2 [meriyah] format 1`] = `
+"Expected '{' (1:13)
+> 1 | ({ method() })
+    |             ^"
+`;
+
+exports[`snippet: #2 [meriyah] format 2`] = `
 "Invalid left-hand side in assignment (1:9)
 > 1 | (a = 1) = 2
     |         ^"
 `;
 
+exports[`snippet: #2 [typescript] format 1`] = `
+"Unexpected token. (1:10)
+> 1 | ({ method() })
+    |          ^^"
+`;
+
+exports[`snippet: #3 [acorn] format 1`] = `
+"Unexpected token (1:15)
+> 1 | ({ method({}) })
+    |               ^"
+`;
+
 exports[`snippet: #3 [babel] format 1`] = `
+"Unexpected token, expected "{" (1:15)
+> 1 | ({ method({}) })
+    |               ^"
+`;
+
+exports[`snippet: #3 [babel] format 2`] = `
 "Invalid parenthesized assignment pattern. (1:1)
 > 1 | (a += b) = 1
     | ^"
 `;
 
 exports[`snippet: #3 [babel-flow] format 1`] = `
+"Unexpected token, expected "{" (1:15)
+> 1 | ({ method({}) })
+    |               ^"
+`;
+
+exports[`snippet: #3 [babel-flow] format 2`] = `
 "Invalid parenthesized assignment pattern. (1:1)
 > 1 | (a += b) = 1
     | ^"
 `;
 
 exports[`snippet: #3 [babel-ts] format 1`] = `
+"Unexpected token, expected "{" (1:15)
+> 1 | ({ method({}) })
+    |               ^"
+`;
+
+exports[`snippet: #3 [babel-ts] format 2`] = `
 "Invalid parenthesized assignment pattern. (1:1)
 > 1 | (a += b) = 1
     | ^"
 `;
 
+exports[`snippet: #3 [espree] format 1`] = `
+"Unexpected token } (1:15)
+> 1 | ({ method({}) })
+    |               ^"
+`;
+
 exports[`snippet: #3 [flow] format 1`] = `
+"Unexpected token \`}\`, expected the token \`{\` (1:15)
+> 1 | ({ method({}) })
+    |               ^"
+`;
+
+exports[`snippet: #3 [flow] format 2`] = `
 "Invalid left-hand side in assignment (1:2)
 > 1 | (a += b) = 1
     |  ^^^^^^"
 `;
 
 exports[`snippet: #3 [meriyah] format 1`] = `
+"Expected '{' (1:15)
+> 1 | ({ method({}) })
+    |               ^"
+`;
+
+exports[`snippet: #3 [meriyah] format 2`] = `
 "Invalid left-hand side in assignment (1:10)
 > 1 | (a += b) = 1
     |          ^"
 `;
 
+exports[`snippet: #3 [typescript] format 1`] = `
+"Unexpected token. (1:10)
+> 1 | ({ method({}) })
+    |          ^^^^"
+`;
+
+exports[`snippet: #4 [acorn] format 1`] = `
+"Unexpected token (1:23)
+> 1 | ({ method(parameter,) })
+    |                       ^"
+`;
+
 exports[`snippet: #4 [babel] format 1`] = `
+"Unexpected token, expected "{" (1:23)
+> 1 | ({ method(parameter,) })
+    |                       ^"
+`;
+
+exports[`snippet: #4 [babel] format 2`] = `
 "Invalid left-hand side in parenthesized expression. (1:2)
 > 1 | (a = b) += 1
     |  ^"
 `;
 
 exports[`snippet: #4 [babel-flow] format 1`] = `
+"Unexpected token, expected "{" (1:23)
+> 1 | ({ method(parameter,) })
+    |                       ^"
+`;
+
+exports[`snippet: #4 [babel-flow] format 2`] = `
 "Invalid left-hand side in parenthesized expression. (1:2)
 > 1 | (a = b) += 1
     |  ^"
 `;
 
 exports[`snippet: #4 [babel-ts] format 1`] = `
+"Unexpected token, expected "{" (1:23)
+> 1 | ({ method(parameter,) })
+    |                       ^"
+`;
+
+exports[`snippet: #4 [babel-ts] format 2`] = `
 "Invalid left-hand side in parenthesized expression. (1:2)
 > 1 | (a = b) += 1
     |  ^"
 `;
 
+exports[`snippet: #4 [espree] format 1`] = `
+"Unexpected token } (1:23)
+> 1 | ({ method(parameter,) })
+    |                       ^"
+`;
+
 exports[`snippet: #4 [flow] format 1`] = `
+"Unexpected token \`}\`, expected the token \`{\` (1:23)
+> 1 | ({ method(parameter,) })
+    |                       ^"
+`;
+
+exports[`snippet: #4 [flow] format 2`] = `
 "Invalid left-hand side in assignment (1:2)
 > 1 | (a = b) += 1
     |  ^^^^^"
 `;
 
 exports[`snippet: #4 [meriyah] format 1`] = `
+"Expected '{' (1:23)
+> 1 | ({ method(parameter,) })
+    |                       ^"
+`;
+
+exports[`snippet: #4 [meriyah] format 2`] = `
 "Invalid left-hand side in assignment (1:10)
 > 1 | (a = b) += 1
     |          ^"
 `;
+
+exports[`snippet: #4 [typescript] format 1`] = `
+"Unexpected token. (1:10)
+> 1 | ({ method(parameter,) })
+    |          ^^^^^^^^^^^^"
+`;
diff --git a/tests/format/misc/errors/invalid/jsfmt.spec.js b/tests/format/misc/errors/invalid/jsfmt.spec.js
--- a/tests/format/misc/errors/invalid/jsfmt.spec.js
+++ b/tests/format/misc/errors/invalid/jsfmt.spec.js
@@ -1,7 +1,13 @@
 run_spec(
   {
     dirname: __dirname,
-    snippets: ["for each (a in b) {}", "class switch() {}"],
+    snippets: [
+      "for each (a in b) {}",
+      "class switch() {}",
+      "({ method() })",
+      "({ method({}) })",
+      "({ method(parameter,) })",
+    ],
   },
   [
     "babel",