Fix printing edge cases in Nullish Coalescing and Optional Ch… (#11255)

This is a mix of changes, most importantly:

- Always print parens when mixing nullish coalescing and another logical
- Fixes assignment in the callee of an optional chain, which now blocks #11248

Also, cleans up a bit of code, and removes an unnecessary parens around `class A extends new B {}`
This commit is contained in:
Justin Ridgewell 2020-03-13 11:35:28 -04:00 committed by GitHub
parent 420f2ee69a
commit b1a589f0aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 73 additions and 35 deletions

View File

@ -46,14 +46,7 @@ function isOrHasCallExpression(node) {
return true; return true;
} }
if (t.isMemberExpression(node)) { return t.isMemberExpression(node) && isOrHasCallExpression(node.object);
return (
isOrHasCallExpression(node.object) ||
(!node.computed && isOrHasCallExpression(node.property))
);
} else {
return false;
}
} }
export function needsWhitespace(node, parent, type) { export function needsWhitespace(node, parent, type) {
@ -97,18 +90,5 @@ export function needsParens(node, parent, printStack) {
if (isOrHasCallExpression(node)) return true; if (isOrHasCallExpression(node)) return true;
} }
/* this check is for NullishCoalescing being used with LogicalOperators like && and ||
* For example when someone creates an ast programmaticaly like this
* t.logicalExpression(
* "??",
* t.logicalExpression("||", t.identifier("a"), t.identifier("b")),
* t.identifier("c"),
* );
* In the example above the AST is equivalent to writing a || b ?? c
* This is incorrect because NullishCoalescing when used with LogicalExpressions should have parenthesis
* The correct syntax is (a || b) ?? c, that is why we need parenthesis in this case
*/
if (t.isLogicalExpression(node) && parent.operator === "??") return true;
return find(expandedParens, node, parent, printStack); return find(expandedParens, node, parent, printStack);
} }

View File

@ -122,8 +122,6 @@ export function Binary(node: Object, parent: Object): boolean {
return true; return true;
} }
} }
return false;
} }
export function UnionTypeAnnotation(node: Object, parent: Object): boolean { export function UnionTypeAnnotation(node: Object, parent: Object): boolean {
@ -244,7 +242,8 @@ export function ConditionalExpression(node: Object, parent: Object): boolean {
t.isBinary(parent) || t.isBinary(parent) ||
t.isConditionalExpression(parent, { test: node }) || t.isConditionalExpression(parent, { test: node }) ||
t.isAwaitExpression(parent) || t.isAwaitExpression(parent) ||
t.isOptionalMemberExpression(parent) || t.isOptionalMemberExpression(parent, { object: node }) ||
t.isOptionalCallExpression(parent, { callee: node }) ||
t.isTaggedTemplateExpression(parent) || t.isTaggedTemplateExpression(parent) ||
t.isTSTypeAssertion(parent) || t.isTSTypeAssertion(parent) ||
t.isTSAsExpression(parent) t.isTSAsExpression(parent)
@ -272,16 +271,28 @@ export function OptionalCallExpression(node: Object, parent: Object): boolean {
); );
} }
export function AssignmentExpression(node: Object): boolean { export function AssignmentExpression(
node: Object,
parent: Object,
printStack: Array<Object>,
): boolean {
if (t.isObjectPattern(node.left)) { if (t.isObjectPattern(node.left)) {
return true; return true;
} else { } else {
return ConditionalExpression(...arguments); return ConditionalExpression(node, parent, printStack);
} }
} }
export function NewExpression(node: Object, parent: Object): boolean { export function LogicalExpression(node: Object, parent: Object): boolean {
return isClassExtendsClause(node, parent); switch (node.operator) {
case "||":
if (!t.isLogicalExpression(parent)) return false;
return parent.operator === "??" || parent.operator === "&&";
case "&&":
return t.isLogicalExpression(parent, { operator: "??" });
case "??":
return t.isLogicalExpression(parent) && parent.operator !== "??";
}
} }
// Walk up the print stack to determine if our node can come first // Walk up the print stack to determine if our node can come first

View File

@ -1,3 +1,6 @@
1 + (a = 2); 1 + (a = 2);
1 + (a += 2); 1 + (a += 2);
a = a || (a = {}); a = a || (a = {});
(a = b)();
(a = b)?.();

View File

@ -1,3 +1,5 @@
1 + (a = 2); 1 + (a = 2);
1 + (a += 2); 1 + (a += 2);
a = a || (a = {}); a = a || (a = {});
(a = b)();
(a = b)?.();

View File

@ -12,7 +12,7 @@ class A6 extends class {} {}
class A7 extends (B ? C : D) {} class A7 extends (B ? C : D) {}
class A8 extends (new B()) {} class A8 extends new B() {}
class A9 extends (B, C) {} class A9 extends (B, C) {}

View File

@ -1,2 +1,17 @@
const foo = 'test' const foo = 'test'
console.log((foo ?? '') == '') console.log((foo ?? '') == '')
a ?? (b ?? c);
(a ?? b) ?? c;
a ?? (b || c);
a || (b ?? c);
(a ?? b) || c;
(a || b) ?? c;
a ?? (b && c);
a && (b ?? c);
(a ?? b) && c;
(a && b) ?? c;

View File

@ -1,2 +1,12 @@
const foo = 'test'; const foo = 'test';
console.log((foo ?? '') == ''); console.log((foo ?? '') == '');
a ?? b ?? c;
a ?? b ?? c;
a ?? (b || c);
a || (b ?? c);
(a ?? b) || c;
(a || b) ?? c;
a ?? (b && c);
a && (b ?? c);
(a ?? b) && c;
(a && b) ?? c;

View File

@ -2,4 +2,14 @@ foo ||bar;
(x => x)|| bar; (x => x)|| bar;
(function a(x){return x;})|| 2; (function a(x){return x;})|| 2;
0||(function(){return alpha;}); 0||(function(){return alpha;});
a ?? (b || c);
a && (b && c);
(a && b) && c;
a || (b || c);
(a || b) || c;
a || (b && c);
a && (b || c);
(a || b) && c;
(a && b) || c;

View File

@ -9,4 +9,11 @@ foo || bar;
return alpha; return alpha;
}; };
a ?? (b || c); a && b && c;
a && b && c;
a || b || c;
a || b || c;
a || b && c;
a && (b || c);
(a || b) && c;
a && b || c;