Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix parenthesis for nullish coalescing #10269

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 2 additions & 4 deletions packages/babel-generator/src/generators/expressions.js
Expand Up @@ -227,10 +227,8 @@ export function BindExpression(node: Object) {
this.print(node.callee, node);
}

export {
AssignmentExpression as BinaryExpression,
AssignmentExpression as LogicalExpression,
};
export { AssignmentExpression as BinaryExpression };
export { AssignmentExpression as LogicalExpression };

export function MemberExpression(node: Object) {
this.print(node.object, node);
Expand Down
12 changes: 12 additions & 0 deletions packages/babel-generator/src/node/index.js
Expand Up @@ -96,6 +96,18 @@ export function needsParens(node, parent, printStack) {
if (t.isNewExpression(parent) && parent.callee === node) {
if (isOrHasCallExpression(node)) return true;
}
jridgewell marked this conversation as resolved.
Show resolved Hide resolved
/* this check if for NullishCoalescing with LogicalOperators liks && 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 above case we should that 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 parens in this case
*/
if (t.isLogicalExpression(node) && parent.operator === "??") return true;

return find(expandedParens, node, parent, printStack);
}
@@ -1,4 +1,5 @@
foo ||bar;
(x => x)|| bar;
(function a(x){return x;})|| 2;
0||(function(){return alpha;});
0||(function(){return alpha;});
a ?? (b || c);
@@ -0,0 +1,3 @@
{
"plugins": ["nullishCoalescingOperator"]
}
Expand Up @@ -7,4 +7,6 @@ foo || bar;

0 || function () {
return alpha;
};
};

a ?? (b || c);
23 changes: 23 additions & 0 deletions packages/babel-generator/test/index.js
Expand Up @@ -298,6 +298,29 @@ describe("generation", function() {
});

describe("programmatic generation", function() {
it("should add parenthesis when NullishCoalescing is used along with ||", function() {
jridgewell marked this conversation as resolved.
Show resolved Hide resolved
// https://github.com/babel/babel/issues/10260
const nullishCoalesc = t.logicalExpression(
"??",
t.logicalExpression("||", t.identifier("a"), t.identifier("b")),
t.identifier("c"),
);
const output = generate(nullishCoalesc).code;
expect(output).toBe(`(a || b) ?? c`);
});
jridgewell marked this conversation as resolved.
Show resolved Hide resolved
it("should add parenthesis when NullishCoalesing is used with &&", function() {
const nullishCoalesc = t.logicalExpression(
"??",
t.identifier("a"),
t.logicalExpression(
"&&",
t.identifier("b"),
t.logicalExpression("&&", t.identifier("c"), t.identifier("d")),
),
);
const output = generate(nullishCoalesc).code;
expect(output).toBe(`a ?? (b && c && d)`);
});
jridgewell marked this conversation as resolved.
Show resolved Hide resolved
it("numeric member expression", function() {
// Should not generate `0.foo`
const mem = t.memberExpression(
Expand Down
4 changes: 2 additions & 2 deletions packages/babel-parser/src/tokenizer/types.js
Expand Up @@ -188,8 +188,8 @@ export const types: { [name: string]: TokenType } = {
_null: createKeyword("null", { startsExpr }),
_true: createKeyword("true", { startsExpr }),
_false: createKeyword("false", { startsExpr }),
_in: createKeyword("in", { beforeExpr, binop: 7 }),
_instanceof: createKeyword("instanceof", { beforeExpr, binop: 7 }),
_in: createKeyword("in", { beforeExpr, binop: 8 }),
_instanceof: createKeyword("instanceof", { beforeExpr, binop: 8 }),
_typeof: createKeyword("typeof", { beforeExpr, prefix, startsExpr }),
_void: createKeyword("void", { beforeExpr, prefix, startsExpr }),
_delete: createKeyword("delete", { beforeExpr, prefix, startsExpr }),
Expand Down