Repository: prettier/prettier (JavaScript)
Date: 2019-11-19 13:15:18+00:00
Task type: Bug Fix

Issue:
Logical operators & nullish coalescing break strangely in template string expressions
<!--

BEFORE SUBMITTING AN ISSUE:

1.  Search for your issue on GitHub: https://github.com/prettier/prettier/issues
    A large number of opened issues are duplicates of existing issues.
    If someone has already opened an issue for what you are experiencing,
    you do not need to open a new issue — please add a 👍 reaction to the
    existing issue instead.

2.  We get a lot of requests for adding options, but Prettier is
    built on the principle of being opinionated about code formatting.
    This means we have a very high bar for adding new options.
    Find out more: https://prettier.io/docs/en/option-philosophy.html

Tip! Don't write this stuff manually.

1. Go to https://prettier.io/playground
2. Paste your code and set options
3. Press the "Report issue" button in the lower right

-->

Hi! :wave:

**Prettier 1.19.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEB6VACKBXANrgSwGcALDSAQ1ziLAKgHMAaDXCBgsKjCABzgBOFGBAFEWFKABMMAIwgRqknvyEixcuCQoA3OBhgl9RCgFs4AHSgADAIz2HDjACZXbtxgDM3nz4wAWQKCgjABWcIiIjAA2WLi4jAB2ZJSUjAASYFsMAH4clwBfawBuKztHRxd3dy9fXwDg4LDIyJj4+KTU1IysjAAyPsKSsorK6pq6+samltb2jq7uzOyAHxWh0ptRp3GPSb9pkNmo+YTFtOWMdecizat0A0EoCgEATwxebFIaAyMMAHcSIp9HAAB68AQ0IgEaAGCAYChYOD-Vj0SxbbbZXauWr7BqHZrHNqnTrnHrZfLZJAbKwgJggPgwGFQIjIUAvAQQf4ABReCFZKF0EAIUjpIFkQjAAGs4DAAMq8Ch0RjIGACbBwekkGCmXAAdRIBHgREVYDgcv5RoIOiNr2Q4CIrPp9CIghg3KEDFMFGQADMqK76QArIiggBCkpl8rMcAAMmi-QHNSAQ6C5fQGNQAIrYCDwRO4QMgRViQT22QUWRwXBiiH0GB6kWGZAADgADPSIRBXXqhLx7RCaII9GKAI65+AevgCkAUIgAWigcDgUhXYsh44IkI9FC9PqQ-sLyddpgIqvVx4z2YncALRZglcbUmbSGc9LVFAIhEYAGEIKZvXtKBoFvekvjgAAVSsBUPIsdA1ABJaQEHlMABAIXgYAAQWkOUYFeag7zgAoCiAA)

This happens with both the Babel and TypeScript parsers.

**Input:**
```jsx
// nullish coalescing, logical operators, and boolean operators behave the same
`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${1 ?? 2}`;
`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${1 && 2}`;
`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${1 || 2}`;
`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${1 | 2}`;

// ternary pushes the whole expression to a new line
`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${1 ? 1 : 2}`;
```

**Output:**
```jsx
// nullish coalescing, logical operators, and boolean operators behave the same
`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${1 ??
  2}`;
`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${1 &&
  2}`;
`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${1 ||
  2}`;
`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${1 |
  2}`;

// ternary pushes the whole expression to a new line
`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${
  1 ? 1 : 2
}`;
```

**Expected behavior:**

I think the non-ternary cases look pretty ugly. I would expect all of these statements to mimic the behaviour of the ternary expression case, yielding this output:

```jsx
// nullish coalescing, logical operators, and boolean operators behave the same
`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${
  1 ?? 2
}`;
`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${
  1 && 2
}`;
`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${
  1 || 2
}`;
`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${
  1 | 2
}`;

// ternary pushes the whole expression to a new line
`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${
  1 ? 1 : 2
}`;
```


Issue discussion:
i think it makes sense
Sometimes breaking like this:

```js
`abc ${...
  ...}`
```

…and sometimes like this:

```js
`abc ${
  ...
}`
```

