From 86bc6d2b832c03b2b8a8fae153322b643803e725 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Wed, 11 Dec 2024 11:10:31 +0800 Subject: [PATCH] fix(transformer/class-properties): panic when the callee or member is ParenthesisExpression or TS-syntax expressions. --- .../src/es2022/class_properties/private.rs | 11 ++++----- .../src/es2022/class_properties/utils.rs | 9 ++++++-- .../snapshots/oxc.snap.md | 23 +++++++++++++++++-- .../input.js | 8 +++++++ .../output.js | 13 +++++++++++ .../typescript/optional-call/input.ts | 9 ++++++++ .../typescript/optional-call/output.js | 14 +++++++++++ .../typescript/optional-member/input.ts | 9 ++++++++ .../typescript/optional-member/output.js | 13 +++++++++++ .../test/fixtures/typescript/options.json | 3 +++ 10 files changed, 102 insertions(+), 10 deletions(-) create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-optional-member-with-sequence/input.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-optional-member-with-sequence/output.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/optional-call/input.ts create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/optional-call/output.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/optional-member/input.ts create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/optional-member/output.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/options.json diff --git a/crates/oxc_transformer/src/es2022/class_properties/private.rs b/crates/oxc_transformer/src/es2022/class_properties/private.rs index 3320e94202f991..fbf4e388d51a91 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/private.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/private.rs @@ -1001,7 +1001,6 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>, ) -> Option> { - assert_expr_neither_parenthesis_nor_typescript_syntax(expr); match expr { Expression::PrivateFieldExpression(_) => { Some(self.transform_private_field_expression_of_chain_expression(expr, ctx)) @@ -1015,7 +1014,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { self.transform_call_expression_of_chain_expression(call, ctx) } _ => { - assert_expr_neither_parenthesis_nor_typescript_syntax(expr); + assert_expr_neither_parenthesis_nor_typescript_syntax(expr, &self.ctx.source_path); None } } @@ -1126,7 +1125,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { ctx: &mut TraverseCtx<'a>, ) -> Option> { let is_optional = member.optional(); - let object = member.object_mut(); + let object = member.object_mut().get_inner_expression_mut(); let result = self.transform_chain_element_recursively(object, ctx)?; if is_optional && !object.is_identifier_reference() { // `o?.Foo.#self.self?.self.unicorn;` -> `(result ? void 0 : object)?.self.unicorn` @@ -1320,7 +1319,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { let callee = &mut call.callee; let callee_member = callee.to_member_expression_mut(); let is_optional_callee = callee_member.optional(); - let object = callee_member.object_mut(); + let object = callee_member.object_mut().get_inner_expression_mut(); let context = if let Some(result) = self.transform_chain_element_recursively(object, ctx) { if is_optional_callee { @@ -1609,7 +1608,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { object: Expression<'a>, ctx: &mut TraverseCtx<'a>, ) -> (Expression<'a>, Expression<'a>) { - assert_expr_neither_parenthesis_nor_typescript_syntax(&object); + assert_expr_neither_parenthesis_nor_typescript_syntax(&object, &self.ctx.source_path); self.ctx.duplicate_expression(object, false, ctx) } @@ -1628,7 +1627,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { object: Expression<'a>, ctx: &mut TraverseCtx<'a>, ) -> (Expression<'a>, Expression<'a>, Expression<'a>) { - assert_expr_neither_parenthesis_nor_typescript_syntax(&object); + assert_expr_neither_parenthesis_nor_typescript_syntax(&object, &self.ctx.source_path); self.ctx.duplicate_expression_twice(object, false, ctx) } diff --git a/crates/oxc_transformer/src/es2022/class_properties/utils.rs b/crates/oxc_transformer/src/es2022/class_properties/utils.rs index 9324a433307ae0..923a5af3a3d96e 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/utils.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/utils.rs @@ -1,6 +1,8 @@ //! ES2022: Class Properties //! Utility functions. +use std::path::PathBuf; + use oxc_ast::ast::*; use oxc_span::SPAN; use oxc_syntax::reference::ReferenceFlags; @@ -55,9 +57,12 @@ pub(super) fn create_underscore_ident_name<'a>(ctx: &mut TraverseCtx<'a>) -> Ide } #[inline] -pub(super) fn assert_expr_neither_parenthesis_nor_typescript_syntax(expr: &Expression) { +pub(super) fn assert_expr_neither_parenthesis_nor_typescript_syntax( + expr: &Expression, + path: &PathBuf, +) { debug_assert!( !(matches!(expr, Expression::ParenthesizedExpression(_)) || expr.is_typescript_syntax()), - "Should not be: {expr:?}", + "Should not be: {expr:?} in {path:?}", ); } diff --git a/tasks/transform_conformance/snapshots/oxc.snap.md b/tasks/transform_conformance/snapshots/oxc.snap.md index bd67702933ae2b..37ca1aa419bfd3 100644 --- a/tasks/transform_conformance/snapshots/oxc.snap.md +++ b/tasks/transform_conformance/snapshots/oxc.snap.md @@ -1,6 +1,6 @@ commit: 54a8389f -Passed: 103/117 +Passed: 104/120 # All Passed: * babel-plugin-transform-class-static-block @@ -16,7 +16,7 @@ Passed: 103/117 * regexp -# babel-plugin-transform-class-properties (4/7) +# babel-plugin-transform-class-properties (5/10) * private-loose-tagged-template/input.js Scope children mismatch: after transform: ScopeId(1): [ScopeId(2), ScopeId(3), ScopeId(4)] @@ -62,6 +62,25 @@ Scope parent mismatch: after transform: ScopeId(20): Some(ScopeId(1)) rebuilt : ScopeId(20): Some(ScopeId(0)) +* typescript/optional-call/input.ts +Scope children mismatch: +after transform: ScopeId(0): [ScopeId(1)] +rebuilt : ScopeId(0): [ScopeId(1), ScopeId(3)] +Scope children mismatch: +after transform: ScopeId(1): [ScopeId(2), ScopeId(3)] +rebuilt : ScopeId(1): [ScopeId(2)] +Scope parent mismatch: +after transform: ScopeId(2): Some(ScopeId(1)) +rebuilt : ScopeId(3): Some(ScopeId(0)) +Symbol reference IDs mismatch for "X": +after transform: SymbolId(0): [ReferenceId(0), ReferenceId(2), ReferenceId(6), ReferenceId(11), ReferenceId(16)] +rebuilt : SymbolId(0): [ReferenceId(0), ReferenceId(2), ReferenceId(8), ReferenceId(14)] + +* typescript/optional-member/input.ts +Symbol reference IDs mismatch for "X": +after transform: SymbolId(0): [ReferenceId(0), ReferenceId(2), ReferenceId(6), ReferenceId(9), ReferenceId(12)] +rebuilt : SymbolId(0): [ReferenceId(0), ReferenceId(2), ReferenceId(6), ReferenceId(10)] + # babel-plugin-transform-async-to-generator (14/15) * super/nested/input.js diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-optional-member-with-sequence/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-optional-member-with-sequence/input.js new file mode 100644 index 00000000000000..7e86f6ac9c3825 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-optional-member-with-sequence/input.js @@ -0,0 +1,8 @@ +class A { + static #a = 33; + b = 44; + method() { + (undefined, this)?.#a; + (undefined, this)?.b; + } +} diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-optional-member-with-sequence/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-optional-member-with-sequence/output.js new file mode 100644 index 00000000000000..767a760c0f41ff --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-optional-member-with-sequence/output.js @@ -0,0 +1,13 @@ +class A { + constructor() { + babelHelpers.defineProperty(this, "b", 44); + } + method() { + var _ref; + (_ref = (undefined, this)) === null || _ref === void 0 ? void 0 : babelHelpers.assertClassBrand(A, _ref, _a)._; + (undefined, this)?.b; + } +} +var _a = { + _: 33 +}; \ No newline at end of file diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/optional-call/input.ts b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/optional-call/input.ts new file mode 100644 index 00000000000000..5664b90e87ee31 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/optional-call/input.ts @@ -0,0 +1,9 @@ +class X { + static #m = () => {}; + method() { + const o = { X }; + (o.X as typeof X).#m(); + o.X!.#m(); + o.X.#m(); + } +} diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/optional-call/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/optional-call/output.js new file mode 100644 index 00000000000000..f3b5df4dcbc284 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/optional-call/output.js @@ -0,0 +1,14 @@ +class X { + method() { + var _o$X, _o$X2, _o$X3; + const o = { + X + }; + babelHelpers.assertClassBrand(X, _o$X = o.X, _m)._.call(_o$X); + babelHelpers.assertClassBrand(X, _o$X2 = o.X, _m)._.call(_o$X2); + babelHelpers.assertClassBrand(X, _o$X3 = o.X, _m)._.call(_o$X3); + } +} +var _m = { + _: () => {} +}; \ No newline at end of file diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/optional-member/input.ts b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/optional-member/input.ts new file mode 100644 index 00000000000000..6352d2137b47c8 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/optional-member/input.ts @@ -0,0 +1,9 @@ +class X { + static #a = 0; + method() { + const o = { X }; + (o.X as typeof X).#a; + o.X!.#a; + o.X.#a; + } +} diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/optional-member/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/optional-member/output.js new file mode 100644 index 00000000000000..73eaea980fcbd5 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/optional-member/output.js @@ -0,0 +1,13 @@ +class X { + method() { + const o = { + X + }; + babelHelpers.assertClassBrand(X, o.X, _a)._; + babelHelpers.assertClassBrand(X, o.X, _a)._; + babelHelpers.assertClassBrand(X, o.X, _a)._; + } +} +var _a = { + _: 0 +}; \ No newline at end of file diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/options.json b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/options.json new file mode 100644 index 00000000000000..b737952dc0a31d --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/typescript/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["transform-class-properties", "transform-typescript"] +}