Repository: jlongster/prettier (js)

Issue:
Missing parenthesis for class expression inside ternary
```js
if (1) (class {} ? 1 : 2)
```

outputs

```js
if (1) class {} ? 1 : 2;
```

which is invalid.

https://jlongster.github.io/prettier/#%7B%22content%22%3A%22if%20(1)%20(class%20%7B%7D%20%3F%201%20%3A%202)%22%2C%22options%22%3A%7B%22printWidth%22%3A80%2C%22tabWidth%22%3A2%2C%22singleQuote%22%3Afalse%2C%22trailingComma%22%3Afalse%2C%22bracketSpacing%22%3Afalse%2C%22doc%22%3Afalse%7D%7D

We should probably always put parenthesis around class expressions if they are left of a ternary.

Pull request description:
Add parenthesis when class expressions are left of a ternary
Fixes #426

Patch:
diff --git a/src/fast-path.js b/src/fast-path.js
index 36e0cc1db..35bbd28de 100644
--- a/src/fast-path.js
+++ b/src/fast-path.js
@@ -452,6 +452,10 @@ FPp.needsParens = function(assumeExpressionContext) {
           }
         case "MemberExpression":
           return name === "object" && parent.object === node;
+        case "ConditionalExpression":
+          if (parent.test === node) {
+            return true;
+          }
       }
 
       return false;
diff --git a/src/printer.js b/src/printer.js
index c7c2873ef..cae7878ae 100644
--- a/src/printer.js
+++ b/src/printer.js
@@ -609,12 +609,24 @@ function genericPrintNoParens(path, options, print) {
     case "Decorator":
       return concat([ "@", path.call(print, "expression") ]);
     case "ArrayExpression":
-    case "ArrayPattern":
+    case "ArrayPattern": {
       if (n.elements.length === 0) {
         parts.push("[]");
       } else {
         const lastElem = util.getLast(n.elements);
-        const canHaveTrailingComma = !(lastElem && lastElem.type === "RestElement");
+        let canHaveTrailingComma = !(lastElem && lastElem.type === "RestElement");
+
+        // There's a bug with flow 0.38 where trailing commas are triggering
+        // parse errors for array patterns in arguments of a function
+        const parent = path.getParentNode(0);
+        if (options.parser === 'flow' &&
+          n.type === "ArrayPattern" &&
+          parent && (
+          parent.type === 'FunctionExpression' ||
+          parent.type === 'FunctionDeclaration' ||
+          parent.type === 'ArrowFunctionExpression')) {
+          canHaveTrailingComma = false;
+        }
 
         // JavaScript allows you to have empty elements in an array which
         // changes its length based on the number of commas. The algorithm
@@ -655,6 +667,7 @@ function genericPrintNoParens(path, options, print) {
       if (n.typeAnnotation) parts.push(path.call(print, "typeAnnotation"));
 
       return concat(parts);
+    }
     case "SequenceExpression":
       return join(", ", path.map(print, "expressions"));
     case "ThisExpression":


Test patch:
diff --git a/tests/array_trailing_comma/__snapshots__/jsfmt.spec.js.snap b/tests/array_trailing_comma/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000..5171c2359
--- /dev/null
+++ b/tests/array_trailing_comma/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,69 @@
+exports[`test trailing_comma.js 1`] = `
+"f = ([s=1]) => 1;
+function f([s=1]){};
+f = function([s=1]){};
+class c {a([s=1]){}}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+f = (
+  [
+    s = 1
+  ],
+) =>
+  1;
+function f(
+  [
+    s = 1
+  ],
+) {
+}
+f = function(
+  [
+    s = 1
+  ],
+) {
+};
+class c {
+  a(
+    [
+      s = 1
+    ],
+  ) {
+  }
+}
+"
+`;
+
+exports[`test trailing_comma.js 2`] = `
+"f = ([s=1]) => 1;
+function f([s=1]){};
+f = function([s=1]){};
+class c {a([s=1]){}}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+f = (
+  [
+    s = 1,
+  ],
+) =>
+  1;
+function f(
+  [
+    s = 1,
+  ],
+) {
+}
+f = function(
+  [
+    s = 1,
+  ],
+) {
+};
+class c {
+  a(
+    [
+      s = 1,
+    ],
+  ) {
+  }
+}
+"
+`;
diff --git a/tests/array_trailing_comma/jsfmt.spec.js b/tests/array_trailing_comma/jsfmt.spec.js
new file mode 100644
index 000000000..cfda4f52c
--- /dev/null
+++ b/tests/array_trailing_comma/jsfmt.spec.js
@@ -0,0 +1,2 @@
+run_spec(__dirname, {printWidth: 1, trailingComma: true});
+run_spec(__dirname, {printWidth: 1, trailingComma: true, parser: 'babylon'});
diff --git a/tests/array_trailing_comma/trailing_comma.js b/tests/array_trailing_comma/trailing_comma.js
new file mode 100644
index 000000000..5b953b32f
--- /dev/null
+++ b/tests/array_trailing_comma/trailing_comma.js
@@ -0,0 +1,4 @@
+f = ([s=1]) => 1;
+function f([s=1]){};
+f = function([s=1]){};
+class c {a([s=1]){}}
diff --git a/tests/classes/__snapshots__/jsfmt.spec.js.snap b/tests/classes/__snapshots__/jsfmt.spec.js.snap
index c3208a09b..28a1e1e84 100644
--- a/tests/classes/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/classes/__snapshots__/jsfmt.spec.js.snap
@@ -305,3 +305,10 @@ declare var o: { p: number };
 (o: Class<C>); // error, object type incompatible with class type
 "
 `;
+
+exports[`test ternary.js 1`] = `
+"if (1) (class {}) ? 1 : 2;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+if (1) (class {}) ? 1 : 2;
+"
+`;
diff --git a/tests/classes/ternary.js b/tests/classes/ternary.js
new file mode 100644
index 000000000..3d6727056
--- /dev/null
+++ b/tests/classes/ternary.js
@@ -0,0 +1,1 @@
+if (1) (class {}) ? 1 : 2;