…sounds like a bug to me. We should always print like the second example.
@lydell :+1: Let's mark as bug
Feel free to send a PR with fix
Cool! I have no idea how to fix this, I'm afraid :sweat_smile: 

Solution patch:
diff --git a/changelog_unreleased/javascript/pr-7003.md b/changelog_unreleased/javascript/pr-7003.md
new file mode 100644
index 000000000000..73563471a0c4
--- /dev/null
+++ b/changelog_unreleased/javascript/pr-7003.md
@@ -0,0 +1,28 @@
+#### Fix formatting of logical, binary and sequence expressions in template literals ([#7010](https://github.com/prettier/prettier/pull/7010) by [@evilebottnawi](https://github.com/evilebottnawi))
+
+<!-- prettier-ignore -->
+```js
+// Input
+`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${foo || bar}`;
+`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${foo | bar}`;
+`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${(foo, bar)}`;
+
+// Prettier stable
+`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${foo ||
+  bar}`;
+`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${foo |
+  bar}`;
+`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${(foo,
+bar)}`;
+
+// Prettier master
+`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${
+  foo || bar
+}`;
+`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${
+  foo | bar
+}`;
+`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${
+  (foo, bar)
+}`;
+```
diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js
index df2dea511e8a..c1f876f04cf4 100644
--- a/src/language-js/printer-estree.js
+++ b/src/language-js/printer-estree.js
@@ -609,7 +609,8 @@ function printPathNoParens(path, options, print, args) {
         (parent.type === "ConditionalExpression" &&
           parentParent.type !== "ReturnStatement" &&
           parentParent.type !== "CallExpression" &&
-          parentParent.type !== "OptionalCallExpression");
+          parentParent.type !== "OptionalCallExpression") ||
+        parent.type === "TemplateLiteral";
 
       const shouldIndentIfInlining =
         parent.type === "AssignmentExpression" ||
@@ -2481,7 +2482,11 @@ function printPathNoParens(path, options, print, args) {
               (n.expressions[i].comments && n.expressions[i].comments.length) ||
               n.expressions[i].type === "MemberExpression" ||
               n.expressions[i].type === "OptionalMemberExpression" ||
-              n.expressions[i].type === "ConditionalExpression"
+              n.expressions[i].type === "ConditionalExpression" ||
+              n.expressions[i].type === "LogicalExpression" ||
+              n.expressions[i].type === "BinaryExpression" ||
+              n.expressions[i].type === "SequenceExpression" ||
+              n.expressions[i].type === "TSAsExpression"
             ) {
               printed = concat([indent(concat([softline, printed])), softline]);
             }


Test patch:
diff --git a/tests/line_suffix_boundary/__snapshots__/jsfmt.spec.js.snap b/tests/line_suffix_boundary/__snapshots__/jsfmt.spec.js.snap
index 583f31c1d3f7..475d1d817e90 100644
--- a/tests/line_suffix_boundary/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/line_suffix_boundary/__snapshots__/jsfmt.spec.js.snap
@@ -32,7 +32,8 @@ ExampleStory.getFragment('story')}
 </div>;
 
 =====================================output=====================================
