From 02bfbfeef56b45825b86ce1808695a75268f0067 Mon Sep 17 00:00:00 2001 From: Dunqing <29533304+Dunqing@users.noreply.github.com> Date: Sun, 13 Oct 2024 08:03:37 +0000 Subject: [PATCH] fix(codegen): preserve parenthesis for `ChainExpression` (#6430) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit close: #6385 The fixing way was referenced from `esbuild`, it is according to these [states](https://github.com/evanw/esbuild/blob/332727499e62315cff4ecaff9fa8b86336555e46/internal/js_ast/js_ast.go#L590-L604 )(determined in the parser) to determine whether to print parenthesis Due to differences in implementation details, we are unable to record certain information like `esbuild` does in its parser. But fortunately, The `ParenthesisExpression` AST is actually like what `esbuild` stored states. --- crates/oxc_ast/src/precedence.rs | 13 +++++-- crates/oxc_codegen/src/gen.rs | 4 +-- .../oxc_codegen/tests/integration/esbuild.rs | 34 ++++++++++++++++++- 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/crates/oxc_ast/src/precedence.rs b/crates/oxc_ast/src/precedence.rs index 56e54b46ed199..12d84eeb0f7b9 100644 --- a/crates/oxc_ast/src/precedence.rs +++ b/crates/oxc_ast/src/precedence.rs @@ -2,9 +2,10 @@ use oxc_syntax::precedence::{GetPrecedence, Precedence}; use crate::ast::{ match_member_expression, AssignmentExpression, AwaitExpression, BinaryExpression, - CallExpression, ComputedMemberExpression, ConditionalExpression, Expression, ImportExpression, - LogicalExpression, MemberExpression, NewExpression, PrivateFieldExpression, SequenceExpression, - StaticMemberExpression, TSTypeAssertion, UnaryExpression, UpdateExpression, YieldExpression, + CallExpression, ChainExpression, ComputedMemberExpression, ConditionalExpression, Expression, + ImportExpression, LogicalExpression, MemberExpression, NewExpression, PrivateFieldExpression, + SequenceExpression, StaticMemberExpression, TSTypeAssertion, UnaryExpression, UpdateExpression, + YieldExpression, }; impl<'a> GetPrecedence for Expression<'a> { @@ -103,6 +104,12 @@ impl<'a> GetPrecedence for NewExpression<'a> { } } +impl<'a> GetPrecedence for ChainExpression<'a> { + fn precedence(&self) -> Precedence { + Precedence::Member + } +} + impl<'a> GetPrecedence for MemberExpression<'a> { fn precedence(&self) -> Precedence { Precedence::Member diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index 089bed38e3f1b..299b94885c0b9 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -2047,12 +2047,12 @@ impl<'a> GenExpr for AwaitExpression<'a> { impl<'a> GenExpr for ChainExpression<'a> { fn gen_expr(&self, p: &mut Codegen, precedence: Precedence, ctx: Context) { - match &self.expression { + p.wrap(precedence >= Precedence::Postfix, |p| match &self.expression { ChainElement::CallExpression(expr) => expr.print_expr(p, precedence, ctx), match_member_expression!(ChainElement) => { self.expression.to_member_expression().print_expr(p, precedence, ctx); } - } + }); } } diff --git a/crates/oxc_codegen/tests/integration/esbuild.rs b/crates/oxc_codegen/tests/integration/esbuild.rs index ed655a3e39778..c03e431baad49 100644 --- a/crates/oxc_codegen/tests/integration/esbuild.rs +++ b/crates/oxc_codegen/tests/integration/esbuild.rs @@ -1,4 +1,6 @@ -//! Tests ported from [esbuild](https://github.com/evanw/esbuild/blob/main/internal/js_printer/js_printer_test.go#L164) +//! Tests ported from `esbuild` +//! * +//! * use crate::tester::{test, test_minify}; @@ -1155,3 +1157,33 @@ fn test_using() { test_minify("await using x = y", "await using x=y;"); test_minify("await using x = y, z = _", "await using x=y,z=_;"); } + +#[test] +fn test_preserve_optional_chain_parentheses() { + test("a?.b.c", "a?.b.c;\n"); + test("(a?.b).c", "(a?.b).c;\n"); + test("a?.b.c.d", "a?.b.c.d;\n"); + test("(a?.b.c).d", "(a?.b.c).d;\n"); + test("a?.b[c]", "a?.b[c];\n"); + test("(a?.b)[c]", "(a?.b)[c];\n"); + test("a?.b(c)", "a?.b(c);\n"); + test("(a?.b)(c)", "(a?.b)(c);\n"); + + test("a?.[b][c]", "a?.[b][c];\n"); + test("(a?.[b])[c]", "(a?.[b])[c];\n"); + test("a?.[b][c][d]", "a?.[b][c][d];\n"); + test("(a?.[b][c])[d]", "(a?.[b][c])[d];\n"); + test("a?.[b].c", "a?.[b].c;\n"); + test("(a?.[b]).c", "(a?.[b]).c;\n"); + test("a?.[b](c)", "a?.[b](c);\n"); + test("(a?.[b])(c)", "(a?.[b])(c);\n"); + + test("a?.(b)(c)", "a?.(b)(c);\n"); + test("(a?.(b))(c)", "(a?.(b))(c);\n"); + test("a?.(b)(c)(d)", "a?.(b)(c)(d);\n"); + test("(a?.(b)(c))(d)", "(a?.(b)(c))(d);\n"); + test("a?.(b).c", "a?.(b).c;\n"); + test("(a?.(b)).c", "(a?.(b)).c;\n"); + test("a?.(b)[c]", "a?.(b)[c];\n"); + test("(a?.(b))[c]", "(a?.(b))[c];\n"); +}