Repository navigation
fix: parenthesize ?? when it is combined with || or && - #1442
Merged
eventualbuddha merged 1 commit intoAug 21, 2026
Merged
Conversation
CoalesceExpression only admits BitwiseORExpression operands, plus a nested CoalesceExpression on the left, so a || b ?? c and a ?? b || c are both SyntaxErrors. needsParens decides this by comparing precedence, which parenthesizes a ?? nested inside || or && but never a || or && nested inside ??, so printing the latter produced output that does not parse. Fixes benjamn#1422.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1422.
??cannot be combined with||or&&without parentheses. TheCoalesceExpressionproduction only admitsBitwiseORExpressionoperands, plus a nestedCoalesceExpressionon the left, soa || b ?? canda ?? b || care both SyntaxErrors.needsParensdecides this by comparing operator precedence, and??sits at the bottom of the table since #1095. That covers a??nested inside||or&&, which is why(a ?? b) || cprints correctly today. It does not cover the other direction: with??as the parent,pp > npis false and nothing is emitted.So printing a
??whose operand is a||or&&produces output that does not parse:All four throw when parsed:
With this change they print as
(a || b) ?? c,a ?? (b || c),(a && b) ?? canda ?? (b && c).The check is on the operator pair rather than on precedence, because this restriction is a grammar production rather than an ordering.
??alongside a non-logical operator is unaffected:a ?? b + c,a + b ?? canda ?? b ?? cstill print without parentheses.Verified by feeding every printed form through
new Functionto confirm it parses, and by checking the opposite direction is not over-parenthesized. Round-tripping unmodified source containing each shape comes back byte-identical.Two tests are added. The first covers the four broken shapes and fails on
main. The second locks in that??is left alone when it is not combined with||or&&, and passes onmain, so it guards against over-correction rather than restating the fix.npm testis clean: 779 passing, lint and build included.