Repository: jlongster/prettier
Pull request #315 (2017-01-18 23:20:08)

Fix crash for single rest on class declaration
I thought I didn't need to check the length but forgot that the rest argument is not in the list for class declaration. Now it doesn't crash anymore and there's a test...


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 752736783..83be70e09 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
 # 0.0.9
 
-[link](https://github.com/jlongster/prettier/compare/0192d58...a7405257)
+[link](https://github.com/jlongster/prettier/compare/0.0.8...0.0.9)
 
 * Workaround flow bug parsing astral unicode characters (#277)
 * Allow specifying the major mode that `defun-before-save` will use. (#276
diff --git a/bin/prettier.js b/bin/prettier.js
index 3321b030d..57e5dd80f 100755
--- a/bin/prettier.js
+++ b/bin/prettier.js
@@ -30,7 +30,8 @@ const stdin = argv["stdin"];
 
 if (!filenames.length && !stdin) {
   console.log(
-    "Usage: prettier [opts] [filename ...]\n\n" + "Available options:\n" +
+    "Usage: prettier [opts] [filename ...]\n\n" +
+      "Available options:\n" +
       "  --write              Edit the file in-place (beware!)\n" +
       "  --stdin              Read input from stdin\n" +
       "  --print-width <int>  Specify the length of line that the printer will wrap on. Defaults to 80.\n" +
diff --git a/index.js b/index.js
index 194f2a936..e1e351337 100644
--- a/index.js
+++ b/index.js
@@ -3,7 +3,7 @@ const babylon = require("babylon");
 const Printer = require("./src/printer").Printer;
 const flowParser = require("flow-parser");
 const comments = require("./src/comments");
-const version = require('./package.json').version;
+const version = require("./package.json").version;
 
 var babylonOptions = {
   sourceType: "module",
@@ -32,10 +32,11 @@ function format(text, opts) {
     ast = flowParser.parse(text, {
       esproposal_class_instance_fields: true,
       esproposal_class_static_fields: true,
-      esproposal_export_star_as: true,
+      esproposal_export_star_as: true
     });
     if (ast.errors.length > 0) {
-      let msg = ast.errors[0].message + " on line " +
+      let msg = ast.errors[0].message +
+        " on line " +
         ast.errors[0].loc.start.line;
       if (opts.filename) {
         msg += " in file " + opts.filename;
diff --git a/package.json b/package.json
index 40ba40721..aa4b434a3 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,7 @@
 {
   "name": "prettier",
   "version": "0.0.9",
+  "description": "Prettier is an opinionated JavaScript formatter",
   "bin": {
     "prettier": "./bin/prettier.js"
   },
diff --git a/src/comments.js b/src/comments.js
index 10398d3f9..0283f3a0e 100644
--- a/src/comments.js
+++ b/src/comments.js
@@ -161,7 +161,8 @@ exports.attach = function(comments, ast, text) {
       // have here is a dangling comment, e.g. [/* crickets */].
       breakTies(tiesToBreak, text);
       addDanglingComment(en, comment);
-    } else {}
+    } else {
+    }
   });
 
   breakTies(tiesToBreak, text);
@@ -182,8 +183,8 @@ function breakTies(tiesToBreak, text) {
     return;
   }
 
-  var pn = tiesToBreak[(0)].precedingNode;
-  var fn = tiesToBreak[(0)].followingNode;
+  var pn = tiesToBreak[0].precedingNode;
+  var fn = tiesToBreak[0].followingNode;
   var gapEndPos = locStart(fn);
 
   // Iterate backwards through tiesToBreak, examining the gaps
@@ -272,7 +273,7 @@ exports.printComments = function(path, print, options) {
   var parent = path.getParentNode();
   var printed = print(path);
   var comments = n.Node.check(value) && types.getFieldValue(value, "comments");
-  var isFirstInProgram = n.Program.check(parent) && parent.body[(0)] === value;
+  var isFirstInProgram = n.Program.check(parent) && parent.body[0] === value;
 
   if (!comments || comments.length === 0) {
     return printed;
@@ -290,7 +291,8 @@ exports.printComments = function(path, print, options) {
       if (
         leading ||
           trailing &&
-            !(n.Statement.check(value) || comment.type === "Block" ||
+            !(n.Statement.check(value) ||
+              comment.type === "Block" ||
               comment.type === "CommentBlock")
       ) {
         leadingParts.push(printLeadingComment(commentPath, print));
diff --git a/src/fast-path.js b/src/fast-path.js
index 19eb2be28..6e873c952 100644
--- a/src/fast-path.js
+++ b/src/fast-path.js
@@ -26,8 +26,7 @@ FastPath.from = function(obj) {
     // lightweight FastPath [..., name, value] stacks.
     var copy = Object.create(FastPath.prototype);
     var stack = [ obj.value ];
-    for (var pp; pp = obj.parentPath; obj = pp)
-      stack.push(obj.name, pp.value);
+    for (var pp; pp = obj.parentPath; obj = pp) stack.push(obj.name, pp.value);
     copy.stack = stack.reverse();
     return copy;
   }
@@ -91,9 +90,9 @@ FPp.getParentNode = function getParentNode(count) {
 FPp.getRootValue = function getRootValue() {
   var s = this.stack;
   if (s.length % 2 === 0) {
-    return s[(1)];
+    return s[1];
   }
-  return s[(0)];
+  return s[0];
 };
 
 // Temporarily push properties named by string arguments given after the
@@ -210,7 +209,8 @@ FPp.needsParens = function(assumeExpressionContext) {
   // Add parens around a `class` that extends an expression (it should
   // parse correctly, even if it's invalid)
   if (
-    parent.type === "ClassDeclaration" && parent.superClass === node &&
+    parent.type === "ClassDeclaration" &&
+      parent.superClass === node &&
       node.type === "AwaitExpression"
   ) {
     return true;
@@ -219,7 +219,8 @@ FPp.needsParens = function(assumeExpressionContext) {
   // The left-hand side of the ** exponentiation operator must always
   // be parenthesized unless it's an ident or literal
   if (
-    parent.type === "BinaryExpression" && parent.operator === "**" &&
+    parent.type === "BinaryExpression" &&
+      parent.operator === "**" &&
       parent.left === node &&
       node.type !== "Identifier" &&
       node.type !== "Literal" &&
@@ -232,7 +233,8 @@ FPp.needsParens = function(assumeExpressionContext) {
     case "UpdateExpression":
     case "SpreadElement":
     case "SpreadProperty":
-      return parent.type === "MemberExpression" && name === "object" &&
+      return parent.type === "MemberExpression" &&
+        name === "object" &&
         parent.object === node;
 
     case "UnaryExpression":
@@ -339,7 +341,8 @@ FPp.needsParens = function(assumeExpressionContext) {
 
     case "NumericLiteral":
     case "Literal":
-      return parent.type === "MemberExpression" && isNumber.check(node.value) &&
+      return parent.type === "MemberExpression" &&
+        isNumber.check(node.value) &&
         name === "object" &&
         parent.object === node;
 
@@ -397,7 +400,8 @@ FPp.needsParens = function(assumeExpressionContext) {
 
     default:
       if (
-        parent.type === "NewExpression" && name === "callee" &&
+        parent.type === "NewExpression" &&
+          name === "callee" &&
           parent.callee === node
       ) {
         return containsCallExpression(node);
@@ -405,7 +409,8 @@ FPp.needsParens = function(assumeExpressionContext) {
   }
 
   if (
-    assumeExpressionContext !== true && !this.canBeFirstInStatement() &&
+    assumeExpressionContext !== true &&
+      !this.canBeFirstInStatement() &&
       this.firstInStatement()
   )
     return true;
@@ -418,11 +423,11 @@ function isBinary(node) {
 }
 
 function isUnaryLike(node) {
-  return // I considered making SpreadElement and SpreadProperty subtypes
+  // I considered making SpreadElement and SpreadProperty subtypes
   // of UnaryExpression, but they're not really Expression nodes.
-  n.UnaryExpression.check(
-    node
-  ) || n.SpreadElement && n.SpreadElement.check(node) || n.SpreadProperty && n.SpreadProperty.check(node);
+  return n.UnaryExpression.check(node) ||
+    n.SpreadElement && n.SpreadElement.check(node) ||
+    n.SpreadProperty && n.SpreadProperty.check(node);
 }
 
 function containsCallExpression(node) {
@@ -445,7 +450,8 @@ function containsCallExpression(node) {
 
 FPp.canBeFirstInStatement = function() {
   var node = this.getNode();
-  return !n.FunctionExpression.check(node) && !n.ObjectExpression.check(node) &&
+  return !n.FunctionExpression.check(node) &&
+    !n.ObjectExpression.check(node) &&
     !n.ClassExpression.check(node) &&
     !(n.AssignmentExpression.check(node) && n.ObjectPattern.check(node.left));
 };
@@ -470,7 +476,7 @@ FPp.firstInStatement = function() {
     if (
       n.BlockStatement.check(parent) && parentName === "body" && childName === 0
     ) {
-      assert.strictEqual(parent.body[(0)], child);
+      assert.strictEqual(parent.body[0], child);
       return true;
     }
 
@@ -480,10 +486,11 @@ FPp.firstInStatement = function() {
     }
 
     if (
-      n.SequenceExpression.check(parent) && parentName === "expressions" &&
+      n.SequenceExpression.check(parent) &&
+        parentName === "expressions" &&
         childName === 0
     ) {
-      assert.strictEqual(parent.expressions[(0)], child);
+      assert.strictEqual(parent.expressions[0], child);
       continue;
     }
 
@@ -508,7 +515,8 @@ FPp.firstInStatement = function() {
     }
 
     if (
-      n.UnaryExpression.check(parent) && !parent.prefix &&
+      n.UnaryExpression.check(parent) &&
+        !parent.prefix &&
         childName === "argument"
     ) {
       assert.strictEqual(parent.argument, child);
diff --git a/src/pp.js b/src/pp.js
index 4b61b8ac3..8b55a0421 100644
--- a/src/pp.js
+++ b/src/pp.js
@@ -46,7 +46,7 @@ function multilineGroup(contents, opts) {
 
 function conditionalGroup(states, opts) {
   return group(
-    states[(0)],
+    states[0],
     Object.assign(opts || {}, { expandedStates: states })
   );
 }
@@ -165,9 +165,9 @@ function fits(next, restCommands, width) {
     }
 
     const x = cmds.pop();
-    const ind = x[(0)];
-    const mode = x[(1)];
-    const doc = x[(2)];
+    const ind = x[0];
+    const mode = x[1];
+    const doc = x[2];
 
     if (typeof doc === "string") {
       width -= doc.length;
@@ -190,7 +190,7 @@ function fits(next, restCommands, width) {
         case "if-break":
           if (mode === MODE_BREAK) {
             if (doc.breakContents) {
-              cmds.push([ ind, mode, doc.breakContents ]);              
+              cmds.push([ ind, mode, doc.breakContents ]);
             }
           }
           if (mode === MODE_FLAT) {
@@ -232,9 +232,9 @@ function print(w, doc) {
   let shouldRemeasure = false;
   while (cmds.length !== 0) {
     const x = cmds.pop();
-    const ind = x[(0)];
-    const mode = x[(1)];
-    const doc = x[(2)];
+    const ind = x[0];
+    const mode = x[1];
+    const doc = x[2];
 
     if (typeof doc === "string") {
       out.push(doc);
@@ -319,7 +319,7 @@ function print(w, doc) {
         case "if-break":
           if (mode === MODE_BREAK) {
             if (doc.breakContents) {
-              cmds.push([ ind, mode, doc.breakContents ]);              
+              cmds.push([ ind, mode, doc.breakContents ]);
             }
           }
           if (mode === MODE_FLAT) {
@@ -359,8 +359,10 @@ function print(w, doc) {
               } else {
                 if (out.length > 0) {
                   // Trim whitespace at the end of line
-                  out[out.length - 1] = out[out.length - 1]
-                    .replace(/[^\S\n]*$/, '');
+                  out[out.length - 1] = out[out.length - 1].replace(
+                    /[^\S\n]*$/,
+                    ""
+                  );
                 }
 
                 out.push("\n" + " ".repeat(ind));
diff --git a/src/printer.js b/src/printer.js
index 3e2fd50f9..8408f434c 100644
--- a/src/printer.js
+++ b/src/printer.js
@@ -74,7 +74,8 @@ function genericPrint(path, options, printPath) {
   }
 
   if (
-    node.decorators && node.decorators.length > 0 &&
+    node.decorators &&
+      node.decorators.length > 0 &&
       // If the parent node is an export declaration, it will be
       // responsible for printing node.decorators.
       !util.getParentExportDeclaration(path)
@@ -86,7 +87,8 @@ function genericPrint(path, options, printPath) {
       "decorators"
     );
   } else if (
-    util.isExportDeclaration(node) && node.declaration &&
+    util.isExportDeclaration(node) &&
+      node.declaration &&
       node.declaration.decorators
   ) {
     // Export declarations are responsible for printing any decorators
@@ -183,13 +185,15 @@ function genericPrintNoParens(path, options, print) {
       const parts = [];
       printBinaryishExpressions(path, parts, print, options);
 
-      return group(concat([
-        // Don't include the initial expression in the indentation
-        // level. The first item is guaranteed to be the first
-        // left-most expression.
-        parts.length > 0 ? parts[0] : "",
-        indent(options.tabWidth, concat(parts.slice(1)))
-      ]));
+      return group(
+        concat([
+          // Don't include the initial expression in the indentation
+          // level. The first item is guaranteed to be the first
+          // left-most expression.
+          parts.length > 0 ? parts[0] : "",
+          indent(options.tabWidth, concat(parts.slice(1)))
+        ])
+      );
     }
     case "AssignmentPattern":
       return concat([
@@ -239,13 +243,11 @@ function genericPrintNoParens(path, options, print) {
       ]);
     case "FunctionDeclaration":
     case "FunctionExpression":
-      if (n.async)
-        parts.push("async ");
+      if (n.async) parts.push("async ");
 
       parts.push("function");
 
-      if (n.generator)
-        parts.push("*");
+      if (n.generator) parts.push("*");
 
       if (n.id) {
         parts.push(" ", path.call(print, "id"));
@@ -265,17 +267,17 @@ function genericPrintNoParens(path, options, print) {
 
       return concat(parts);
     case "ArrowFunctionExpression":
-      if (n.async)
-        parts.push("async ");
+      if (n.async) parts.push("async ");
 
       if (n.typeParameters) {
         parts.push(path.call(print, "typeParameters"));
       }
 
       if (
-        n.params.length === 1 && !n.rest &&
-          n.params[(0)].type === "Identifier" &&
-          !n.params[(0)].typeAnnotation &&
+        n.params.length === 1 &&
+          !n.rest &&
+          n.params[0].type === "Identifier" &&
+          !n.params[0].typeAnnotation &&
           !n.predicate &&
           !n.returnType
       ) {
@@ -297,9 +299,9 @@ function genericPrintNoParens(path, options, print) {
       const collapsed = concat([ concat(parts), " ", body ]);
 
       if (
-        n.body.type === 'ArrayExpression' ||
-        n.body.type === 'ObjectExpression' ||
-        n.body.type === 'JSXElement'
+        n.body.type === "ArrayExpression" ||
+          n.body.type === "ObjectExpression" ||
+          n.body.type === "JSXElement"
       ) {
         return group(collapsed);
       }
@@ -322,21 +324,17 @@ function genericPrintNoParens(path, options, print) {
     case "YieldExpression":
       parts.push("yield");
 
-      if (n.delegate)
-        parts.push("*");
+      if (n.delegate) parts.push("*");
 
-      if (n.argument)
-        parts.push(" ", path.call(print, "argument"));
+      if (n.argument) parts.push(" ", path.call(print, "argument"));
 
       return concat(parts);
     case "AwaitExpression":
       parts.push("await");
 
-      if (n.all)
-        parts.push("*");
+      if (n.all) parts.push("*");
 
-      if (n.argument)
-        parts.push(" ", path.call(print, "argument"));
+      if (n.argument) parts.push(" ", path.call(print, "argument"));
 
       return concat(parts);
     case "ModuleDeclaration":
@@ -448,7 +446,7 @@ function genericPrintNoParens(path, options, print) {
         assert.ok(standalones.length <= 1);
 
         if (standalones.length > 0) {
-          parts.push(standalones[(0)]);
+          parts.push(standalones[0]);
         }
 
         if (standalones.length > 0 && grouped.length > 0) {
@@ -464,10 +462,7 @@ function genericPrintNoParens(path, options, print) {
                   options.tabWidth,
                   concat([
                     options.bracketSpacing ? line : softline,
-                    join(
-                      concat([ ",", line ]),
-                      grouped
-                    )
+                    join(concat([ ",", line ]), grouped)
                   ])
                 ),
                 ifBreak(options.trailingComma ? "," : ""),
@@ -513,9 +508,7 @@ function genericPrintNoParens(path, options, print) {
       }
 
       if (hasContent) {
-        parts.push(
-          indent(options.tabWidth, concat([ hardline, naked ]))
-        );
+        parts.push(indent(options.tabWidth, concat([ hardline, naked ])));
       }
 
       parts.push(hardline, "}");
@@ -676,8 +669,7 @@ function genericPrintNoParens(path, options, print) {
         );
       }
 
-      if (n.typeAnnotation)
-        parts.push(path.call(print, "typeAnnotation"));
+      if (n.typeAnnotation) parts.push(path.call(print, "typeAnnotation"));
 
       return concat(parts);
     case "SequenceExpression":
@@ -700,10 +692,8 @@ function genericPrintNoParens(path, options, print) {
     // Babel 6 Literal split
     case "StringLiteral":
     case "Literal":
-      if (typeof n.value === "number")
-        return n.raw;
-      if (typeof n.value !== "string")
-        return fromString(n.value, options);
+      if (typeof n.value === "number") return n.raw;
+      if (typeof n.value !== "string") return fromString(n.value, options);
 
       return nodeStr(n, options);
     case // Babel 6
@@ -723,8 +713,7 @@ function genericPrintNoParens(path, options, print) {
     case "UnaryExpression":
       parts.push(n.operator);
 
-      if (/[a-z]$/.test(n.operator))
-        parts.push(" ");
+      if (/[a-z]$/.test(n.operator)) parts.push(" ");
 
       parts.push(path.call(print, "argument"));
 
@@ -732,8 +721,7 @@ function genericPrintNoParens(path, options, print) {
     case "UpdateExpression":
       parts.push(path.call(print, "argument"), n.operator);
 
-      if (n.prefix)
-        parts.reverse();
+      if (n.prefix) parts.reverse();
 
       return concat(parts);
     case "ConditionalExpression":
@@ -774,7 +762,7 @@ function genericPrintNoParens(path, options, print) {
       parts = [
         n.kind,
         " ",
-        printed[(0)],
+        printed[0],
         indent(
           options.tabWidth,
           concat(printed.slice(1).map(p => concat([ ",", line, p ])))
@@ -918,10 +906,8 @@ function genericPrintNoParens(path, options, print) {
       var parts = [ doBody ];
       const hasBraces = isCurlyBracket(clause);
 
-      if (hasBraces)
-        parts.push(" while");
-      else
-        parts.push(concat([ line, "while" ]));
+      if (hasBraces) parts.push(" while");
+      else parts.push(concat([ line, "while" ]));
 
       parts.push(" (", path.call(print, "test"), ");");
 
@@ -937,8 +923,7 @@ function genericPrintNoParens(path, options, print) {
     case "BreakStatement":
       parts.push("break");
 
-      if (n.label)
-        parts.push(" ", path.call(print, "label"));
+      if (n.label) parts.push(" ", path.call(print, "label"));
 
       parts.push(";");
 
@@ -946,8 +931,7 @@ function genericPrintNoParens(path, options, print) {
     case "ContinueStatement":
       parts.push("continue");
 
-      if (n.label)
-        parts.push(" ", path.call(print, "label"));
+      if (n.label) parts.push(" ", path.call(print, "label"));
 
       parts.push(";");
 
@@ -1004,10 +988,8 @@ function genericPrintNoParens(path, options, print) {
         "}"
       ]);
     case "SwitchCase":
-      if (n.test)
-        parts.push("case ", path.call(print, "test"), ":");
-      else
-        parts.push("default:");
+      if (n.test) parts.push("case ", path.call(print, "test"), ":");
+      else parts.push("default:");
 
       if (n.consequent.length > 0) {
         const cons = path.call(
@@ -1131,13 +1113,11 @@ function genericPrintNoParens(path, options, print) {
     case "ClassPropertyDefinition":
       parts.push("static ", path.call(print, "definition"));
 
-      if (!namedTypes.MethodDefinition.check(n.definition))
-        parts.push(";");
+      if (!namedTypes.MethodDefinition.check(n.definition)) parts.push(";");
 
       return concat(parts);
     case "ClassProperty":
-      if (n.static)
-        parts.push("static ");
+      if (n.static) parts.push("static ");
 
       var key;
 
@@ -1154,11 +1134,9 @@ function genericPrintNoParens(path, options, print) {
 
       parts.push(key);
 
-      if (n.typeAnnotation)
-        parts.push(path.call(print, "typeAnnotation"));
+      if (n.typeAnnotation) parts.push(path.call(print, "typeAnnotation"));
 
-      if (n.value)
-        parts.push(" = ", path.call(print, "value"));
+      if (n.value) parts.push(" = ", path.call(print, "value"));
 
       parts.push(";");
 
@@ -1348,11 +1326,7 @@ function genericPrintNoParens(path, options, print) {
       );
 
       if (n["extends"].length > 0) {
-        parts.push(
-          "extends ",
-          join(", ", path.map(print, "extends")),
-          " "
-        );
+        parts.push("extends ", join(", ", path.map(print, "extends")), " ");
       }
 
       parts.push(path.call(print, "body"));
@@ -1377,7 +1351,7 @@ function genericPrintNoParens(path, options, print) {
           indent(
             options.tabWidth,
             concat([
-              types[(0)],
+              types[0],
               indent(
                 options.tabWidth,
                 concat(types.slice(1).map(t => concat([ " ", op, line, t ])))
@@ -1432,7 +1406,8 @@ function genericPrintNoParens(path, options, print) {
         : n.variance === "minus" ? "-" : "";
       // TODO: This is a bad hack and we need a better way to know
       // when to emit an arrow function or not.
-      var isFunction = !n.variance && !n.optional &&
+      var isFunction = !n.variance &&
+        !n.optional &&
         n.value.type === "FunctionTypeAnnotation";
       return concat([
         n.static ? "static " : "",
@@ -1611,7 +1586,7 @@ function printPropertyKey(path, print) {
   if (
     (node.type === "StringLiteral" ||
       node.type === "Literal" && typeof node.value === "string") &&
-    isIdentifierName(node.value)
+      isIdentifierName(node.value)
   ) {
     // 'a' -> a
     return node.value;
@@ -1766,7 +1741,7 @@ function printFunctionParams(path, print, options) {
   }
 
   const lastParam = util.getLast(path.getValue().params);
-  const canHaveTrailingComma = lastParam.type !== "RestElement" && !fun.rest;
+  const canHaveTrailingComma = !(lastParam && lastParam.type === "RestElement") && !fun.rest;
 
   return concat([
     "(",
@@ -1784,11 +1759,9 @@ function printObjectMethod(path, options, print) {
   var objMethod = path.getValue();
   var parts = [];
 
-  if (objMethod.async)
-    parts.push("async ");
+  if (objMethod.async) parts.push("async ");
 
-  if (objMethod.generator)
-    parts.push("*");
+  if (objMethod.generator) parts.push("*");
 
   if (
     objMethod.method || objMethod.kind === "get" || objMethod.kind === "set"
@@ -1866,7 +1839,7 @@ function printExportDeclaration(path, options, print) {
     if (decl.specifiers && decl.specifiers.length > 0) {
       if (
         decl.specifiers.length === 1 &&
-          decl.specifiers[(0)].type === "ExportBatchSpecifier"
+          decl.specifiers[0].type === "ExportBatchSpecifier"
       ) {
         parts.push("*");
       } else {
@@ -1879,10 +1852,7 @@ function printExportDeclaration(path, options, print) {
                 options.tabWidth,
                 concat([
                   options.bracketSpacing ? line : softline,
-                  join(
-                    concat([ ",", line ]),
-                    path.map(print, "specifiers")
-                  )
+                  join(concat([ ",", line ]), path.map(print, "specifiers"))
                 ])
               ),
               ifBreak(options.trailingComma ? "," : ""),
@@ -1978,10 +1948,8 @@ function printMemberChain(node, options, print) {
   // Traverse down and gather up all of the calls on member
   // expressions. This flattens it out into a list that we can
   // easily analyze.
-  while (
-    leftmost.type === "CallExpression" &&
-      leftmost.callee.type === "MemberExpression"
-  ) {
+  while (leftmost.type === "CallExpression" &&
+    leftmost.callee.type === "MemberExpression") {
     nodes.push({ member: leftmost.callee, call: leftmost });
     leftmostParent = leftmost;
     leftmost = leftmost.callee.object;
@@ -1996,7 +1964,7 @@ function printMemberChain(node, options, print) {
   // and indent them.
   function argIsFunction(call) {
     if (call.arguments.length > 0) {
-      const type = call.arguments[(0)].type;
+      const type = call.arguments[0].type;
       return typeIsFunction(type);
     }
     return false;
@@ -2006,14 +1974,13 @@ function printMemberChain(node, options, print) {
     nodes.filter(n => argIsFunction(n.call)).length > 1;
 
   if (hasMultipleLookups) {
-    const leftmostPrinted = FastPath.from(leftmostParent).call(print, "callee", "object");
-    const nodesPrinted = nodes.map(
-      node =>
-        ({
-          property: printMemberLookup(FastPath.from(node.member), print),
-          args: printArgumentsList(FastPath.from(node.call), options, print)
-        })
-    );
+    const leftmostPrinted = FastPath
+      .from(leftmostParent)
+      .call(print, "callee", "object");
+    const nodesPrinted = nodes.map(node => ({
+      property: printMemberLookup(FastPath.from(node.member), print),
+      args: printArgumentsList(FastPath.from(node.call), options, print)
+    }));
     const fullyExpanded = concat([
       leftmostPrinted,
       concat(
@@ -2080,53 +2047,56 @@ function printJSXChildren(path, options, print) {
   const children = [];
   const jsxWhitespace = options.singleQuote
     ? ifBreak("{' '}", " ")
-    : ifBreak("{\" \"}", " ");
+    : ifBreak('{" "}', " ");
 
   // using `map` instead of `each` because it provides `i`
-  path.map(function(childPath, i) {
-    const child = childPath.getValue();
-    const isLiteral = namedTypes.Literal.check(child);
-
-    if (isLiteral && typeof child.value === "string") {
-      if (/\S/.test(child.value)) {
-        const beginBreak = child.value.match(/^\s*\n/);
-        const endBreak = child.value.match(/\n\s*$/);
-        const beginSpace = child.value.match(/^\s+/);
-        const endSpace = child.value.match(/\s+$/);
-
-        if (beginBreak) {
-          children.push(hardline);
-        } else if (beginSpace) {
-          children.push(jsxWhitespace);
-        }
+  path.map(
+    function(childPath, i) {
+      const child = childPath.getValue();
+      const isLiteral = namedTypes.Literal.check(child);
+
+      if (isLiteral && typeof child.value === "string") {
+        if (/\S/.test(child.value)) {
+          const beginBreak = child.value.match(/^\s*\n/);
+          const endBreak = child.value.match(/\n\s*$/);
+          const beginSpace = child.value.match(/^\s+/);
+          const endSpace = child.value.match(/\s+$/);
+
+          if (beginBreak) {
+            children.push(hardline);
+          } else if (beginSpace) {
+            children.push(jsxWhitespace);
+          }
 
-        children.push(child.value.replace(/^\s+|\s+$/g, ""));
+          children.push(child.value.replace(/^\s+|\s+$/g, ""));
 
-        if (endBreak) {
+          if (endBreak) {
+            children.push(hardline);
+          } else {
+            if (endSpace) children.push(jsxWhitespace);
+            children.push(softline);
+          }
+        } else if (/\n/.test(child.value)) {
+          // TODO: add another hardline if >1 newline appeared. (also above)
           children.push(hardline);
-        } else {
-          if (endSpace) children.push(jsxWhitespace);
+        } else if (/\s/.test(child.value)) {
+          // whitespace-only without newlines,
+          // eg; a single space separating two elements
+          children.push(jsxWhitespace);
           children.push(softline);
         }
-      } else if (/\n/.test(child.value)) {
-        // TODO: add another hardline if >1 newline appeared. (also above)
-        children.push(hardline);
-      } else if (/\s/.test(child.value)) {
-        // whitespace-only without newlines,
-        // eg; a single space separating two elements
-        children.push(jsxWhitespace);
-        children.push(softline);
-      }
-    } else {
-      children.push(print(childPath));
+      } else {
+        children.push(print(childPath));
 
-      // add a line unless it's followed by a JSX newline
-      let next = n.children[i + 1];
-      if (!(next && /^\s*\n/.test(next.value))) {
-        children.push(softline);
+        // add a line unless it's followed by a JSX newline
+        let next = n.children[i + 1];
+        if (!(next && /^\s*\n/.test(next.value))) {
+          children.push(softline);
+        }
       }
-    }
-  }, "children");
+    },
+    "children"
+  );
 
   return children;
 }
@@ -2210,23 +2180,25 @@ function printJSXElement(path, options, print) {
 
   const closingLines = path.call(print, "closingElement");
 
-  const multiLineElem = group(concat([
-    openingLines,
-    indent(options.tabWidth, group(
-      concat(childrenGroupedByLine),
-      { shouldBreak: true }
-    )),
-    hardline,
-    closingLines,
-  ]));
+  const multiLineElem = group(
+    concat([
+      openingLines,
+      indent(
+        options.tabWidth,
+        group(concat(childrenGroupedByLine), { shouldBreak: true })
+      ),
+      hardline,
+      closingLines
+    ])
+  );
 
   let elem;
   if (hasAnyHardLine) {
     elem = multiLineElem;
   } else {
     elem = conditionalGroup([
-      group(concat([openingLines, concat(children), closingLines])),
-      multiLineElem,
+      group(concat([ openingLines, concat(children), closingLines ])),
+      multiLineElem
     ]);
   }
 
@@ -2238,10 +2210,10 @@ function maybeWrapJSXElementInParens(path, elem, options) {
   if (!parent) return elem;
 
   const NO_WRAP_PARENTS = {
-    "JSXElement": true,
-    "ExpressionStatement": true,
-    "CallExpression": true,
-    "ConditionalExpression": true,
+    JSXElement: true,
+    ExpressionStatement: true,
+    CallExpression: true,
+    ConditionalExpression: true
   };
   if (NO_WRAP_PARENTS[parent.type]) {
     return elem;
@@ -2273,7 +2245,7 @@ function printBinaryishExpressions(path, parts, print) {
   let node = path.getValue();
 
   // We treat BinaryExpression and LogicalExpression nodes the same.
-  if(isBinaryish(node)) {
+  if (isBinaryish(node)) {
     // Put all operators with the same precedence level in the same
     // group. The reason we only need to do this with the `left`
     // expression is because given an expression like `1 + 2 - 3`, it
@@ -2282,23 +2254,19 @@ function printBinaryishExpressions(path, parts, print) {
     // expressions on the right side mean they have a difference
     // precedence level and should be treated as a separate group, so
     // print them normally.
-    if(util.getPrecedence(node.left.operator) === util.getPrecedence(node.operator)) {
+    if (
+      util.getPrecedence(node.left.operator) ===
+        util.getPrecedence(node.operator)
+    ) {
       // Flatten them out by recursively calling this function. The
       // printed values will all be appended to `parts`.
       path.call(left => printBinaryishExpressions(left, parts, print), "left");
-    }
-    else {
+    } else {
       parts.push(path.call(print, "left"));
     }
 
-    parts.push(
-      " ",
-      node.operator,
-      line,
-      path.call(print, "right")
-    );
-  }
-  else {
+    parts.push(" ", node.operator, line, path.call(print, "right"));
+  } else {
     // Our stopping case. Simply print the node normally.
     parts.push(path.call(print));
   }
@@ -2333,8 +2301,7 @@ function lastNonSpaceCharacter(lines) {
   do {
     var ch = lines.charAt(pos);
 
-    if (/\S/.test(ch))
-      return ch;
+    if (/\S/.test(ch)) return ch;
   } while (lines.prevPos(pos));
 }
 
@@ -2354,14 +2321,14 @@ function nodeStr(node, options) {
   }
 
   const result = jsesc(str, {
-    quotes: shouldUseSingleQuote ? 'single' : 'double',
+    quotes: shouldUseSingleQuote ? "single" : "double",
     wrap: true
   });
 
   // Workaround a bug in the Javascript version of the flow parser where
   // astral unicode characters like \uD801\uDC28 are incorrectly parsed as
   // a sequence of \uFFFD.
-  if (options.useFlowParser && result.indexOf('\\uFFFD') !== -1) {
+  if (options.useFlowParser && result.indexOf("\\uFFFD") !== -1) {
     return node.raw;
   }
 
@@ -2372,7 +2339,7 @@ function isFirstStatement(path) {
   const parent = path.getParentNode();
   const node = path.getValue();
   const body = parent.body;
-  return body && body[(0)] === node;
+  return body && body[0] === node;
 }
 
 function isLastStatement(path) {
diff --git a/src/util.js b/src/util.js
index 9f128958c..fb8a29e68 100644
--- a/src/util.js
+++ b/src/util.js
@@ -78,8 +78,7 @@ util.fixFaultyLocations = function(node, text) {
 };
 
 util.isExportDeclaration = function(node) {
-  if (node)
-    switch (node.type) {
+  if (node) switch (node.type) {
       case "ExportDeclaration":
       case "ExportDefaultDeclaration":
       case "ExportDefaultSpecifier":
@@ -123,7 +122,7 @@ function skipNewLineForward(text, index) {
   // For some statement/expressions, it's the character immediately
   // afterward. For others, it's the last character in it. We need to
   // scan until we hit a newline in order to skip it.
-  while(index < text.length) {
+  while (index < text.length) {
     if (text.charAt(index) === "\n") {
       return index + 1;
     }
@@ -180,7 +179,7 @@ util.skipSpaces = skipSpaces;
 
 function locStart(node) {
   if (node.range) {
-    return node.range[(0)];
+    return node.range[0];
   }
   return node.start;
 }
@@ -188,7 +187,7 @@ util.locStart = locStart;
 
 function locEnd(node) {
   if (node.range) {
-    return node.range[(1)];
+    return node.range[1];
   }
   return node.end;
 }
@@ -196,7 +195,7 @@ util.locEnd = locEnd;
 
 function setLocStart(node, index) {
   if (node.range) {
-    node.range[(0)] = index;
+    node.range[0] = index;
   } else {
     node.start = index;
   }
@@ -205,7 +204,7 @@ util.setLocStart = setLocStart;
 
 function setLocEnd(node, index) {
   if (node.range) {
-    node.range[(1)] = index;
+    node.range[1] = index;
   } else {
     node.end = index;
   }
@@ -243,4 +242,4 @@ var PRECEDENCE = {};
 
 util.getPrecedence = function(op) {
   return PRECEDENCE[op];
-}
+};


Test patch:
diff --git a/tests/rest/__snapshots__/jsfmt.spec.js.snap b/tests/rest/__snapshots__/jsfmt.spec.js.snap
index 70b59847b..79cff5aa0 100644
--- a/tests/rest/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/rest/__snapshots__/jsfmt.spec.js.snap
@@ -10,6 +10,8 @@ function f(
   superSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperLong,
   ...args
 ) {}
+
+declare class C { f(...superSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperLong): void; }
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 declare class C {
   f(
@@ -23,5 +25,11 @@ function f(
   ...args
 ) {
 }
+
+declare class C {
+  f(
+    ...superSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperLong
+  ): void,
+}
 "
 `;
diff --git a/tests/rest/trailing-commas.js b/tests/rest/trailing-commas.js
index 39ebdccdf..65a05498a 100644
--- a/tests/rest/trailing-commas.js
+++ b/tests/rest/trailing-commas.js
@@ -9,3 +9,5 @@ function f(
   superSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperLong,
   ...args
 ) {}
+
+declare class C { f(...superSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperLong): void; }