diff --git a/crates/oxc_transformer/src/common/arrow_function_converter.rs b/crates/oxc_transformer/src/common/arrow_function_converter.rs index 58c1d66bb..e0836c829 100644 --- a/crates/oxc_transformer/src/common/arrow_function_converter.rs +++ b/crates/oxc_transformer/src/common/arrow_function_converter.rs @@ -1313,18 +1313,27 @@ impl<'a> VisitMut<'a> for ConstructorBodyThisAfterSuperInserter<'a, '_> { /// `super();` -> `super(); _this = this;` fn visit_statements(&mut self, statements: &mut ArenaVec<'a, Statement<'a>>) { for (index, stmt) in statements.iter_mut().enumerate() { - if matches!(stmt, Statement::ExpressionStatement(expr_stmt) if expr_stmt.expression.is_super_call_expression()) - { - let assignment = self.create_assignment_to_this_temp_var(); - let assignment = self.ctx.ast.statement_expression(SPAN, assignment); - statements.insert(index + 1, assignment); - // `super();` found as top-level statement in this block of statements. - // No need to continue visiting later statements, because `_this` is definitely assigned - // to at this point - no need to assign to it again. - // This means we don't visit the whole constructor in the common case where `super();` - // appears as a top-level statement early in class constructor - // `constructor() { super(); blah; blah; blah; }`. - break; + if let Statement::ExpressionStatement(expr_stmt) = stmt { + if let Expression::CallExpression(call_expr) = &mut expr_stmt.expression { + if matches!(&call_expr.callee, Expression::Super(_)) { + // Visit arguments in `super(x, y, z)` call. + // Required to handle edge case `super(super(), f = () => this)`. + self.visit_arguments(&mut call_expr.arguments); + + // Insert `_this = this;` after `super();` + let assignment = self.create_assignment_to_this_temp_var(); + let assignment = self.ctx.ast.statement_expression(SPAN, assignment); + statements.insert(index + 1, assignment); + + // `super();` found as top-level statement in this block of statements. + // No need to continue visiting later statements, because `_this` is definitely + // assigned to at this point - no need to assign to it again. + // This means we don't visit the whole constructor in the common case where + // `super();` appears as a top-level statement early in class constructor + // `constructor() { super(); blah; blah; blah; }`. + break; + } + } } self.visit_statement(stmt); diff --git a/tasks/transform_conformance/snapshots/oxc.snap.md b/tasks/transform_conformance/snapshots/oxc.snap.md index b46bf18a9..5b221fbc9 100644 --- a/tasks/transform_conformance/snapshots/oxc.snap.md +++ b/tasks/transform_conformance/snapshots/oxc.snap.md @@ -1,6 +1,6 @@ commit: 54a8389f -Passed: 129/151 +Passed: 130/152 # All Passed: * babel-plugin-transform-class-static-block diff --git a/tasks/transform_conformance/snapshots/oxc_exec.snap.md b/tasks/transform_conformance/snapshots/oxc_exec.snap.md index 5eec08381..03bfc670b 100644 --- a/tasks/transform_conformance/snapshots/oxc_exec.snap.md +++ b/tasks/transform_conformance/snapshots/oxc_exec.snap.md @@ -2,7 +2,7 @@ commit: 54a8389f node: v22.12.0 -Passed: 3 of 5 (60.00%) +Passed: 4 of 6 (66.67%) Failures: diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-async-to-generator/test/fixtures/class/this-after-super-in-super/exec.js b/tasks/transform_conformance/tests/babel-plugin-transform-async-to-generator/test/fixtures/class/this-after-super-in-super/exec.js new file mode 100644 index 000000000..363b6f707 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-async-to-generator/test/fixtures/class/this-after-super-in-super/exec.js @@ -0,0 +1,16 @@ +let f; + +class S {} + +class C extends S { + constructor(x) { + super(super(), this.x = x, f = async () => this); + } +} + +try { new C(123) } catch {} + +return f().then((c) => { + expect(c).toBeInstanceOf(C); + expect(c.x).toBe(123); +}); diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-async-to-generator/test/fixtures/class/this-after-super-in-super/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-async-to-generator/test/fixtures/class/this-after-super-in-super/input.js new file mode 100644 index 000000000..c06902ca5 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-async-to-generator/test/fixtures/class/this-after-super-in-super/input.js @@ -0,0 +1,9 @@ +let f; + +class S {} + +class C extends S { + constructor(x) { + super(super(), this.x = x, f = async () => this); + } +} diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-async-to-generator/test/fixtures/class/this-after-super-in-super/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-async-to-generator/test/fixtures/class/this-after-super-in-super/output.js new file mode 100644 index 000000000..a6a09d357 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-async-to-generator/test/fixtures/class/this-after-super-in-super/output.js @@ -0,0 +1,17 @@ +let f; + +class S {} + +class C extends S { + constructor(x) { + var _this; + super( + (super(), _this = this), + this.x = x, + f = babelHelpers.asyncToGenerator(function* () { + return _this; + }) + ); + _this = this; + } +}