Skip to content

Commit

Permalink
Print parens for extra.parenthesized. Fix bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
conartist6 committed May 25, 2020
1 parent 83a8f83 commit a6c1a19
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 92 deletions.
112 changes: 53 additions & 59 deletions lib/fast-path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,52 +305,43 @@ FPp.needsParens = function(assumeExpressionContext) {
return false;
}

const name = this.getName();

// If the value of this path is some child of a Node and not a Node
// itself, then it doesn't need parentheses. Only Node objects (in fact,
// only Expression nodes) need parentheses.
if (this.getValue() !== node) {
return false;
}

// Only statements don't need parentheses.
if (n.Statement.check(node)) {
if (parent.type === "ParenthesizedExpression" || (node.extra && node.extra.parenthesized)) {
return false;
}

switch (node.type) {
// Only statements don't need parentheses.
case "Statement":
return false;
// Identifiers never need parentheses.
if (node.type === "Identifier") {
case "Identifier":
return false;
}

if (parent.type === "ParenthesizedExpression") {
return false;
}

switch (node.type) {
case "UnaryExpression":
case "SpreadElement":
case "SpreadProperty":
return parent.type === "MemberExpression"
&& name === "object"
&& parent.object === node;
return parent.type === "MemberExpression" && parent.object === node;

case "BinaryExpression":
case "LogicalExpression":
switch (parent.type) {
case "CallExpression":
return name === "callee"
&& parent.callee === node;
return parent.callee === node;

case "UnaryExpression":
case "SpreadElement":
case "SpreadProperty":
return true;

case "MemberExpression":
return name === "object"
&& parent.object === node;
return parent.object === node;

case "BinaryExpression":
case "LogicalExpression":
Expand All @@ -363,8 +354,7 @@ FPp.needsParens = function(assumeExpressionContext) {
return true;
}

if (pp === np && name === "right") {
assert.strictEqual(parent.right, node);
if (pp === np && parent.right === node) {
return true;
}

Expand All @@ -384,7 +374,7 @@ FPp.needsParens = function(assumeExpressionContext) {
return false;

case "ExpressionStatement":
return name !== "expression";
return parent.expression !== node;

default:
// Otherwise err on the side of overparenthesization, adding
Expand All @@ -393,18 +383,24 @@ FPp.needsParens = function(assumeExpressionContext) {
}

case "YieldExpression":
case "AwaitExpression":
switch (parent.type) {
case "BinaryExpression":
case "LogicalExpression":
case "UnaryExpression":
case "SpreadElement":
case "SpreadProperty":
case "BinaryExpression":
case "LogicalExpression":
return true;

case "CallExpression":
case "MemberExpression":
case "NewExpression":
return parent.callee === node;

case "ConditionalExpression":
case "YieldExpression":
return true;
return parent.test === node;

case "MemberExpression":
return parent.object === node;

default:
return false;
Expand All @@ -415,16 +411,15 @@ FPp.needsParens = function(assumeExpressionContext) {
return parent.type === "NullableTypeAnnotation";

case "Literal":
return parent.type === "MemberExpression"
&& isNumber.check(node.value)
&& name === "object"
&& parent.object === node;
return (
parent.type === "MemberExpression" &&
isNumber.check(node.value) &&
parent.object === node
);

// Babel 6 Literal split
case "NumericLiteral":
return parent.type === "MemberExpression"
&& name === "object"
&& parent.object === node;
return parent.type === "MemberExpression" && parent.object === node;

case "AssignmentExpression":
case "ConditionalExpression":
Expand All @@ -438,60 +433,59 @@ FPp.needsParens = function(assumeExpressionContext) {

case "CallExpression":
case "NewExpression":
return name === "callee"
&& parent.callee === node;
return parent.callee === node;

case "ConditionalExpression":
return name === "test"
&& parent.test === node;
return parent.test === node;

case "MemberExpression":
return name === "object"
&& parent.object === node;
return parent.object === node;

default:
return false;
}

case "ArrowFunctionExpression":
if (n.CallExpression.check(parent) &&
name === 'callee') {
return true;
}

if (n.MemberExpression.check(parent) &&
name === 'object') {
return true;
switch(parent.type) {
case 'CallExpression':
return parent.callee === node;
case 'MemberExpression':
return parent.object === node;
case 'TSAsExpression':
return parent.expression === node;
default:
return isBinary(parent);
}

return isBinary(parent);

case "ObjectExpression":
if (parent.type === "ArrowFunctionExpression" &&
name === "body") {
if (parent.type === "ArrowFunctionExpression" && parent.body === node) {
return true;
}

break;

case 'TSAsExpression':
if (parent.type === 'ArrowFunctionExpression' &&
name === 'body' &&
node.expression.type === 'ObjectExpression') {
case "TSAsExpression":
if (
parent.type === "ArrowFunctionExpression" &&
parent.body === node &&
node.expression.type === "ObjectExpression"
) {
return true;
}
break;

case "CallExpression":
if (name === "declaration" &&
n.ExportDefaultDeclaration.check(parent) &&
n.FunctionExpression.check(node.callee)) {
if (
parent.declaration === node &&
n.ExportDefaultDeclaration.check(parent) &&
n.FunctionExpression.check(node.callee)
) {
return true;
}
break;
}

if (parent.type === "NewExpression" &&
name === "callee" &&
parent.callee === node) {
return containsCallExpression(node);
}
Expand Down
22 changes: 4 additions & 18 deletions lib/printer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,14 +186,14 @@ function genericPrint(path: any, config: any, options: any, printPath: any) {
return linesWithoutParens;
}

let shouldAddParens = false;
let shouldAddParens = node.extra ? node.extra.parenthesized : false;
const decoratorsLines = printDecorators(path, printPath);

if (decoratorsLines.isEmpty()) {
// Nodes with decorators can't have parentheses, so we can avoid
// computing path.needsParens() except in this case.
if (! options.avoidRootParens) {
shouldAddParens = path.needsParens();
shouldAddParens = shouldAddParens || path.needsParens();
}
} else {
parts.push(decoratorsLines);
Expand Down Expand Up @@ -2147,17 +2147,13 @@ function genericPrintNoParens(path: any, options: any, print: any) {
]);

case "TSAsExpression": {
var withParens = n.extra && n.extra.parenthesized === true;
if (withParens) parts.push("(");
const expression = path.call(print, "expression");
const expressionType = path.getValue().expression.type;
const needParens = expressionType === "ArrowFunctionExpression" || expressionType === "FunctionExpression";

parts.push(
needParens ? '(' + expression + ')' : expression,
expression,
fromString(" as "),
path.call(print, "typeAnnotation")
);
if (withParens) parts.push(")");

return concat(parts);
}
Expand Down Expand Up @@ -2311,22 +2307,13 @@ function genericPrintNoParens(path: any, options: any, print: any) {
return concat(parts);

case "TSTypeAssertion":
var withParens = n.extra && n.extra.parenthesized === true;
if (withParens) {
parts.push("(");
}

parts.push(
"<",
path.call(print, "typeAnnotation"),
"> ",
path.call(print, "expression")
);

if (withParens) {
parts.push(")");
}

return concat(parts);

case "TSTypeParameterDeclaration":
Expand Down Expand Up @@ -2507,7 +2494,6 @@ function genericPrintNoParens(path: any, options: any, print: any) {
case "XMLComment":
case "XMLProcessingInstruction":
default:
debugger;
throw new Error("unknown type: " + JSON.stringify(n.type));
}
}
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 57 additions & 0 deletions test/parens-babylon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import assert from "assert";
import * as babylon from "@babel/parser";
import { parse as recastParse } from "../lib/parser";
import { Printer } from "../lib/printer";
import * as types from "ast-types";

const printer = new Printer();
const { namedTypes: n } = types;

function parseExpression(expr: any) {
const ast: any = babylon.parseExpression(expr);
return n.ExpressionStatement.check(ast) ? ast.expression : ast;
}

const parse = (expr: string) => recastParse(expr, {
parser: babylon,
});

function check(expr: string) {
const ast = parse(expr);

const reprinted = printer.print(ast).code;
assert.strictEqual(reprinted, expr);

const expressionAst = parseExpression(expr);
const generic = printer.printGenerically(expressionAst).code;
types.astNodesAreEquivalent.assert(expressionAst, parseExpression(generic));
}

describe("babylon parens", function () {
it("AwaitExpression", function () {
check("async () => ({...(await obj)})");
check("(async function* () { yield await foo })");
});

it("YieldExpression", function () {
check("(function* () { return {...(yield obj)}})");
});

it("decorative parens", function () {
const ast = parse("1");
const expr = ast.program.body[0].expression;

expr.extra.parenthesized = true;

assert.strictEqual(printer.print(ast).code, '(1)');
});

it("decorative parens which are also necessary", function () {
const ast = parse("(1).foo");
const expr = ast.program.body[0].expression;

expr.object.extra.parenthesized = false;

assert.strictEqual(printer.print(ast).code, "(1).foo");
});
});
21 changes: 21 additions & 0 deletions test/parens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,27 @@ describe("parens", function () {
].join(eol));
});

it("AwaitExpression", function () {
check("async () => (await a) && (await b)");
check("async () => +(await a)");
check("async () => (await f)()");
check("async () => new (await C)");
check("async () => [...(await obj)]");
check("async () => (await a) ? b : c");
check("async () => (await a).b");
});

it("YieldExpression", function () {
check("function* test () { return (yield a) && (yield b) }");
check("function* test () { return +(yield a) }");
check("function* test () { return (yield f)() }");
check("function* test () { return new (yield C) }");
check("function* test () { return [...(yield obj)] }");
check("function* test () { return (yield a) ? b : c }");
check("function* test () { return (yield a).b }");
check("function* test () { yield yield foo }");
});

it("DiscretionaryParens", function () {
const code = [
"if (info.line && (i > 0 || !skipFirstLine)) {",
Expand Down
1 change: 1 addition & 0 deletions test/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "./jsx";
import "./lines";
import "./mapping";
import "./parens";
import "./parens-babylon";
import "./parser";
import "./patcher";
import "./perf";
Expand Down
Loading

0 comments on commit a6c1a19

Please sign in to comment.