-\`\${a + a // a
+\`\${
+  a + a // a
 }
 
 \${
diff --git a/tests/strings/__snapshots__/jsfmt.spec.js.snap b/tests/strings/__snapshots__/jsfmt.spec.js.snap
index ff176f2b37d0..d3cdd404a8f7 100644
--- a/tests/strings/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/strings/__snapshots__/jsfmt.spec.js.snap
@@ -191,26 +191,13 @@ const Bar = styled.div\`
 
 =====================================output=====================================
 foo(
-  \`a long string \${1 +
-    2 +
-    3 +
-    2 +
-    3 +
-    2 +
-    3 +
-    2 +
-    3 +
-    2 +
-    3 +
-    2 +
-    3 +
-    2 +
-    3 +
-    2 +
-    3} with expr\`
+  \`a long string \${
+    1 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3
+  } with expr\`
 );
 
-const x = \`a long string \${1 +
+const x = \`a long string \${
+  1 +
   2 +
   3 +
   2 +
@@ -229,10 +216,12 @@ const x = \`a long string \${1 +
   2 +
   3 +
   2 +
-  3} with expr\`;
+  3
+} with expr\`;
 
 foo(
-  \`a long string \${1 +
+  \`a long string \${
+    1 +
     2 +
     3 +
     2 +
@@ -253,7 +242,8 @@ foo(
     2 +
     3 +
     2 +
-    3} with expr\`
+    3
+  } with expr\`
 );
 
 pipe.write(
@@ -385,26 +375,13 @@ const Bar = styled.div\`
 
 =====================================output=====================================
 foo(
-  \`a long string \${1 +
-    2 +
-    3 +
-    2 +
-    3 +
-    2 +
-    3 +
-    2 +
-    3 +
-    2 +
-    3 +
-    2 +
-    3 +
-    2 +
-    3 +
-    2 +
-    3} with expr\`,
+  \`a long string \${
+    1 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3
+  } with expr\`,
 );
 
-const x = \`a long string \${1 +
+const x = \`a long string \${
+  1 +
   2 +
   3 +
   2 +
@@ -423,10 +400,12 @@ const x = \`a long string \${1 +
   2 +
   3 +
   2 +
-  3} with expr\`;
+  3
+} with expr\`;
 
 foo(
-  \`a long string \${1 +
+  \`a long string \${
+    1 +
     2 +
     3 +
     2 +
@@ -447,7 +426,8 @@ foo(
     2 +
     3 +
     2 +
-    3} with expr\`,
+    3
+  } with expr\`,
 );
 
 pipe.write(
diff --git a/tests/template_literals/__snapshots__/jsfmt.spec.js.snap b/tests/template_literals/__snapshots__/jsfmt.spec.js.snap
index 4768cc0d2c0f..812b09b5134b 100644
--- a/tests/template_literals/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/template_literals/__snapshots__/jsfmt.spec.js.snap
@@ -1,5 +1,45 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`binary-exporessions.js 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+\`111111111 222222222 333333333 444444444 555555555 666666666 777777777 \${1 |
+2}\`;
+\`111111111 222222222 333333333 444444444 555555555 666666666 777777777 \${1 &
+2}\`;
+
+=====================================output=====================================
+\`111111111 222222222 333333333 444444444 555555555 666666666 777777777 \${
+  1 | 2
+}\`;
+\`111111111 222222222 333333333 444444444 555555555 666666666 777777777 \${
+  1 & 2
+}\`;
+
+================================================================================
+`;
+
+exports[`conditional-expressions.js 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+\`111111111 222222222 333333333 444444444 555555555 666666666 777777777 \${
+  1 ? 1 : 2
+}\`;
+
+=====================================output=====================================
+\`111111111 222222222 333333333 444444444 555555555 666666666 777777777 \${
+  1 ? 1 : 2
+}\`;
+
+================================================================================
+`;
+
 exports[`css-prop.js 1`] = `
 ====================================options=====================================
 parsers: ["babel", "flow", "typescript"]
@@ -191,6 +231,49 @@ throw new Error(
 ================================================================================
 `;
 
+exports[`logical-expressions.js 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+\`111111111 222222222 333333333 444444444 555555555 666666666 777777777 \${1 ??
+2}\`;
+\`111111111 222222222 333333333 444444444 555555555 666666666 777777777 \${1 &&
+2}\`;
+\`111111111 222222222 333333333 444444444 555555555 666666666 777777777 \${1 ||
+2}\`;
+
+=====================================output=====================================
+\`111111111 222222222 333333333 444444444 555555555 666666666 777777777 \${
+  1 ?? 2
+}\`;
+\`111111111 222222222 333333333 444444444 555555555 666666666 777777777 \${
+  1 && 2
+}\`;
+\`111111111 222222222 333333333 444444444 555555555 666666666 777777777 \${
+  1 || 2
+}\`;
+
+================================================================================
+`;
+
+exports[`sequence-expressions.js 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+\`111111111 222222222 333333333 444444444 555555555 666666666 777777777 \${(1, 2)}\`;
+
+=====================================output=====================================
+\`111111111 222222222 333333333 444444444 555555555 666666666 777777777 \${
+  (1, 2)
+}\`;
+
+================================================================================
+`;
+
 exports[`styled-components-with-expressions.js 1`] = `
 ====================================options=====================================
 parsers: ["babel", "flow", "typescript"]
diff --git a/tests/template_literals/binary-exporessions.js b/tests/template_literals/binary-exporessions.js
new file mode 100644
index 000000000000..7507623e0a63
--- /dev/null
+++ b/tests/template_literals/binary-exporessions.js
@@ -0,0 +1,4 @@
+`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${1 |
+2}`;
+`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${1 &
+2}`;
diff --git a/tests/template_literals/conditional-expressions.js b/tests/template_literals/conditional-expressions.js
new file mode 100644
index 000000000000..5da7c3bd5635
--- /dev/null
+++ b/tests/template_literals/conditional-expressions.js
@@ -0,0 +1,3 @@
+`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${
+  1 ? 1 : 2
+}`;
diff --git a/tests/template_literals/logical-expressions.js b/tests/template_literals/logical-expressions.js
new file mode 100644
index 000000000000..f2964d8f15c7
--- /dev/null
+++ b/tests/template_literals/logical-expressions.js
@@ -0,0 +1,6 @@
+`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${1 ??
+2}`;
+`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${1 &&
+2}`;
+`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${1 ||
+2}`;
diff --git a/tests/template_literals/sequence-expressions.js b/tests/template_literals/sequence-expressions.js
new file mode 100644
index 000000000000..ab20d4972893
--- /dev/null
+++ b/tests/template_literals/sequence-expressions.js
@@ -0,0 +1,1 @@
+`111111111 222222222 333333333 444444444 555555555 666666666 777777777 ${(1, 2)}`;
diff --git a/tests/typescript_template_literals/__snapshots__/jsfmt.spec.js.snap b/tests/typescript_template_literals/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000000..abb2f54740fc
--- /dev/null
+++ b/tests/typescript_template_literals/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,32 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`as-expression.ts 1`] = `
+====================================options=====================================
+parsers: ["typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+const a = \`\${(foo + bar) as baz}\`;
+const b = \`\${(veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongFoo + bar) as baz}\`;
+const b = \`\${(foo + veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongBar) as baz}\`;
+const b = \`\${(foo + bar) as veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongBaz}\`;
+const b = \`\${(veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongFoo + veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongBar) as veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongBaz}\`;
+
+=====================================output=====================================
+const a = \`\${(foo + bar) as baz}\`;
+const b = \`\${
+  (veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongFoo + bar) as baz
+}\`;
+const b = \`\${
+  (foo + veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongBar) as baz
+}\`;
+const b = \`\${
+  (foo + bar) as veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongBaz
+}\`;
+const b = \`\${
+  (veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongFoo +
+    veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongBar) as veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongBaz
+}\`;
+
+================================================================================
+`;
diff --git a/tests/typescript_template_literals/as-expression.ts b/tests/typescript_template_literals/as-expression.ts
new file mode 100644
index 000000000000..3c2edeff8190
--- /dev/null
+++ b/tests/typescript_template_literals/as-expression.ts
@@ -0,0 +1,5 @@
+const a = `${(foo + bar) as baz}`;
+const b = `${(veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongFoo + bar) as baz}`;
+const b = `${(foo + veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongBar) as baz}`;
+const b = `${(foo + bar) as veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongBaz}`;
+const b = `${(veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongFoo + veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongBar) as veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongBaz}`;
diff --git a/tests/typescript_template_literals/jsfmt.spec.js b/tests/typescript_template_literals/jsfmt.spec.js
new file mode 100644
index 000000000000..2ea3bb6eb2e4
--- /dev/null
+++ b/tests/typescript_template_literals/jsfmt.spec.js
@@ -0,0 +1,1 @@
+run_spec(__dirname, ["typescript"]);