Repository: jlongster/prettier (js)

Issue:
Wrapping algorithm for callback arrow functions adds a comma to the arguments list
> Version: 0.0.5 (npm@latest); also 0.0.4, and prettier-browser.

Example: ([prettier-browser](https://vjeux.github.io/prettier-browser/#%7B%22content%22%3A%22func('a%20short%20name'%2C%20()%20%3D%3E%20%7B%5Cn%20%20return%20true%3B%5Cn%7D)%5Cn%5Cnfunc('a%20very%20very%20very%20very%20long%20long%20long%20name%20that%20exceeds%20the%20width%20limit'%2C%20()%20%3D%3E%20%7B%5Cn%20%20return%20true%3B%5Cn%7D)%5Cn%5Cnfunc('a%20very%20very%20very%20very%20long%20long%20long%20name%20that%20exceeds%20the%20width%20limit'%2C%20function()%20%7B%5Cn%20%20return%20true%3B%5Cn%7D)%5Cn%22%2C%22options%22%3A%7B%22printWidth%22%3A80%2C%22tabWidth%22%3A2%2C%22useFlowParser%22%3Afalse%2C%22singleQuote%22%3Atrue%2C%22trailingComma%22%3Atrue%2C%22bracketSpacing%22%3Atrue%7D%7D))

```js
func('a very very very very long long long name that exceeds the width limit', () => {
  return true;
})

// becomes 

func('a very very very very long long long name that exceeds the width limit', (
  ,
) => 
  {
    return true;
  });
```

Where did the comma in the arguments list come from?

It doesn't happen with shorter functions that _don't_ exceed the line length limit, nor with a `function` callback.

```js
// input
func('a short name', () => {
  return true;
})

// output
func('a short name', () => {
  return true;
});
```

```js
// input
func('a very very very very long long long name that exceeds the width limit', function() {
  return true;
})

// output
func(
  'a very very very very long long long name that exceeds the width limit',
  function() {
    return true;
  },
);
```

> Side-note: The output for the incorrect function causes `prettier` to throw an error: [prettier-browser](https://vjeux.github.io/prettier-browser/#%7B%22content%22%3A%22%5Cnfunc('a%20very%20very%20very%20very%20long%20long%20long%20name%20that%20exceeds%20the%20width%20limit'%2C%20(%5Cn%20%20%2C%5Cn)%20%3D%3E%20%5Cn%20%20%7B%5Cn%20%20%20%20return%20true%3B%5Cn%20%20%7D)%3B%22%2C%22options%22%3A%7B%22printWidth%22%3A80%2C%22tabWidth%22%3A2%2C%22useFlowParser%22%3Afalse%2C%22singleQuote%22%3Atrue%2C%22trailingComma%22%3Atrue%2C%22bracketSpacing%22%3Atrue%7D%7D). Is the comma actually illegal there, or should I file that as a separate bug?

---

This looks like a wonderful tool. I'm really excited to see where it leads.

Pull request description:
Fix comma when an arrow function with no arguments breaks
Fixes #118

Patch:
diff --git a/README.md b/README.md
index d77edc0b4..a4a5b25af 100644
--- a/README.md
+++ b/README.md
@@ -181,6 +181,10 @@ Can also be installed using `ext install prettier-vscode`
 
 [Check repository for configuration and shortcuts](https://github.com/esbenp/prettier-vscode)
 
+### Sublime Text
+
+Please see [this issue](https://github.com/jlongster/prettier/issues/17) for those interested in working on Sublime Text integration.
+
 More editors are coming soon.
 
 ## Language Support
diff --git a/index.js b/index.js
index 54de9ad6c..1f7fed44d 100644
--- a/index.js
+++ b/index.js
@@ -50,7 +50,7 @@ function format(text, opts) {
   opts.originalText = text;
 
   const printer = new Printer(opts);
-  return printer.printGenerically(ast).code;
+  return printer.print(ast);
 }
 
 function formatWithShebang(text, opts) {
diff --git a/package.json b/package.json
index 8c4662616..04c16ab22 100644
--- a/package.json
+++ b/package.json
@@ -21,8 +21,7 @@
     "flow-parser": "^0.37.0",
     "get-stdin": "^5.0.1",
     "minimist": "^1.2.0",
-    "private": "^0.1.6",
-    "source-map": "^0.5.6"
+    "private": "^0.1.6"
   },
   "devDependencies": {
     "jest": "^18.0.0",
diff --git a/src/fast-path.js b/src/fast-path.js
index 5299ebc1c..bcc19b505 100644
--- a/src/fast-path.js
+++ b/src/fast-path.js
@@ -228,6 +228,7 @@ FPp.needsParens = function(assumeExpressionContext) {
   }
 
   switch (node.type) {
+    case "UpdateExpression":
     case "UnaryExpression":
     case "SpreadElement":
     case "SpreadProperty":
@@ -289,6 +290,7 @@ FPp.needsParens = function(assumeExpressionContext) {
           return true;
       }
 
+    case "AwaitExpression":
     case "YieldExpression":
       switch (parent.type) {
         case "BinaryExpression":
@@ -296,13 +298,14 @@ FPp.needsParens = function(assumeExpressionContext) {
         case "UnaryExpression":
         case "SpreadElement":
         case "SpreadProperty":
-        case "CallExpression":
-        case "MemberExpression":
         case "NewExpression":
         case "ConditionalExpression":
-        case "YieldExpression":
+        case "MemberExpression":
           return true;
 
+        case "CallExpression":
+          return parent.callee === node;
+
         default:
           return false;
       }
diff --git a/src/printer.js b/src/printer.js
index fde8865bf..fafe6d14c 100644
--- a/src/printer.js
+++ b/src/printer.js
@@ -1,6 +1,5 @@
 "use strict";
 var assert = require("assert");
-var sourceMap = require("source-map");
 var printComments = require("./comments").printComments;
 var pp = require("./pp");
 var fromString = pp.fromString;
@@ -27,52 +26,13 @@ var FastPath = require("./fast-path");
 var util = require("./util");
 var isIdentifierName = require("esutils").keyword.isIdentifierNameES6;
 
-function PrintResult(code, sourceMap) {
-  assert.ok(this instanceof PrintResult);
-
-  isString.assert(code);
-
-  this.code = code;
-
-  if (sourceMap) {
-    isObject.assert(sourceMap);
-
-    this.map = sourceMap;
-  }
-}
-
-var PRp = PrintResult.prototype;
-var warnedAboutToString = false;
-
-PRp.toString = function() {
-  if (!warnedAboutToString) {
-    console.warn(
-      "Deprecation warning: recast.print now returns an object with " +
-        "a .code property. You appear to be treating the object as a " +
-        "string, which might still work but is strongly discouraged."
-    );
-
-    warnedAboutToString = true;
-  }
-
-  return this.code;
-};
-
-var emptyPrintResult = new PrintResult("");
-
 function Printer(originalOptions) {
   assert.ok(this instanceof Printer);
 
-  var explicitTabWidth = originalOptions && originalOptions.tabWidth;
   var options = normalizeOptions(originalOptions);
 
   assert.notStrictEqual(options, originalOptions);
 
-  // It's common for client code to pass the same options into both
-  // recast.parse and recast.print, but the Printer doesn't need (and
-  // can be confused by) options.sourceFileName, so we null it out.
-  options.sourceFileName = null;
-
   // Print the entire AST generically.
   function printGenerically(path) {
     return printComments(
@@ -84,37 +44,13 @@ function Printer(originalOptions) {
 
   this.print = function(ast) {
     if (!ast) {
-      return emptyPrintResult;
-    }
-
-    var lines = print(FastPath.from(ast), true);
-    return new PrintResult(
-      lines.toString(options),
-      util.composeSourceMaps(
-        options.inputSourceMap,
-        lines.getSourceMap(options.sourceMapName, options.sourceRoot)
-      )
-    );
-  };
-
-  this.printGenerically = function(ast) {
-    if (!ast) {
-      return emptyPrintResult;
+      return "";
     }
 
     var path = FastPath.from(ast);
-    var oldReuseWhitespace = options.reuseWhitespace;
-
-    // Do not reuse whitespace (or anything else, for that matter)
-    // when printing generically.
-    options.reuseWhitespace = false;
-
     var res = printGenerically(path);
-    var pr = new PrintResult(pp.print(options.printWidth, res));
-
-    options.reuseWhitespace = oldReuseWhitespace;
 
-    return pr;
+    return pp.print(options.printWidth, res);
   };
 }
 
@@ -548,21 +484,24 @@ function genericPrintNoParens(path, options, print) {
         "body"
       );
 
+      const hasContent = getFirstString(naked);
+      const hasDirectives = n.directives && n.directives.length > 0;
+
       // If there are no contents, return a simple block
-      if (!getFirstString(naked)) {
+      if (!hasContent && !hasDirectives) {
         return "{}";
       }
 
       parts.push("{");
 
       // Babel 6
-      if (n.directives) {
+      if (hasDirectives) {
         path.each(
           function(childPath) {
             parts.push(
               indent(
                 options.tabWidth,
-                concat([ hardline, print(childPath), ";", hardline ])
+                concat([ hardline, print(childPath), ";" ])
               )
             );
           },
@@ -570,7 +509,11 @@ function genericPrintNoParens(path, options, print) {
         );
       }
 
-      parts.push(indent(options.tabWidth, concat([ hardline, naked ])));
+      if (hasContent) {
+        parts.push(
+          indent(options.tabWidth, concat([ hardline, naked ]))
+        );
+      }
 
       parts.push(hardline, "}");
 
@@ -1644,7 +1587,11 @@ function printStatementSequence(path, options, print) {
 
 function printPropertyKey(path, print) {
   var node = path.getNode().key;
-  if (node.type === "StringLiteral" && isIdentifierName(node.value)) {
+  if (
+    (node.type === "StringLiteral" ||
+      node.type === "Literal" && typeof node.value === "string") &&
+    isIdentifierName(node.value)
+  ) {
     // 'a' -> a
     return node.value;
   }
@@ -1790,6 +1737,10 @@ function printFunctionParams(path, print, options) {
     printed.push(concat([ "...", path.call(print, "rest") ]));
   }
 
+  if (printed.length === 0) {
+    return "()";
+  }
+
   return concat([
     "(",
     indent(
@@ -2144,7 +2095,7 @@ function adjustClause(clause, options, forceSpace) {
     return concat([ " ", clause ]);
   }
 
-  return indent(options.tabWidth, concat([ hardline, clause ]));
+  return indent(options.tabWidth, concat([ line, clause ]));
 }
 
 function isCurlyBracket(doc) {
diff --git a/src/util.js b/src/util.js
index d124a686f..bc51155ef 100644
--- a/src/util.js
+++ b/src/util.js
@@ -3,9 +3,6 @@ var assert = require("assert");
 var types = require("ast-types");
 var getFieldValue = types.getFieldValue;
 var n = types.namedTypes;
-var sourceMap = require("source-map");
-var SourceMapConsumer = sourceMap.SourceMapConsumer;
-var SourceMapGenerator = sourceMap.SourceMapGenerator;
 var hasOwn = Object.prototype.hasOwnProperty;
 var util = exports;
 
@@ -33,55 +30,6 @@ function copyPos(pos) {
 }
 util.copyPos = copyPos;
 
-util.composeSourceMaps = function(formerMap, latterMap) {
-  if (formerMap) {
-    if (!latterMap) {
-      return formerMap;
-    }
-  } else {
-    return latterMap || null;
-  }
-
-  var smcFormer = new SourceMapConsumer(formerMap);
-  var smcLatter = new SourceMapConsumer(latterMap);
-  var smg = new SourceMapGenerator({
-    file: latterMap.file,
-    sourceRoot: latterMap.sourceRoot
-  });
-
-  var sourcesToContents = {};
-
-  smcLatter.eachMapping(function(mapping) {
-    var origPos = smcFormer.originalPositionFor({
-      line: mapping.originalLine,
-      column: mapping.originalColumn
-    });
-
-    var sourceName = origPos.source;
-    if (sourceName === null) {
-      return;
-    }
-
-    smg.addMapping({
-      source: sourceName,
-      original: copyPos(origPos),
-      generated: {
-        line: mapping.generatedLine,
-        column: mapping.generatedColumn
-      },
-      name: mapping.name
-    });
-
-    var sourceContent = smcFormer.sourceContentFor(sourceName);
-    if (sourceContent && !hasOwn.call(sourcesToContents, sourceName)) {
-      sourcesToContents[sourceName] = sourceContent;
-      smg.setSourceContent(sourceName, sourceContent);
-    }
-  });
-
-  return smg.toJSON();
-};
-
 function expandLoc(parentNode, childNode) {
   if (locStart(childNode) - locStart(parentNode) < 0) {
     setLocStart(parentNode, locStart(childNode));


Test patch:
diff --git a/tests/abnormal/__snapshots__/jsfmt.spec.js.snap b/tests/abnormal/__snapshots__/jsfmt.spec.js.snap
index bf33d38a1..d8cbcd17d 100644
--- a/tests/abnormal/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/abnormal/__snapshots__/jsfmt.spec.js.snap
@@ -33,8 +33,7 @@ function foo() {
 function bar(x: number) {}
 function foo() {
   var x = null;
-  if (x == null)
-    return;
+  if (x == null) return;
   bar(x);
 }
 "
diff --git a/tests/arrows/__snapshots__/jsfmt.spec.js.snap b/tests/arrows/__snapshots__/jsfmt.spec.js.snap
index 0cbabd48d..f1b48892b 100644
--- a/tests/arrows/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/arrows/__snapshots__/jsfmt.spec.js.snap
@@ -49,3 +49,13 @@ function selectBestEffortImageForWidth(
 }
 "
 `;
+
+exports[`test long-call-no-args.js 1`] = `
+"veryLongCall(VERY_VERY_VERY_VERY_VERY_VERY_VERY_VERY_VERY_VERY_LONG_CONSTANT, () => {})
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+veryLongCall(
+  VERY_VERY_VERY_VERY_VERY_VERY_VERY_VERY_VERY_VERY_LONG_CONSTANT,
+  () => {}
+);
+"
+`;
diff --git a/tests/arrows/long-call-no-args.js b/tests/arrows/long-call-no-args.js
new file mode 100644
index 000000000..8dcb17c88
--- /dev/null
+++ b/tests/arrows/long-call-no-args.js
@@ -0,0 +1,1 @@
+veryLongCall(VERY_VERY_VERY_VERY_VERY_VERY_VERY_VERY_VERY_VERY_LONG_CONSTANT, () => {})
diff --git a/tests/async/__snapshots__/jsfmt.spec.js.snap b/tests/async/__snapshots__/jsfmt.spec.js.snap
index 53f85d5cb..d0b6434b0 100644
--- a/tests/async/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/async/__snapshots__/jsfmt.spec.js.snap
@@ -459,6 +459,13 @@ console.log(x.await);
 
 var await = 3;
 var y = { await };
+
+async function f() { (await f()).length }
+async function g() {
+  invariant(
+    (await driver.navigator.getUrl()).substr(-7)
+  );
+}
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 async function f() {
   await 1;
@@ -514,5 +521,12 @@ console.log(x.await);
 
 var await = 3;
 var y = { await };
+
+async function f() {
+  (await f()).length;
+}
+async function g() {
+  invariant((await driver.navigator.getUrl()).substr(-7));
+}
 "
 `;
diff --git a/tests/async/await_parse.js b/tests/async/await_parse.js
index 10149204e..042e679b0 100644
--- a/tests/async/await_parse.js
+++ b/tests/async/await_parse.js
@@ -22,3 +22,10 @@ console.log(x.await);
 
 var await = 3;
 var y = { await };
+
+async function f() { (await f()).length }
+async function g() {
+  invariant(
+    (await driver.navigator.getUrl()).substr(-7)
+  );
+}
diff --git a/tests/break/__snapshots__/jsfmt.spec.js.snap b/tests/break/__snapshots__/jsfmt.spec.js.snap
index 406a0070d..732c630b6 100644
--- a/tests/break/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/break/__snapshots__/jsfmt.spec.js.snap
@@ -83,8 +83,7 @@ function foo(b) {
 
 function bar(b) {
   var x = b ? null : false;
-  if (x == null)
-    return;
+  if (x == null) return;
   switch (\"\") {
     case 0:
       var y: number = x;
@@ -101,8 +100,7 @@ function bar(b) {
 
 function bar2(b) {
   var x = b ? null : false;
-  if (x == null)
-    return;
+  if (x == null) return;
   switch (\"\") {
     case 0: {
       let y: number = x;
diff --git a/tests/destructuring/__snapshots__/jsfmt.spec.js.snap b/tests/destructuring/__snapshots__/jsfmt.spec.js.snap
index a90d73fa4..ebfacac4d 100644
--- a/tests/destructuring/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/destructuring/__snapshots__/jsfmt.spec.js.snap
@@ -555,7 +555,7 @@ exports[`test string_lit.js 1`] = `
 var { \"with-dash\": with_dash } = { \"with-dash\": \"motivating example\" };
 (with_dash: \"motivating example\"); // ok
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-var { \"key\": val } = { key: \"val\" };
+var { key: val } = { key: \"val\" };
 (val: void);
 // error: string ~> void
 var { \"with-dash\": with_dash } = { \"with-dash\": \"motivating example\" };
diff --git a/tests/instanceof/__snapshots__/jsfmt.spec.js.snap b/tests/instanceof/__snapshots__/jsfmt.spec.js.snap
index c757067d1..d32238bbf 100644
--- a/tests/instanceof/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/instanceof/__snapshots__/jsfmt.spec.js.snap
@@ -104,16 +104,13 @@ function x(b) {
 
 function consumer1(b) {
   var g = x(b);
-  if (g instanceof X2)
-    g.foo = \"1337\";
-  else
-    g.foo = 1337;
+  if (g instanceof X2) g.foo = \"1337\";
+  else g.foo = 1337;
 }
 
 function consumer2(b) {
   var g = x(b);
-  if (g instanceof X1)
-    g.foo = \"1337\"; // oops
+  if (g instanceof X1) g.foo = \"1337\"; // oops
 }
 
 // x.y instanceof t
@@ -131,16 +128,13 @@ function y(b) {
 
 function consumer3(b) {
   var g = y(b);
-  if (g.bar instanceof X2)
-    g.bar.foo = \"1337\";
-  else
-    g.bar.foo = 1337;
+  if (g.bar instanceof X2) g.bar.foo = \"1337\";
+  else g.bar.foo = 1337;
 }
 
 function consumer4(b) {
   var g = y(b);
-  if (g.bar instanceof X1)
-    g.bar.foo = \"1337\"; // oops
+  if (g.bar instanceof X1) g.bar.foo = \"1337\"; // oops
 }
 
 // x.y.z instance of t
@@ -158,25 +152,20 @@ function z(b) {
 
 function consumer5(b) {
   var g = z(b);
-  if (g.baz.bar instanceof X2)
-    g.baz.bar.foo = \"1337\";
-  else
-    g.baz.bar.foo = 1337;
+  if (g.baz.bar instanceof X2) g.baz.bar.foo = \"1337\";
+  else g.baz.bar.foo = 1337;
 }
 
 function consumer6(b) {
   var g = z(b);
-  if (g.baz.bar instanceof X1)
-    g.baz.bar.foo = \"1337\"; // oops
+  if (g.baz.bar instanceof X1) g.baz.bar.foo = \"1337\"; // oops
 }
 
 // this instanceof t
 class C {
   m() {
-    if (this instanceof D)
-      alert(this.s);
-    else
-      alert(\"nope\");
+    if (this instanceof D) alert(this.s);
+    else alert(\"nope\");
   }
 }
 
diff --git a/tests/nullable/__snapshots__/jsfmt.spec.js.snap b/tests/nullable/__snapshots__/jsfmt.spec.js.snap
index 5489aef98..33cfd5db7 100644
--- a/tests/nullable/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/nullable/__snapshots__/jsfmt.spec.js.snap
@@ -48,11 +48,9 @@ function corge(x: number) {}
 
 var x = bar();
 // x: ?string
-if (x != null)
-  qux(x);
+if (x != null) qux(x);
 // x: ?string | null
-if (x != null)
-  corge(x);
+if (x != null) corge(x);
 // x: ?string | null
 function grault() {
   x = null;
diff --git a/tests/path/__snapshots__/jsfmt.spec.js.snap b/tests/path/__snapshots__/jsfmt.spec.js.snap
index 2de635b6a..282608fed 100644
--- a/tests/path/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/path/__snapshots__/jsfmt.spec.js.snap
@@ -9,8 +9,7 @@ var z:number = x;
 var x = 1;
 while (typeof x == \"number\" || typeof x == \"string\") {
   x = x + 1;
-  if (true)
-    x = \"\";
+  if (true) x = \"\";
 }
 var z: number = x;
 "
diff --git a/tests/plsummit/__snapshots__/jsfmt.spec.js.snap b/tests/plsummit/__snapshots__/jsfmt.spec.js.snap
index 7a51a4316..b7c5075fc 100644
--- a/tests/plsummit/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/plsummit/__snapshots__/jsfmt.spec.js.snap
@@ -80,8 +80,7 @@ function foo() {
 }
 
 function bar(x: ?string): number {
-  if (x == null)
-    x = \"\";
+  if (x == null) x = \"\";
   return x.length;
 }
 "
diff --git a/tests/refi/__snapshots__/jsfmt.spec.js.snap b/tests/refi/__snapshots__/jsfmt.spec.js.snap
index 176504bd4..090250741 100644
--- a/tests/refi/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/refi/__snapshots__/jsfmt.spec.js.snap
@@ -85,8 +85,7 @@ var tests = [
     }
   },
   function() {
-    if (x == null)
-      return;
+    if (x == null) return;
     var y: string = x; // ok
   },
   function() {
@@ -383,8 +382,7 @@ var tests = [
   },
   function() {
     var x: { p: ?string } = { p: \"xxx\" };
-    if (x.p == null)
-      return;
+    if (x.p == null) return;
     var y: string = x.p; // ok
   },
   function() {
@@ -762,8 +760,7 @@ var paths = [
   },
   function() {
     var x: ?string = \"xxx\";
-    if (x == null)
-      return;
+    if (x == null) return;
     var y: string = x; // ok
   },
   function() {
@@ -1071,28 +1068,22 @@ class C {
   p: ?string;
 
   ensure0(): string {
-    if (this.p != null)
-      return this.p;
-    else
-      return \"\";
+    if (this.p != null) return this.p;
+    else return \"\";
   }
 
   ensure1(): string {
-    if (this.p == null)
-      return \"\";
+    if (this.p == null) return \"\";
     return this.p;
   }
 
   ensure2(): string | void {
-    if (this.p !== null)
-      return this.p;
-    else
-      return \"\";
+    if (this.p !== null) return this.p;
+    else return \"\";
   }
 
   ensure3(): string | void {
-    if (this.p === null)
-      return \"\";
+    if (this.p === null) return \"\";
     return this.p;
   }
 }
@@ -1100,17 +1091,13 @@ class C {
 // super.p op null
 class D extends C {
   ensure100(): string {
-    if (super.p != null)
-      return super.p;
-    else
-      return \"\";
+    if (super.p != null) return super.p;
+    else return \"\";
   }
 
   ensure101(): string {
-    if (super.p == null)
-      return \"\";
-    else
-      return super.p;
+    if (super.p == null) return \"\";
+    else return super.p;
   }
 
   ensure103(): string {
@@ -1486,31 +1473,23 @@ class A {
   p: ?string;
 
   ensure0(): string {
-    if (typeof this.p == \"string\")
-      return this.p;
-    else
-      return \"\";
+    if (typeof this.p == \"string\") return this.p;
+    else return \"\";
   }
 
   ensure1(): string {
-    if (typeof this.p != \"string\")
-      return \"\";
-    else
-      return this.p;
+    if (typeof this.p != \"string\") return \"\";
+    else return this.p;
   }
 
   ensure2(): string | void {
-    if (typeof this.p === \"string\")
-      return this.p;
-    else
-      return \"\";
+    if (typeof this.p === \"string\") return this.p;
+    else return \"\";
   }
 
   ensure3(): string | void {
-    if (typeof this.p !== \"string\")
-      return \"\";
-    else
-      return this.p;
+    if (typeof this.p !== \"string\") return \"\";
+    else return this.p;
   }
 }
 "
@@ -1648,17 +1627,13 @@ class A {
   p: ?string;
 
   ensure0(): string {
-    if (this.p !== undefined && this.p !== null)
-      return this.p;
-    else
-      return \"\";
+    if (this.p !== undefined && this.p !== null) return this.p;
+    else return \"\";
   }
 
   ensure1(): string {
-    if (this.p === undefined || this.p === null)
-      return \"\";
-    else
-      return this.p;
+    if (this.p === undefined || this.p === null) return \"\";
+    else return this.p;
   }
 }
 "
@@ -1796,17 +1771,13 @@ class A {
   p: ?string;
 
   ensure0(): string {
-    if (this.p !== void 0 && this.p !== null)
-      return this.p;
-    else
-      return \"\";
+    if (this.p !== void 0 && this.p !== null) return this.p;
+    else return \"\";
   }
 
   ensure1(): string {
-    if (this.p === void 0 || this.p === null)
-      return \"\";
-    else
-      return this.p;
+    if (this.p === void 0 || this.p === null) return \"\";
+    else return this.p;
   }
 }
 "
diff --git a/tests/refinements/__snapshots__/jsfmt.spec.js.snap b/tests/refinements/__snapshots__/jsfmt.spec.js.snap
index 163f54563..e70352b9c 100644
--- a/tests/refinements/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/refinements/__snapshots__/jsfmt.spec.js.snap
@@ -287,8 +287,7 @@ type NonNullType = { kind: \"NonNullType\", type: Name | ListType | BadType };
 type BadType = {};
 
 function getTypeASTName(typeAST: Type): string {
-  if (!typeAST.type)
-    throw new Error(\"Must be wrapping type\");
+  if (!typeAST.type) throw new Error(\"Must be wrapping type\");
   // OK
   return getTypeASTName(typeAST.type); // error, BadType not a subtype of Type
 }
@@ -2466,8 +2465,7 @@ type Breakfast = Apple | Orange | Broccoli | Carrot;
 function bar(x: Breakfast) {
   if (x.kind === \"Fruit\") {
     (x.taste: \"Good\");
-  } // error, Apple.taste = Bad else
-    (x.raw: \"No\"); // error, Carrot.raw = Maybe
+  } // error, Apple.taste = Bad else (x.raw: \"No\"); // error, Carrot.raw = Maybe
 }
 
 function qux(x: Breakfast) {
@@ -2480,8 +2478,7 @@ function qux(x: Breakfast) {
 
 // example 4
 function list(n) {
-  if (n > 0)
-    return { kind: \"cons\", next: list(n - 1) };
+  if (n > 0) return { kind: \"cons\", next: list(n - 1) };
   return { kind: \"nil\" };
 }
 function length(l) {
@@ -2493,8 +2490,7 @@ function length(l) {
   }
 }
 function check(n) {
-  if (n >= 0)
-    return n === length(list(n));
+  if (n >= 0) return n === length(list(n));
   return true;
 }
 
@@ -2520,10 +2516,8 @@ kind({ kind: EnumKind.A, A: 1 });
 type Citizen = { citizen: true };
 type NonCitizen = { citizen: false, nationality: string };
 function nationality(x: Citizen | NonCitizen) {
-  if (x.citizen)
-    return \"Shire\";
-  else
-    return x.nationality;
+  if (x.citizen) return \"Shire\";
+  else return x.nationality;
 }
 
 let tests = [
diff --git a/tests/super/__snapshots__/jsfmt.spec.js.snap b/tests/super/__snapshots__/jsfmt.spec.js.snap
index e80899e8e..b2ecf83cd 100644
--- a/tests/super/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/super/__snapshots__/jsfmt.spec.js.snap
@@ -211,10 +211,8 @@ class G extends A {
 class H extends A {
   y: number;
   constructor() {
-    if (Math.random() < 0.5)
-      super();
-    else
-      super();
+    if (Math.random() < 0.5) super();
+    else super();
     this.y;
     // OK
     this.x; // OK
@@ -262,8 +260,7 @@ class K_ {
 class K extends K_ {
   constructor() {
     super(() => {
-      if (_this)
-        _this.foo();
+      if (_this) _this.foo();
     });
     // OK
     var _this = this;
diff --git a/tests/tabWith/__snapshots__/jsfmt.spec.js.snap b/tests/tabWith/__snapshots__/jsfmt.spec.js.snap
index e583af41a..03113ea47 100644
--- a/tests/tabWith/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/tabWith/__snapshots__/jsfmt.spec.js.snap
@@ -15,8 +15,7 @@ class A {
     var x = 1;
     while (typeof x == \"number\" || typeof x == \"string\") {
       x = x + 1;
-      if (true)
-        x = \"\";
+      if (true) x = \"\";
     }
     var z: number = x;
   }
@@ -41,8 +40,7 @@ class A {
         var x = 1;
         while (typeof x == \"number\" || typeof x == \"string\") {
             x = x + 1;
-            if (true)
-                x = \"\";
+            if (true) x = \"\";
         }
         var z: number = x;
     }
diff --git a/tests/type-destructors/__snapshots__/jsfmt.spec.js.snap b/tests/type-destructors/__snapshots__/jsfmt.spec.js.snap
index f42c9a98e..2848c5c28 100644
--- a/tests/type-destructors/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/type-destructors/__snapshots__/jsfmt.spec.js.snap
@@ -17,8 +17,7 @@ function foo(x: ?string): $NonMaybeType<?string> {
 function foo(x: ?string): $NonMaybeType<?string> {
   if (x != null) {
     return x;
-  } else
-    return 0; // this should be an error
+  } else return 0; // this should be an error
 }
 
 //(foo(): string); // should not be necessary to expose the error above
@@ -51,10 +50,8 @@ type Obj_Prop_x = $PropertyType<Obj, \"x\">;
 (42: Obj_Prop_x);
 
 function foo(o: Obj): $PropertyType<Obj, \"x\"> {
-  if (false)
-    return o.x;
-  else
-    return 0;
+  if (false) return o.x;
+  else return 0;
 }
 "
 `;
diff --git a/tests/typeof/__snapshots__/jsfmt.spec.js.snap b/tests/typeof/__snapshots__/jsfmt.spec.js.snap
index e7f7e5827..f42a14490 100644
--- a/tests/typeof/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/typeof/__snapshots__/jsfmt.spec.js.snap
@@ -161,7 +161,7 @@ var f: typeof numberAlias = 42;
  * These provoke a specific error, not just the generic
  * \"type is incompatible\"
  */
-var Map = { \"A\": \"this is A\", \"B\": \"this is B\", \"C\": \"this is C\" };
+var Map = { A: \"this is A\", B: \"this is B\", C: \"this is C\" };
 var keys: $Keys<Map> = \"A\"; // Error: ineligible value used in type anno
 "
 `;
diff --git a/tests/undefined/__snapshots__/jsfmt.spec.js.snap b/tests/undefined/__snapshots__/jsfmt.spec.js.snap
index 553191442..d869ee280 100644
--- a/tests/undefined/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/undefined/__snapshots__/jsfmt.spec.js.snap
@@ -47,8 +47,7 @@ function foo() {
 
 function bar() {
   var x: ?{ bar(): void };
-  if (x)
-    x.bar();
+  if (x) x.bar();
 }
 
 function qux(x?: number, y: string = \"\", z) {}
diff --git a/tests/union_new/__snapshots__/jsfmt.spec.js.snap b/tests/union_new/__snapshots__/jsfmt.spec.js.snap
index 22046bc1c..e0e214dfe 100644
--- a/tests/union_new/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/union_new/__snapshots__/jsfmt.spec.js.snap
@@ -330,10 +330,8 @@ function hello(x:X): string {
 type X = ({ a: true } & { b: string }) | ({ a: false } & { c: string });
 //type X = {a:true, b:string} | {a:false, c:string}; // this works.
 function hello(x: X): string {
-  if (x.a === true)
-    return x.b;
-  else
-    return x.c;
+  if (x.a === true) return x.b;
+  else return x.c;
 }
 "
 `;
@@ -1242,10 +1240,8 @@ type Cons = { kind: \"cons\", next: List };
 // disjoint unions
 
 function length(list: List) {
-  if (list.kind === \"cons\")
-    return length(list.next) + 1;
-  else
-    return 0;
+  if (list.kind === \"cons\") return length(list.next) + 1;
+  else return 0;
 }
 
 length({ kind: \"nil\" });
@@ -1619,10 +1615,8 @@ type Error = { type: \"ERROR\" } & Empty;
 export type T = Success | Error;
 
 function foo(x: T) {
-  if (x.type === \"SUCCESS\")
-    return x.result;
-  else
-    return x.result;
+  if (x.type === \"SUCCESS\") return x.result;
+  else return x.result;
 }
 "
 `;
@@ -1827,17 +1821,13 @@ function hello4(x:X): string {
 type X = ({ a: true } & { b: string }) | ({ a: false } & { c: string });
 //type X = {a:true, b:string} | {a:false, c:string}; // this works.
 function hello1(x: X): string {
-  if (x.a === true)
-    return x.b;
-  else
-    return x.c;
+  if (x.a === true) return x.b;
+  else return x.c;
 }
 
 function hello2(x: X): string {
-  if (x.a === false)
-    return x.c;
-  else
-    return x.b;
+  if (x.a === false) return x.c;
+  else return x.b;
 }
 
 function hello3(x: X): string {
diff --git a/tests/update_expression/__snapshots__/jsfmt.spec.js.snap b/tests/update_expression/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000..b77426804
--- /dev/null
+++ b/tests/update_expression/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,6 @@
+exports[`test update_expression.js 1`] = `
+"(this.x++).toString()
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+(this.x++).toString();
+"
+`;
diff --git a/tests/update_expression/jsfmt.spec.js b/tests/update_expression/jsfmt.spec.js
new file mode 100644
index 000000000..989047bcc
--- /dev/null
+++ b/tests/update_expression/jsfmt.spec.js
@@ -0,0 +1,1 @@
+run_spec(__dirname);
diff --git a/tests/update_expression/update_expression.js b/tests/update_expression/update_expression.js
new file mode 100644
index 000000000..762c641da
--- /dev/null
+++ b/tests/update_expression/update_expression.js
@@ -0,0 +1,1 @@
+(this.x++).toString()