mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(codegen): parenthesize await expression as base of ** (#23001)
## What The base of an exponentiation `**` must be an `UpdateExpression` (ECMA-262 §13.6 `ExponentiationExpression : UpdateExpression ** ExponentiationExpression`). A `UnaryExpression` base is therefore already parenthesized by codegen (`(-a) ** b`, `(typeof a) ** b`), but `AwaitExpression` — which has the same `Prefix` precedence — was missing from the special case. As a result `(await a) ** b` was emitted as `await a ** b`, which is a **different AST** (`await (a ** b)`) and is in fact rejected by oxc's own parser (*"Wrap await expression in parentheses to enforce operator precedence"*), so the output did not round-trip. `AwaitExpression` is now included alongside `UnaryExpression` in the exponentiation-base special case. ## Examples | Input | Before | After | | --- | --- | --- | | `(await a) ** b` | `await a ** b` ❌ (un-parseable) | `(await a) ** b` ✅ | | `(await a) ** (await b)` | `await a ** await b` ❌ | `(await a) ** await b` ✅ | The right operand is correctly left unparenthesized — the `**` right side is an `ExponentiationExpression`, which permits a bare `UnaryExpression`/`await`.
This commit is contained in:
2 files changed
+12
-2
No files matched your search
@@ -200,10 +200,13 @@ impl<'a> BinaryExpressionVisitor<'a> {
|
||||
}
|
||||
}
|
||||
BinaryishOperator::Binary(BinaryOperator::Exponential) => {
|
||||
// Negative numbers are printed using a unary operator
|
||||
// The base of `**` must be an `UpdateExpression`, so a unary/await base
|
||||
// must be parenthesized. Negative numbers print as a unary operator.
|
||||
if matches!(
|
||||
e.left(),
|
||||
Expression::UnaryExpression(_) | Expression::NumericLiteral(_)
|
||||
Expression::UnaryExpression(_)
|
||||
| Expression::AwaitExpression(_)
|
||||
| Expression::NumericLiteral(_)
|
||||
) {
|
||||
self.left_precedence = Precedence::Call;
|
||||
}
|
||||
|
||||
@@ -105,6 +105,13 @@ fn expr() {
|
||||
test("new (f().g.h)();", "new (f()).g.h();\n");
|
||||
test("new (f[g()])();", "new f[g()]();\n");
|
||||
test("new (new f())();", "new new f()();\n");
|
||||
|
||||
// The base of `**` must be an UpdateExpression, so a unary/await base keeps parens.
|
||||
test_same("(-a) ** b;\n");
|
||||
test_same("(typeof a) ** b;\n");
|
||||
test_same("(await a) ** b;\n");
|
||||
// Only the base needs wrapping; the `**` right operand may be a UnaryExpression.
|
||||
test("(await a) ** (await b);", "(await a) ** await b;\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user