Repository: prettier/prettier (JavaScript)
Date: 2023-08-29 09:35:00+00:00
Task type: Bug Fix

Issue:
Error when formatting anonymous export default function
**Prettier 3.0.2**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEcAeAHCAnGACAEzgDMBDAVwBt9jyowYBLaACgEoAdKAem7xgAWjAM54ReUnlr0m0LsC5482ODHLYoeACwAmLgF8QAGhAQMsqMOShS2bBADuABVsIrKUpQekAnlZMARtikYADWqgDKpAC2cAAyjFBwyGSUwnCBwWGRGCGJAObIMNjkGSDp0YxFJWXCBZRwAIrkEPApnukmAFbCaBH1TS1tSKmdIACOQ3BO9hjuIKTCALRJcEQExiDFpIyUBQDCENHRpMgLlJSbdVD5DQCCMMWMAeTwTnDYCUntaWUCMNFKAB1ITwYS5MBwCJuRhMABusJ8ZzAwn8IDhpQAklAiLAImBsIxzHccREYD4Gj8xhh7OkgcEMGcaXB0tg4ckTIlWTAZqR8icqWVcthWWcAqQAnBLiYaYkYEDGARBMgABwABhMKkmjBUvP5pxGHTKMAlCqVAmQOhM5HSABUJe5RmU4NFJQR1nFSDdyHy4AAxHAnR4FM4UGAQED6fRAA)
<!-- prettier-ignore -->
```sh
--parser babel
```

**Input:**
<!-- prettier-ignore -->
```jsx
export default function()
// this is a function
{
  return 42
}
```

**Output:**
<!-- prettier-ignore -->
```jsx
TypeError: Cannot read properties of null (reading 'range')
    at O (https://prettier.io/lib/plugins/estree.js:10:13646)
    at https://prettier.io/lib/plugins/estree.js:13:5419
    at fu (https://prettier.io/lib/plugins/estree.js:13:5454)
    at https://prettier.io/lib/plugins/estree.js:13:769
    at Array.some (<anonymous>)
    at Eo (https://prettier.io/lib/plugins/estree.js:13:761)
    at bn (https://prettier.io/lib/standalone.js:30:2799)
    at Ht (https://prettier.io/lib/standalone.js:30:8022)
    at je (https://prettier.io/lib/standalone.js:30:7194)
    at Gn (https://prettier.io/lib/standalone.js:30:11017)
```

**Expected behavior:**


Found this during #15324


Issue discussion:

