Repository: prettier/prettier (JavaScript)

Issue:
Using member access expressions (`@(array[0])`) in decorators produces incorrect output
**Prettier 2.8.3**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAucAbAhgZ0wAgApwBOm0OwAOlDjgAIAUAtgJ4Aickh6MEhAgoZyYBtAAwBdAJSVqAc0Jw4MOhLIBfSuqggANCAgAHGAEtomZKHQCIAdzyWEZlOlTX0TM7oBGnMAGtFAMroDHAAMkZQcMgAZs6YcF4+-jAB+uhgETLIMIQArgkgcAyecAAmpWWh6FAyuegycABiPAxcxjXIIOi53DogABYwDKgA6v1G8JhpYHABDhNGAG4TTJ1g2H0R8YQweJwyrTFxBQBWmAAeAZmocACKuRDwR6jxumnERJ0wTPpwmGCEIyGPr6QGwEZGUowfrIAAcIjehAg8RGnH0nVBfyIiyiugAjg94HsDI4upgALSRMplPryAlGeR7eqHJCxF4FeIMIzZPIc65wXgwHJGTw9OAEQjhSLPV4gTD8+6PKKs466GDoTwQqEwpAAJjVnCMqEyAGEIAwWYVMABWPq5eIAFQ1jjZssW+QAklAKrAAgCgTBeN6At8bjK4KpVEA)

<!-- prettier-ignore -->
```sh
--parser typescript
```

**Input:**

<!-- prettier-ignore -->
```tsx
class Person {
  @(myDecoratorArray[0])
  greet() {}
}

```

**Output:**

<!-- prettier-ignore -->
```tsx
class Person {
  @myDecoratorArray[0]
  greet() {}
}

```

**Why is this wrong?**

In the original script, the `Person` class has a single member: a `greet` method decorated with `myDecoratorArray[0]`.

The output has very different code. The `Person` class now has two members:

- a `greet` method with _zero_ decorators, and
- a computed property (`0`) decorated with `myDecoratorArray`.

**Expected behavior:**

The output should be the same as the input, with the decorator properly enclosed in parentheses.


Hints:

Patch:
diff --git a/src/language-js/needs-parens.js b/src/language-js/needs-parens.js
--- a/src/language-js/needs-parens.js
+++ b/src/language-js/needs-parens.js
@@ -187,6 +187,10 @@ function needsParens(path, options) {
     }
     case "Decorator": {
       if (name === "expression") {
+        if (isMemberExpression(node) && node.computed) {
+          return true;
+        }
+
         let hasCallExpression = false;
         let hasMemberExpression = false;
         let current = node;


Test patch:
diff --git a/tests/config/format-test.js b/tests/config/format-test.js
--- a/tests/config/format-test.js
+++ b/tests/config/format-test.js
@@ -82,6 +82,8 @@ const meriyahDisabledTests = new Set([
     __dirname,
     "../format/js/babel-plugins/decorator-auto-accessors.js"
   ),
+  // Parsing to different ASTs
+  path.join(__dirname, "../format/js/decorators/member-expression.js"),
 ]);
 
 const isUnstable = (filename, options) => {
diff --git a/tests/format/js/decorators/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/decorators/__snapshots__/jsfmt.spec.js.snap
--- a/tests/format/js/decorators/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/format/js/decorators/__snapshots__/jsfmt.spec.js.snap
@@ -162,6 +162,144 @@ export class Bar {}
 ================================================================================
 `;
 
+exports[`member-expression.js [acorn] format 1`] = `
+"Unexpected character '@' (3:5)
+  1 | [
+  2 |   class {
+> 3 |     @(decorators[0])
+    |     ^
+  4 |     method() {}
+  5 |   },
+  6 |   class {"
+`;
+
+exports[`member-expression.js [espree] format 1`] = `
+"Unexpected character '@' (3:5)
+  1 | [
+  2 |   class {
+> 3 |     @(decorators[0])
+    |     ^
+  4 |     method() {}
+  5 |   },
+  6 |   class {"
+`;
+
+exports[`member-expression.js format 1`] = `
+====================================options=====================================
+parsers: ["babel"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+[
+  class {
+    @(decorators[0])
+    method() {}
+  },
+  class {
+    @decorators[0]
+    method() {}
+  },
+  class {
+    @(decorators?.[0])
+    method() {}
+  },
+  class {
+    @(decorators.at(0))
+    method() {}
+  },
+  class {
+    @(decorators?.at(0))
+    method() {}
+  },
+  class {
+    @(decorators.first)
+    method() {}
+  },
+  class {
+    @(decorators?.first)
+    method() {}
+  },
+  class {
+    @(decorators[first])
+    method() {}
+  },
+  class {
+    @decorators[first]
+    method() {}
+  },
+  class {
+    @(decorators["first"])
+    method() {}
+  },
+  @(decorators[first])
+  class {
+    method() {}
+  },
+  @(decorators[0])
+  class {
+    method() {}
+  },
+]
+
+=====================================output=====================================
+[
+  class {
+    @(decorators[0])
+    method() {}
+  },
+  class {
+    @decorators [0];
+    method() {}
+  },
+  class {
+    @(decorators?.[0])
+    method() {}
+  },
+  class {
+    @decorators.at(0)
+    method() {}
+  },
+  class {
+    @(decorators?.at(0))
+    method() {}
+  },
+  class {
+    @decorators.first
+    method() {}
+  },
+  class {
+    @(decorators?.first)
+    method() {}
+  },
+  class {
+    @(decorators[first])
+    method() {}
+  },
+  class {
+    @decorators [first];
+    method() {}
+  },
+  class {
+    @(decorators["first"])
+    method() {}
+  },
+  (
+    @(decorators[first])
+    class {
+      method() {}
+    }
+  ),
+  (
+    @(decorators[0])
+    class {
+      method() {}
+    }
+  ),
+];
+
+================================================================================
+`;
+
 exports[`methods.js [acorn] format 1`] = `
 "Unexpected character '@' (3:3)
   1 |
diff --git a/tests/format/js/decorators/member-expression.js b/tests/format/js/decorators/member-expression.js
new file mode 100644
--- /dev/null
+++ b/tests/format/js/decorators/member-expression.js
@@ -0,0 +1,50 @@
+[
+  class {
+    @(decorators[0])
+    method() {}
+  },
+  class {
+    @decorators[0]
+    method() {}
+  },
+  class {
+    @(decorators?.[0])
+    method() {}
+  },
+  class {
+    @(decorators.at(0))
+    method() {}
+  },
+  class {
+    @(decorators?.at(0))
+    method() {}
+  },
+  class {
+    @(decorators.first)
+    method() {}
+  },
+  class {
+    @(decorators?.first)
+    method() {}
+  },
+  class {
+    @(decorators[first])
+    method() {}
+  },
+  class {
+    @decorators[first]
+    method() {}
+  },
+  class {
+    @(decorators["first"])
+    method() {}
+  },
+  @(decorators[first])
+  class {
+    method() {}
+  },
+  @(decorators[0])
+  class {
+    method() {}
+  },
+]