Repository: jlongster/prettier (js)

Issue:
new is missing parenthesis
input

```js
new (memoize.Cache || MapCache);
new (typeof this == "function" ? this : Dict())
```

output

```js
new memoize.Cache || MapCache();
new typeof this == "function" ? this : Dict()();
```

https://jlongster.github.io/prettier/#%7B%22content%22%3A%22new%20(memoize.Cache%20%7C%7C%20MapCache)%3B%5Cnnew%20(typeof%20this%20%3D%3D%20%5C%22function%5C%22%20%3F%20this%20%3A%20Dict())%5Cn%22%2C%22options%22%3A%7B%22printWidth%22%3A80%2C%22tabWidth%22%3A2%2C%22singleQuote%22%3Afalse%2C%22trailingComma%22%3Afalse%2C%22bracketSpacing%22%3Atrue%7D%7D

Pull request:
Fix cases of missing parens with NewExpression
Fixes #227.

(Wasn't entirely sure on the test naming, happy to rename/move as needed)

Patch:
diff --git a/index.js b/index.js
index 1f7fed44d..546c60ac9 100644
--- a/index.js
+++ b/index.js
@@ -28,7 +28,11 @@ function format(text, opts) {
   let ast;
 
   if (opts.useFlowParser) {
-    ast = flowParser.parse(text);
+    ast = flowParser.parse(text, {
+      esproposal_class_instance_fields: true,
+      esproposal_class_static_fields: true,
+      esproposal_export_star_as: true,
+    });
     if (ast.errors.length > 0) {
       let msg = ast.errors[(0)].message + " on line " +
         ast.errors[(0)].loc.start.line;
diff --git a/src/fast-path.js b/src/fast-path.js
index bcc19b505..634bdda63 100644
--- a/src/fast-path.js
+++ b/src/fast-path.js
@@ -239,6 +239,7 @@ FPp.needsParens = function(assumeExpressionContext) {
     case "LogicalExpression":
       switch (parent.type) {
         case "CallExpression":
+        case "NewExpression":
           return name === "callee" && parent.callee === node;
 
         case "UnaryExpression":
@@ -340,6 +341,7 @@ FPp.needsParens = function(assumeExpressionContext) {
         case "SpreadProperty":
         case "BinaryExpression":
         case "LogicalExpression":
+        case "NewExpression":
           return true;
 
         case "CallExpression":
diff --git a/src/printer.js b/src/printer.js
index b2cd3d04e..4afe8c1f2 100644
--- a/src/printer.js
+++ b/src/printer.js
@@ -1870,6 +1870,8 @@ function printExportDeclaration(path, options, print) {
     }
 
     parts.push(";");
+  } else {
+    parts.push("{};");
   }
 
   return concat(parts);
diff --git a/src/util.js b/src/util.js
index bc51155ef..ae727036c 100644
--- a/src/util.js
+++ b/src/util.js
@@ -41,9 +41,7 @@ function expandLoc(parentNode, childNode) {
 }
 
 util.fixFaultyLocations = function(node, text) {
-  if (node.type === "TemplateLiteral") {
-    fixTemplateLiteral(node, text);
-  } else if (node.decorators) {
+  if (node.decorators) {
     // Expand the loc of the node responsible for printing the decorators
     // (here, the decorated node) so that it includes node.decorators.
     node.decorators.forEach(function(decorator) {
@@ -79,68 +77,6 @@ util.fixFaultyLocations = function(node, text) {
   }
 };
 
-function fixTemplateLiteral(node, text) {
-  assert.strictEqual(node.type, "TemplateLiteral");
-
-  if (node.quasis.length === 0) {
-    // If there are no quasi elements, then there is nothing to fix.
-    return;
-  }
-
-  // First we need to exclude the opening ` from the loc of the first
-  // quasi element, in case the parser accidentally decided to include it.
-  var afterLeftBackTickPos = locStart(node);
-  assert.strictEqual(text.charAt(afterLeftBackTickPos), "`");
-  assert.ok(afterLeftBackTickPos < text.length);
-  var firstQuasi = node.quasis[(0)];
-  if (locStart(firstQuasi) - afterLeftBackTickPos < 0) {
-    setLocStart(firstQuasi, afterLeftBackTickPos);
-  }
-
-  // Next we need to exclude the closing ` from the loc of the last quasi
-  // element, in case the parser accidentally decided to include it.
-  var rightBackTickPos = locEnd(node);
-  assert.ok(rightBackTickPos >= 0);
-  assert.strictEqual(text.charAt(rightBackTickPos), "`");
-  var lastQuasi = node.quasis[node.quasis.length - 1];
-  if (rightBackTickPos - locEnd(lastQuasi) < 0) {
-    setLocEnd(locEnd(lastQuasi), rightBackTickPos);
-  }
-
-  // Now we need to exclude ${ and } characters from the loc's of all
-  // quasi elements, since some parsers accidentally include them.
-  node.expressions.forEach(function(expr, i) {
-    // Rewind from the start loc over any whitespace and the ${ that
-    // precedes the expression. The position of the $ should be the
-    // same as the end of the preceding quasi element, but some
-    // parsers accidentally include the ${ in the loc of the quasi
-    // element.
-    var dollarCurlyPos = skipSpaces(text, locStart(expr) - 1, true);
-    if (
-      dollarCurlyPos - 1 >= 0 && text.charAt(dollarCurlyPos - 1) === "{" &&
-        dollarCurlyPos - 2 >= 0 &&
-        text.charAt(dollarCurlyPos - 2) === "$"
-    ) {
-      var quasiBefore = node.quasis[i];
-      if (dollarCurlyPos - locEnd(quasiBefore) < 0) {
-        setLocEnd(quasiBefore, dollarCurlyPos);
-      }
-    }
-
-    // Likewise, some parsers accidentally include the } that follows
-    // the expression in the loc of the following quasi element.
-    var rightCurlyPos = skipSpaces(text, locEnd(expr));
-    if (text.charAt(rightCurlyPos) === "}") {
-      assert.ok(rightCurlyPos + 1 < text.length);
-      // Now rightCurlyPos is technically the position just after the }.
-      var quasiAfter = node.quasis[i + 1];
-      if (locStart(quasiAfter) - rightCurlyPos < 0) {
-        setLocStart(locStart(quasiAfter), rightCurlyPos);
-      }
-    }
-  });
-}
-
 util.isExportDeclaration = function(node) {
   if (node)
     switch (node.type) {


Test patch:
diff --git a/tests/export/__snapshots__/jsfmt.spec.js.snap b/tests/export/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000..0b506fe66
--- /dev/null
+++ b/tests/export/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,6 @@
+exports[`test empty.js 1`] = `
+"export {};
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+export {};
+"
+`;
diff --git a/tests/export/empty.js b/tests/export/empty.js
new file mode 100644
index 000000000..cb0ff5c3b
--- /dev/null
+++ b/tests/export/empty.js
@@ -0,0 +1,1 @@
+export {};
diff --git a/tests/export/jsfmt.spec.js b/tests/export/jsfmt.spec.js
new file mode 100644
index 000000000..989047bcc
--- /dev/null
+++ b/tests/export/jsfmt.spec.js
@@ -0,0 +1,1 @@
+run_spec(__dirname);
diff --git a/tests/new_expression/__snapshots__/jsfmt.spec.js.snap b/tests/new_expression/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000..b59887491
--- /dev/null
+++ b/tests/new_expression/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,8 @@
+exports[`test new_expression.js 1`] = `
+"new (memoize.Cache || MapCache)
+new (typeof this == \"function\" ? this : Dict())
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+new (memoize.Cache || MapCache)();
+new (typeof this == \"function\" ? this : Dict())();
+"
+`;
diff --git a/tests/new_expression/jsfmt.spec.js b/tests/new_expression/jsfmt.spec.js
new file mode 100644
index 000000000..989047bcc
--- /dev/null
+++ b/tests/new_expression/jsfmt.spec.js
@@ -0,0 +1,1 @@
+run_spec(__dirname);
diff --git a/tests/new_expression/new_expression.js b/tests/new_expression/new_expression.js
new file mode 100644
index 000000000..23166585a
--- /dev/null
+++ b/tests/new_expression/new_expression.js
@@ -0,0 +1,2 @@
+new (memoize.Cache || MapCache)
+new (typeof this == "function" ? this : Dict())
diff --git a/tests/template/__snapshots__/jsfmt.spec.js.snap b/tests/template/__snapshots__/jsfmt.spec.js.snap
index 55258e7bd..70c3fb59a 100644
--- a/tests/template/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/template/__snapshots__/jsfmt.spec.js.snap
@@ -1,3 +1,32 @@
+exports[`test faulty-locations.js 1`] = `
+"var o = {
+  [\`key\`]: () => {
+    // Comment
+  }
+};
+
+var x = {
+  y: () => Relay.QL\`
+    query {
+      \${foo},
+      field,
+    }
+  \`
+};
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+var o = { [\`key\`]: () => {} };
+
+var x = {
+  y: () => Relay.QL\`
+    query {
+      \${foo},
+      field,
+    }
+  \`
+};
+"
+`;
+
 exports[`test template.js 1`] = `
 "/* @flow */
 
diff --git a/tests/template/faulty-locations.js b/tests/template/faulty-locations.js
new file mode 100644
index 000000000..15a8559c0
--- /dev/null
+++ b/tests/template/faulty-locations.js
@@ -0,0 +1,14 @@
+var o = {
+  [`key`]: () => {
+    // Comment
+  }
+};
+
+var x = {
+  y: () => Relay.QL`
+    query {
+      ${foo},
+      field,
+    }
+  `
+};
diff --git a/tests_config/run_spec.js b/tests_config/run_spec.js
index 9be8940fe..78ed9ca07 100644
--- a/tests_config/run_spec.js
+++ b/tests_config/run_spec.js
@@ -76,7 +76,11 @@ function stripLocation(ast) {
 
 function parse(string) {
   const flowParser = require('flow-parser');
-  return stripLocation(flowParser.parse(string));
+  return stripLocation(flowParser.parse(string, {
+    esproposal_class_instance_fields: true,
+    esproposal_class_static_fields: true,
+    esproposal_export_star_as: true,
+  }));
 }
 
 function prettyprint(src, filename, options) {