message: Recursively detect if complex expression nodes start with a curly brace
diff: @@ -180,6 +180,49 @@ FPp.map = function map(callback/*, name1, name2, ... */) {
  return result;
  };
  +function expressionStartsWithCurlyBrace(node) {
  + // TODO detect when node is already wrapped in parentheses to avoid (admittedly harmless) wrapping in extra parentheses.
  + if(node == null) return false;
  + switch(node.type) {
  + // expressions guaranteed to start with a {
  + case "ObjectExpression":
  + case "ObjectPattern":
  + return true;
  +
  + // All expressions that comprise nested expressions
  + case "BinaryExpression":
  + case "AssignmentExpression":
  + case "LogicalExpression":
  + return expressionStartsWithCurlyBrace(node.left);
  +
  + case "MemberExpression":
  + case "BindExpression":
  + return expressionStartsWithCurlyBrace(node.object);
  +
  + case "CallExpression":
  + return expressionStartsWithCurlyBrace(node.callee);
  +
  + case "UpdateExpression":
  + case "UnaryExpression":
  + return node.prefix === false && expressionStartsWithCurlyBrace(node.argument);
  +
  + case "TSNonNullExpression":
  + return expressionStartsWithCurlyBrace(node.expression);
  +
  + case "ConditionalExpression":
  + return expressionStartsWithCurlyBrace(node.test);
  +
  + case "SequenceExpression":
  + return expressionStartsWithCurlyBrace(node.expressions[0]);
  +
  + case "TaggedTemplateExpression":
  + return expressionStartsWithCurlyBrace(node.tag);
  +
  + default:
  + false;
  + }
  +}
  +
  // Inspired by require("ast-types").NodePath.prototype.needsParens, but
  // more efficient because we're iterating backwards through a stack.
  FPp.needsParens = function(assumeExpressionContext) {
  @@ -221,8 +264,7 @@ FPp.needsParens = function(assumeExpressionContext) {
  }
  if (parent.type === "ArrowFunctionExpression" && name === "body") {
  - // TODO does this expression start with a {?
  - return true;
  + return expressionStartsWithCurlyBrace(node);
  }
  switch (node.type) {
  