From 35f03db464d089c9e7ff148efcd9bb933b974811 Mon Sep 17 00:00:00 2001 From: Dunqing <29533304+Dunqing@users.noreply.github.com> Date: Sat, 31 Aug 2024 14:49:58 +0000 Subject: [PATCH] fix(transformer): `ArrowfunctionExpression`'s expression is true but has more than one body statement (#5366) close: #5363 We insert the new statement here, but it's broken if it a `() => x;`, we need to transform it to `function () { return x }` https://github.com/oxc-project/oxc/blob/8d565d5b23cd22e108881970c5acd10f7c77e80c/crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs#L59-L76 I don't where we should put the fallback logic, It's useful for all plugins. We had already done the same thing in the react refresh plugin. https://github.com/oxc-project/oxc/blob/8d565d5b23cd22e108881970c5acd10f7c77e80c/crates/oxc_transformer/src/react/refresh.rs#L578-L605 --- crates/oxc_transformer/src/lib.rs | 24 ++++++++++++++++++- tasks/transform_conformance/oxc.snap.md | 10 +++++++- .../test/fixtures/options.json | 3 +++ .../input.js | 1 + .../output.js | 4 ++++ 5 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/options.json create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/transform-in-arrow-function-expression/input.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/transform-in-arrow-function-expression/output.js diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index e2c7fc5e3..0f64bb4eb 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -39,7 +39,7 @@ use oxc_allocator::{Allocator, Vec}; use oxc_ast::{ast::*, Trivias}; use oxc_diagnostics::OxcDiagnostic; use oxc_semantic::{ScopeTree, SymbolTable}; -use oxc_span::SourceType; +use oxc_span::{SourceType, SPAN}; use oxc_traverse::{traverse_mut, Traverse, TraverseCtx}; pub use crate::{ @@ -284,6 +284,28 @@ impl<'a> Traverse<'a> for Transformer<'a> { self.x2_es2016.enter_statements(stmts, ctx); } + fn exit_arrow_function_expression( + &mut self, + arrow: &mut ArrowFunctionExpression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + // Some plugins may add new statements to the ArrowFunctionExpression's body, + // which can cause issues with the `() => x;` case, as it only allows a single statement. + // To address this, we wrap the last statement in a return statement and set the expression to false. + // This transforms the arrow function into the form `() => { return x; };`. + if arrow.expression && arrow.body.statements.len() > 1 { + let Statement::ExpressionStatement(statement) = arrow.body.statements.pop().unwrap() + else { + unreachable!("The last statement in an ArrowFunctionExpression should always be an ExpressionStatement.") + }; + arrow + .body + .statements + .push(ctx.ast.statement_return(SPAN, Some(statement.unbox().expression))); + arrow.expression = false; + } + } + fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) { self.x0_typescript.transform_statements_on_exit(stmts, ctx); self.x1_react.transform_statements_on_exit(stmts, ctx); diff --git a/tasks/transform_conformance/oxc.snap.md b/tasks/transform_conformance/oxc.snap.md index 57949f650..e7f97fc25 100644 --- a/tasks/transform_conformance/oxc.snap.md +++ b/tasks/transform_conformance/oxc.snap.md @@ -1,12 +1,20 @@ commit: 3bcfee23 -Passed: 10/37 +Passed: 10/38 # All Passed: * babel-plugin-transform-optional-catch-binding * babel-plugin-transform-arrow-functions +# babel-plugin-transform-nullish-coalescing-operator (0/1) +* transform-in-arrow-function-expression/input.js + x Reference flags mismatch: + | after transform: ReferenceId(3): ReferenceFlags(Write) + | rebuilt : ReferenceId(0): ReferenceFlags(Read | Write) + + + # babel-plugin-transform-typescript (2/8) * class-property-definition/input.ts x Unresolved references mismatch: diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/options.json b/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/options.json new file mode 100644 index 000000000..30d908eec --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["transform-nullish-coalescing-operator"] +} diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/transform-in-arrow-function-expression/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/transform-in-arrow-function-expression/input.js new file mode 100644 index 000000000..ce98f5b2d --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/transform-in-arrow-function-expression/input.js @@ -0,0 +1 @@ +(() => a ?? b); diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/transform-in-arrow-function-expression/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/transform-in-arrow-function-expression/output.js new file mode 100644 index 000000000..68b246e84 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/transform-in-arrow-function-expression/output.js @@ -0,0 +1,4 @@ +() => { + var _a; + return (_a = a) !== null && _a !== void 0 ? _a : b; +};