Solution patch:
diff --git a/changelog_unreleased/javascript/15326.md b/changelog_unreleased/javascript/15326.md
new file mode 100644
index 000000000000..1758d939d842
--- /dev/null
+++ b/changelog_unreleased/javascript/15326.md
@@ -0,0 +1,69 @@
+#### Fix comment between parentheses and function body (#15326 by @fisker)
+
+<!-- prettier-ignore -->
+```jsx
+// Input
+function function_declaration()
+// this is a function
+{
+  return 42
+}
+
+(function function_expression()
+// this is a function
+{
+  return 42
+})();
+
+// Prettier stable
+function function_declaration() {
+  // this is a function
+  return 42;
+}
+
+(function function_expression() // this is a function
+{
+  return 42;
+})();
+
+// Prettier main
+function function_declaration() {
+  // this is a function
+  return 42;
+}
+
+(function function_expression() {
+  // this is a function
+  return 42;
+})();
+```
+
+<!-- prettier-ignore -->
+```jsx
+// Input
+function function_declaration()
+// this is a function
+{
+  return 42
+}
+
+export default function()
+// this is a function
+{
+  return 42
+}
+
+// Prettier stable
+TypeError: Cannot read properties of null (reading 'range')
+
+// Prettier main
+function function_declaration() {
+  // this is a function
+  return 42;
+}
+
+export default function () {
+  // this is a function
+  return 42;
+}
+```
diff --git a/src/language-js/comments/handle-comments.js b/src/language-js/comments/handle-comments.js
index dfbdab0f6e0d..20bc53c16319 100644
--- a/src/language-js/comments/handle-comments.js
+++ b/src/language-js/comments/handle-comments.js
@@ -585,29 +585,20 @@ function handleLastFunctionArgComments({
     return true;
   }
 
+  // Comment between function parameters parentheses and function body
   if (
-    enclosingNode?.type === "FunctionDeclaration" &&
-    followingNode?.type === "BlockStatement"
+    !isBlockComment(comment) &&
+    (enclosingNode?.type === "FunctionDeclaration" ||
+      enclosingNode?.type === "FunctionExpression" ||
+      enclosingNode?.type === "ObjectMethod") &&
+    followingNode?.type === "BlockStatement" &&
+    enclosingNode.body === followingNode
   ) {
-    const functionParamRightParenIndex = (() => {
-      const parameters = getFunctionParameters(enclosingNode);
-      if (parameters.length > 0) {
-        return getNextNonSpaceNonCommentCharacterIndex(
-          text,
-          locEnd(parameters.at(-1)),
-        );
-      }
-      const functionParamLeftParenIndex =
-        getNextNonSpaceNonCommentCharacterIndex(text, locEnd(enclosingNode.id));
-      return (
-        functionParamLeftParenIndex !== false &&
-        getNextNonSpaceNonCommentCharacterIndex(
-          text,
-          functionParamLeftParenIndex + 1,
-        )
-      );
-    })();
-    if (locStart(comment) > functionParamRightParenIndex) {
+    const characterAfterCommentIndex = getNextNonSpaceNonCommentCharacterIndex(
+      text,
+      locEnd(comment),
+    );
+    if (characterAfterCommentIndex === locStart(followingNode)) {
       addBlockStatementFirstComment(followingNode, comment);
       return true;
     }


Test patch:
diff --git a/tests/format/js/comments/function/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/comments/function/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000000..c5de94d7b508
--- /dev/null
+++ b/tests/format/js/comments/function/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,78 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`between-parentheses-and-function-body.js format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+function function_declaration()
+// this is a function
+{
+  return 42
+}
+
+(function named()
+// this is a function
+{
+  return 42
+})();
+
+
+(function ()
+// this is a function
+{
+  return 42
+})();
+
+/* anonymous declaration */
+export default function ()
+// this is a function
+{
+  return 42
+}
+
+a = {
+  foo()
+  // this is a function
+  {},
+
+  bar: function()
+  // this is a function
+  {},
+}
+
+=====================================output=====================================
+function function_declaration() {
+  // this is a function
+  return 42;
+}
+
+(function named() {
+  // this is a function
+  return 42;
+})();
+
+(function () {
+  // this is a function
+  return 42;
+})();
+
+/* anonymous declaration */
+export default function () {
+  // this is a function
+  return 42;
+}
+
+a = {
+  foo() {
+    // this is a function
+  },
+
+  bar: function () {
+    // this is a function
+  },
+};
+
+================================================================================
+`;
diff --git a/tests/format/js/comments/function/between-parentheses-and-function-body.js b/tests/format/js/comments/function/between-parentheses-and-function-body.js
new file mode 100644
index 000000000000..c3553778f752
--- /dev/null
+++ b/tests/format/js/comments/function/between-parentheses-and-function-body.js
@@ -0,0 +1,35 @@
+function function_declaration()
+// this is a function
+{
+  return 42
+}
+
+(function named()
+// this is a function
+{
+  return 42
+})();
+
+
+(function ()
+// this is a function
+{
+  return 42
+})();
+
+/* anonymous declaration */
+export default function ()
+// this is a function
+{
+  return 42
+}
+
+a = {
+  foo()
+  // this is a function
+  {},
+
+  bar: function()
+  // this is a function
+  {},
+}
diff --git a/tests/format/js/comments/function/jsfmt.spec.js b/tests/format/js/comments/function/jsfmt.spec.js
new file mode 100644
index 000000000000..628d261a540d
--- /dev/null
+++ b/tests/format/js/comments/function/jsfmt.spec.js
@@ -0,0 +1,1 @@
+run_spec(import.meta, ["babel", "flow", "typescript"]);