Skip to content

Commit 5037b94

Browse files
authored
fix: class, ternary, and arrow function miss parentheses (#504)
1 parent 2dfdef1 commit 5037b94

File tree

5 files changed

+65
-29
lines changed

5 files changed

+65
-29
lines changed

docs/demo/astring.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/demo/astring.min.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/astring.js

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,7 @@ function expressionNeedsParenthesis(state, node, parentNode, isRightHand) {
145145

146146
function formatExpression(state, node, parentNode, isRightHand) {
147147
/*
148-
Writes into `state` a left-hand or right-hand expression `node`
149-
from a binary expression applying the provided `operator`.
150-
The `isRightHand` parameter should be `true` if the `node` is a right-hand argument.
148+
Writes into `state` the provided `node`, adding parenthesis around if the provided `parentNode` needs it. If `node` is a right-hand argument, the provided `isRightHand` parameter should be `true`.
151149
*/
152150
const { generator } = state
153151
if (expressionNeedsParenthesis(state, node, parentNode, isRightHand)) {
@@ -505,7 +503,21 @@ export const GENERATOR = {
505503
state.write('class ' + (node.id ? `${node.id.name} ` : ''), node)
506504
if (node.superClass) {
507505
state.write('extends ')
508-
this[node.superClass.type](node.superClass, state)
506+
const { superClass } = node
507+
const { type } = superClass
508+
const precedence = state.expressionsPrecedence[type]
509+
if (
510+
(type[0] !== 'C' || type[1] !== 'l' || type[5] !== 'E') &&
511+
(precedence === NEEDS_PARENTHESES ||
512+
precedence < state.expressionsPrecedence.ClassExpression)
513+
) {
514+
// Not a ClassExpression that needs parentheses
515+
state.write('(')
516+
this[node.superClass.type](superClass, state)
517+
state.write(')')
518+
} else {
519+
this[superClass.type](superClass, state)
520+
}
509521
state.write(' ')
510522
}
511523
this.ClassBody(node.body, state)
@@ -514,7 +526,7 @@ export const GENERATOR = {
514526
state.write('import ')
515527
const { specifiers } = node
516528
const { length } = specifiers
517-
// NOTE: Once babili is fixed, put this after condition
529+
// TODO: Once babili is fixed, put this after condition
518530
// https://github.com/babel/babili/issues/430
519531
let i = 0
520532
if (length > 0) {
@@ -830,22 +842,21 @@ export const GENERATOR = {
830842
argument: { type },
831843
} = node
832844
state.write(operator)
845+
const needsParentheses = expressionNeedsParenthesis(state, argument, node)
833846
if (
834-
operator.length > 1 ||
835-
(type[0] === 'U' &&
836-
(type[1] === 'n' || type[1] === 'p') &&
837-
argument.prefix &&
838-
argument.operator[0] === operator &&
839-
(operator === '+' || operator === '-'))
847+
!needsParentheses &&
848+
(operator.length > 1 ||
849+
(type[0] === 'U' &&
850+
(type[1] === 'n' || type[1] === 'p') &&
851+
argument.prefix &&
852+
argument.operator[0] === operator &&
853+
(operator === '+' || operator === '-')))
840854
) {
841855
// Large operator or argument is UnaryExpression or UpdateExpression node
842856
state.write(' ')
843857
}
844-
if (
845-
state.expressionsPrecedence[type] <
846-
state.expressionsPrecedence.UnaryExpression
847-
) {
848-
state.write('(')
858+
if (needsParentheses) {
859+
state.write(operator.length > 1 ? ' (' : '(')
849860
this[type](argument, state)
850861
state.write(')')
851862
} else {
@@ -892,15 +903,17 @@ export const GENERATOR = {
892903
}),
893904
LogicalExpression: BinaryExpression,
894905
ConditionalExpression(node, state) {
906+
const { test } = node
907+
const precedence = state.expressionsPrecedence[test.type]
895908
if (
896-
state.expressionsPrecedence[node.test.type] >
897-
state.expressionsPrecedence.ConditionalExpression
909+
precedence === NEEDS_PARENTHESES ||
910+
precedence <= state.expressionsPrecedence.ConditionalExpression
898911
) {
899-
this[node.test.type](node.test, state)
900-
} else {
901912
state.write('(')
902-
this[node.test.type](node.test, state)
913+
this[test.type](test, state)
903914
state.write(')')
915+
} else {
916+
this[test.type](test, state)
904917
}
905918
state.write(' ? ')
906919
this[node.consequent.type](node.consequent, state)
@@ -909,9 +922,10 @@ export const GENERATOR = {
909922
},
910923
NewExpression(node, state) {
911924
state.write('new ')
925+
const precedence = state.expressionsPrecedence[node.callee.type]
912926
if (
913-
state.expressionsPrecedence[node.callee.type] <
914-
state.expressionsPrecedence.CallExpression ||
927+
precedence === NEEDS_PARENTHESES ||
928+
precedence < state.expressionsPrecedence.CallExpression ||
915929
hasCallExpression(node.callee)
916930
) {
917931
state.write('(')
@@ -923,9 +937,10 @@ export const GENERATOR = {
923937
formatSequence(state, node['arguments'])
924938
},
925939
CallExpression(node, state) {
940+
const precedence = state.expressionsPrecedence[node.callee.type]
926941
if (
927-
state.expressionsPrecedence[node.callee.type] <
928-
state.expressionsPrecedence.CallExpression
942+
precedence === NEEDS_PARENTHESES ||
943+
precedence < state.expressionsPrecedence.CallExpression
929944
) {
930945
state.write('(')
931946
this[node.callee.type](node.callee, state)
@@ -942,9 +957,10 @@ export const GENERATOR = {
942957
this[node.expression.type](node.expression, state)
943958
},
944959
MemberExpression(node, state) {
960+
const precedence = state.expressionsPrecedence[node.object.type]
945961
if (
946-
state.expressionsPrecedence[node.object.type] <
947-
state.expressionsPrecedence.MemberExpression
962+
precedence === NEEDS_PARENTHESES ||
963+
precedence < state.expressionsPrecedence.MemberExpression
948964
) {
949965
state.write('(')
950966
this[node.object.type](node.object, state)

src/tests/fixtures/syntax/class.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,13 @@ class J extends A {
3737
super();
3838
}
3939
}
40+
class K extends (() => {}) {}
41+
class L extends (1 + 1) {}
42+
class M extends (-1) {}
43+
class N extends (c++) {}
44+
function* a() {
45+
class A extends (yield) {}
46+
}
47+
async function b() {
48+
class A extends (await a()) {}
49+
}

src/tests/fixtures/syntax/precedence.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ c = !(x instanceof Number);
1616
d = typeof a === 'boolean';
1717
e = !typeof a === 'boolean';
1818
f = !(typeof a === 'boolean');
19+
f = typeof (() => {});
1920
a = (1.1).toString();
2021
b = new A().toString();
2122
c = new x.A().toString();
@@ -50,3 +51,12 @@ g = b + -+-a++;
5051
yield a = b;
5152
const c = yield 3;
5253
});
54+
(function* () {
55+
!(yield 1);
56+
});
57+
!(() => {});
58+
(() => {}) ? a : b;
59+
({}) ? a : b;
60+
(({}) ? a : b) ? c : d;
61+
(function () {}) ? a : b;
62+
(class {}) ? a : b;

0 commit comments

Comments
 (0)