fix(transformer): arrow func transform maintain scope ID (#3633)

In arrow function transform, set `scope_id` of new `function () {}` to `scope_id` of arrow function it replaces, and alter scope flags.
This commit is contained in:
overlookmotel 2024-06-12 05:22:43 +00:00
parent f6752b482f
commit 39bdebc90e
3 changed files with 52 additions and 20 deletions

View file

@ -1,6 +1,10 @@
use std::cell::Cell;
use oxc_allocator::Vec;
use oxc_ast::ast::*;
use oxc_span::{Atom, SPAN};
use oxc_syntax::scope::ScopeFlags;
use oxc_traverse::TraverseCtx;
use serde::Deserialize;
use crate::context::Ctx;
@ -140,6 +144,7 @@ impl<'a> ArrowFunctions<'a> {
fn transform_arrow_function_expression(
&mut self,
arrow_function_expr: &mut ArrowFunctionExpression<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
let mut body = self.ctx.ast.copy(&arrow_function_expr.body);
@ -154,21 +159,39 @@ impl<'a> ArrowFunctions<'a> {
}
}
let new_function = self.ctx.ast.function(
FunctionType::FunctionExpression,
arrow_function_expr.span,
None,
false,
arrow_function_expr.r#async,
None,
self.ctx.ast.copy(&arrow_function_expr.params),
Some(body),
self.ctx.ast.copy(&arrow_function_expr.type_parameters),
self.ctx.ast.copy(&arrow_function_expr.return_type),
Modifiers::empty(),
);
// There shouldn't need to be a conditional here. Every arrow function should have a scope ID.
// But at present TS transforms don't seem to set `scope_id` in some cases, so this test case
// fails if just unwrap `scope_id`:
// `typescript/tests/cases/compiler/classFieldSuperAccessible.ts`.
// ```ts
// class D {
// accessor b = () => {}
// }
// ```
// TODO: Change to `arrow_function_expr.scope_id.get().unwrap()` once scopes are correct
// in TS transforms.
let scope_id = arrow_function_expr.scope_id.get();
if let Some(scope_id) = scope_id {
let flags = ctx.scopes_mut().get_flags_mut(scope_id);
*flags &= !ScopeFlags::Arrow;
}
Expression::FunctionExpression(new_function)
let new_function = Function {
r#type: FunctionType::FunctionExpression,
span: arrow_function_expr.span,
id: None,
generator: false,
r#async: arrow_function_expr.r#async,
this_param: None,
params: self.ctx.ast.copy(&arrow_function_expr.params),
body: Some(body),
type_parameters: self.ctx.ast.copy(&arrow_function_expr.type_parameters),
return_type: self.ctx.ast.copy(&arrow_function_expr.return_type),
modifiers: Modifiers::empty(),
scope_id: Cell::new(scope_id),
};
Expression::FunctionExpression(self.ctx.ast.alloc(new_function))
}
pub fn transform_expression(&mut self, expr: &mut Expression<'a>) {
@ -181,7 +204,11 @@ impl<'a> ArrowFunctions<'a> {
}
}
pub fn transform_expression_on_exit(&mut self, expr: &mut Expression<'a>) {
pub fn transform_expression_on_exit(
&mut self,
expr: &mut Expression<'a>,
ctx: &mut TraverseCtx<'a>,
) {
match expr {
Expression::ThisExpression(this_expr) => {
if !self.is_inside_arrow_function() {
@ -195,7 +222,7 @@ impl<'a> ArrowFunctions<'a> {
));
}
Expression::ArrowFunctionExpression(arrow_function_expr) => {
*expr = self.transform_arrow_function_expression(arrow_function_expr);
*expr = self.transform_arrow_function_expression(arrow_function_expr, ctx);
self.stacks.pop();
}
Expression::FunctionExpression(_) => {

View file

@ -6,6 +6,7 @@ pub use options::ES2015Options;
use oxc_allocator::Vec;
use oxc_ast::ast::*;
use oxc_traverse::TraverseCtx;
use std::rc::Rc;
use crate::context::Ctx;
@ -61,9 +62,13 @@ impl<'a> ES2015<'a> {
}
}
pub fn transform_expression_on_exit(&mut self, expr: &mut Expression<'a>) {
pub fn transform_expression_on_exit(
&mut self,
expr: &mut Expression<'a>,
ctx: &mut TraverseCtx<'a>,
) {
if self.options.arrow_function.is_some() {
self.arrow_functions.transform_expression_on_exit(expr);
self.arrow_functions.transform_expression_on_exit(expr, ctx);
}
}

View file

@ -154,8 +154,8 @@ impl<'a> Traverse<'a> for Transformer<'a> {
self.x3_es2015.transform_expression(expr);
}
fn exit_expression(&mut self, expr: &mut Expression<'a>, _ctx: &mut TraverseCtx<'a>) {
self.x3_es2015.transform_expression_on_exit(expr);
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
self.x3_es2015.transform_expression_on_exit(expr, ctx);
}
fn enter_simple_assignment_target(