Skip to content

Commit d512cf4

Browse files
committed
fix(transformer): fix spans and scopes in TS enum transform
1 parent e4b4cdf commit d512cf4

File tree

5 files changed

+164
-45
lines changed

5 files changed

+164
-45
lines changed

crates/oxc_transformer/src/helpers/bindings.rs

+3-8
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ impl<'a> BoundIdentifier<'a> {
3737

3838
/// Create `IdentifierReference` referencing this binding which is read from
3939
/// in current scope
40-
pub fn create_read_reference(&self, ctx: &mut TraverseCtx) -> IdentifierReference<'a> {
40+
pub fn create_read_reference(&self, ctx: &mut TraverseCtx<'a>) -> IdentifierReference<'a> {
4141
self.create_spanned_read_reference(SPAN, ctx)
4242
}
4343

@@ -46,14 +46,9 @@ impl<'a> BoundIdentifier<'a> {
4646
pub fn create_spanned_read_reference(
4747
&self,
4848
span: Span,
49-
ctx: &mut TraverseCtx,
49+
ctx: &mut TraverseCtx<'a>,
5050
) -> IdentifierReference<'a> {
51-
let reference_id = ctx.create_bound_reference(
52-
self.name.to_compact_str(),
53-
self.symbol_id,
54-
ReferenceFlag::Read,
55-
);
56-
IdentifierReference::new_read(span, self.name.clone(), Some(reference_id))
51+
ctx.create_bound_reference_id(span, self.name.clone(), self.symbol_id, ReferenceFlag::Read)
5752
}
5853

5954
/// Create `BindingIdentifier` for this binding

crates/oxc_transformer/src/typescript/enum.rs

+64-32
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
use std::cell::Cell;
2+
13
use oxc_allocator::Vec;
24
use oxc_ast::{ast::*, visit::walk_mut, VisitMut};
3-
use oxc_span::{Atom, SPAN};
5+
use oxc_span::{Atom, Span, SPAN};
46
use oxc_syntax::{
57
number::{NumberBase, ToJsInt32, ToJsString},
68
operator::{AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator},
9+
reference::ReferenceFlag,
10+
symbol::SymbolFlags,
711
};
812
use oxc_traverse::TraverseCtx;
913
use rustc_hash::FxHashMap;
@@ -20,14 +24,14 @@ impl<'a> TypeScriptEnum<'a> {
2024
Self { ctx, enums: FxHashMap::default() }
2125
}
2226

23-
pub fn transform_statement(&mut self, stmt: &mut Statement<'a>, ctx: &TraverseCtx<'a>) {
27+
pub fn transform_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
2428
let new_stmt = match stmt {
2529
Statement::TSEnumDeclaration(ts_enum_decl) => {
26-
self.transform_ts_enum(ts_enum_decl, false, ctx)
30+
self.transform_ts_enum(ts_enum_decl, None, ctx)
2731
}
2832
Statement::ExportNamedDeclaration(decl) => {
2933
if let Some(Declaration::TSEnumDeclaration(ts_enum_decl)) = &decl.declaration {
30-
self.transform_ts_enum(ts_enum_decl, true, ctx)
34+
self.transform_ts_enum(ts_enum_decl, Some(decl.span), ctx)
3135
} else {
3236
None
3337
}
@@ -56,16 +60,29 @@ impl<'a> TypeScriptEnum<'a> {
5660
fn transform_ts_enum(
5761
&mut self,
5862
decl: &TSEnumDeclaration<'a>,
59-
is_export: bool,
60-
ctx: &TraverseCtx<'a>,
63+
export_span: Option<Span>,
64+
ctx: &mut TraverseCtx<'a>,
6165
) -> Option<Statement<'a>> {
6266
if decl.declare {
6367
return None;
6468
}
6569

70+
let is_export = export_span.is_some();
6671
let is_not_top_scope = !ctx.scopes().get_flags(ctx.current_scope_id()).is_top();
67-
let span = decl.span;
68-
let ident = decl.id.clone();
72+
73+
let enum_name = decl.id.name.clone();
74+
let func_scope_id = decl.scope_id.get().unwrap();
75+
let param_symbol_id = ctx.symbols_mut().create_symbol(
76+
decl.id.span,
77+
enum_name.to_compact_str(),
78+
SymbolFlags::FunctionScopedVariable,
79+
func_scope_id,
80+
);
81+
let ident = BindingIdentifier {
82+
span: decl.id.span,
83+
name: decl.id.name.clone(),
84+
symbol_id: Cell::new(Some(param_symbol_id)),
85+
};
6986
let kind = self.ctx.ast.binding_pattern_identifier(ident);
7087
let id = self.ctx.ast.binding_pattern(kind, None, false);
7188

@@ -81,25 +98,39 @@ impl<'a> TypeScriptEnum<'a> {
8198
);
8299

83100
// Foo[Foo["X"] = 0] = "X";
84-
let enum_name = decl.id.name.clone();
85101
let is_already_declared = self.enums.contains_key(&enum_name);
86102
let statements = self.transform_ts_enum_members(&decl.members, enum_name.clone(), ctx);
87103
let body = self.ctx.ast.function_body(decl.span, self.ctx.ast.new_vec(), statements);
88-
let r#type = FunctionType::FunctionExpression;
89-
let callee = self.ctx.ast.plain_function(r#type, SPAN, None, params, Some(body));
90-
let callee = Expression::FunctionExpression(callee);
104+
let callee = Expression::FunctionExpression(ctx.alloc(Function {
105+
r#type: FunctionType::FunctionExpression,
106+
span: SPAN,
107+
id: None,
108+
generator: false,
109+
r#async: false,
110+
declare: false,
111+
this_param: None,
112+
params,
113+
body: Some(body),
114+
type_parameters: None,
115+
return_type: None,
116+
scope_id: Cell::new(Some(func_scope_id)),
117+
}));
91118

119+
let var_symbol_id = decl.id.symbol_id.get().unwrap();
92120
let arguments = if (is_export || is_not_top_scope) && !is_already_declared {
93121
// }({});
94122
let object_expr = self.ctx.ast.object_expression(SPAN, self.ctx.ast.new_vec(), None);
95123
self.ctx.ast.new_vec_single(Argument::from(object_expr))
96124
} else {
97125
// }(Foo || {});
98126
let op = LogicalOperator::Or;
99-
let left = self
100-
.ctx
101-
.ast
102-
.identifier_reference_expression(IdentifierReference::new(SPAN, enum_name.clone()));
127+
let left = ctx.create_bound_reference_id(
128+
decl.id.span,
129+
enum_name.clone(),
130+
var_symbol_id,
131+
ReferenceFlag::Read,
132+
);
133+
let left = ctx.ast.identifier_reference_expression(left);
103134
let right = self.ctx.ast.object_expression(SPAN, self.ctx.ast.new_vec(), None);
104135
let expression = self.ctx.ast.logical_expression(SPAN, left, op, right);
105136
self.ctx.ast.new_vec_single(Argument::from(expression))
@@ -109,12 +140,15 @@ impl<'a> TypeScriptEnum<'a> {
109140

110141
if is_already_declared {
111142
let op = AssignmentOperator::Assign;
112-
let left = self.ctx.ast.simple_assignment_target_identifier(IdentifierReference::new(
113-
SPAN,
143+
let left = ctx.create_bound_reference_id(
144+
decl.id.span,
114145
enum_name.clone(),
115-
));
146+
var_symbol_id,
147+
ReferenceFlag::Write,
148+
);
149+
let left = ctx.ast.simple_assignment_target_identifier(left);
116150
let expr = self.ctx.ast.assignment_expression(SPAN, op, left, call_expression);
117-
return Some(self.ctx.ast.expression_statement(SPAN, expr));
151+
return Some(self.ctx.ast.expression_statement(decl.span, expr));
118152
}
119153

120154
let kind = if is_export || is_not_top_scope {
@@ -123,25 +157,21 @@ impl<'a> TypeScriptEnum<'a> {
123157
VariableDeclarationKind::Var
124158
};
125159
let decls = {
126-
let mut decls = self.ctx.ast.new_vec();
127-
128-
let binding_identifier = BindingIdentifier::new(SPAN, enum_name.clone());
160+
let binding_identifier = decl.id.clone();
129161
let binding_pattern_kind = self.ctx.ast.binding_pattern_identifier(binding_identifier);
130162
let binding = self.ctx.ast.binding_pattern(binding_pattern_kind, None, false);
131163
let decl =
132164
self.ctx.ast.variable_declarator(SPAN, kind, binding, Some(call_expression), false);
133-
134-
decls.push(decl);
135-
decls
165+
ctx.ast.new_vec_single(decl)
136166
};
137-
let variable_declaration = self.ctx.ast.variable_declaration(span, kind, decls, false);
167+
let variable_declaration = self.ctx.ast.variable_declaration(decl.span, kind, decls, false);
138168
let variable_declaration = Declaration::VariableDeclaration(variable_declaration);
139169

140-
let stmt = if is_export {
141-
let declaration =
142-
self.ctx.ast.plain_export_named_declaration_declaration(SPAN, variable_declaration);
143-
144-
self.ctx.ast.module_declaration(ModuleDeclaration::ExportNamedDeclaration(declaration))
170+
let stmt = if let Some(export_span) = export_span {
171+
let declaration = ctx
172+
.ast
173+
.plain_export_named_declaration_declaration(export_span, variable_declaration);
174+
Statement::ExportNamedDeclaration(declaration)
145175
} else {
146176
Statement::from(variable_declaration)
147177
};
@@ -154,6 +184,8 @@ impl<'a> TypeScriptEnum<'a> {
154184
enum_name: Atom<'a>,
155185
ctx: &TraverseCtx<'a>,
156186
) -> Vec<'a, Statement<'a>> {
187+
// TODO: Set `span` and `references_id` on all `IdentifierReference`s created here
188+
157189
let mut statements = self.ctx.ast.new_vec();
158190
let mut prev_constant_value = Some(ConstantValue::Number(-1.0));
159191
let mut previous_enum_members = self.enums.entry(enum_name.clone()).or_default().clone();

crates/oxc_transformer/src/typescript/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ impl<'a> TypeScript<'a> {
167167
self.annotations.transform_statements_on_exit(stmts, ctx);
168168
}
169169

170-
pub fn transform_statement(&mut self, stmt: &mut Statement<'a>, ctx: &TraverseCtx<'a>) {
170+
pub fn transform_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
171171
self.r#enum.transform_statement(stmt, ctx);
172172
}
173173

crates/oxc_traverse/src/context/mod.rs

+43-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use oxc_allocator::{Allocator, Box};
22
use oxc_ast::{
3-
ast::{Expression, Statement},
3+
ast::{Expression, IdentifierReference, Statement},
44
AstBuilder,
55
};
66
use oxc_semantic::{ScopeTree, SymbolTable};
7-
use oxc_span::CompactStr;
7+
use oxc_span::{Atom, CompactStr, Span};
88
use oxc_syntax::{
99
reference::{ReferenceFlag, ReferenceId},
1010
scope::{ScopeFlags, ScopeId},
@@ -358,6 +358,19 @@ impl<'a> TraverseCtx<'a> {
358358
self.scoping.create_bound_reference(name, symbol_id, flag)
359359
}
360360

361+
/// Create an `IdentifierReference` bound to a `SymbolId`.
362+
///
363+
/// This is a shortcut for `ctx.scoping.create_bound_reference_id`.
364+
pub fn create_bound_reference_id(
365+
&mut self,
366+
span: Span,
367+
name: Atom<'a>,
368+
symbol_id: SymbolId,
369+
flag: ReferenceFlag,
370+
) -> IdentifierReference<'a> {
371+
self.scoping.create_bound_reference_id(span, name, symbol_id, flag)
372+
}
373+
361374
/// Create an unbound reference.
362375
///
363376
/// This is a shortcut for `ctx.scoping.create_unbound_reference`.
@@ -369,6 +382,18 @@ impl<'a> TraverseCtx<'a> {
369382
self.scoping.create_unbound_reference(name, flag)
370383
}
371384

385+
/// Create an unbound `IdentifierReference`.
386+
///
387+
/// This is a shortcut for `ctx.scoping.create_unbound_reference_id`.
388+
pub fn create_unbound_reference_id(
389+
&mut self,
390+
span: Span,
391+
name: Atom<'a>,
392+
flag: ReferenceFlag,
393+
) -> IdentifierReference<'a> {
394+
self.scoping.create_unbound_reference_id(span, name, flag)
395+
}
396+
372397
/// Create a reference optionally bound to a `SymbolId`.
373398
///
374399
/// If you know if there's a `SymbolId` or not, prefer `TraverseCtx::create_bound_reference`
@@ -384,6 +409,22 @@ impl<'a> TraverseCtx<'a> {
384409
self.scoping.create_reference(name, symbol_id, flag)
385410
}
386411

412+
/// Create an `IdentifierReference` optionally bound to a `SymbolId`.
413+
///
414+
/// If you know if there's a `SymbolId` or not, prefer `TraverseCtx::create_bound_reference_id`
415+
/// or `TraverseCtx::create_unbound_reference_id`.
416+
///
417+
/// This is a shortcut for `ctx.scoping.create_reference_id`.
418+
pub fn create_reference_id(
419+
&mut self,
420+
span: Span,
421+
name: Atom<'a>,
422+
symbol_id: Option<SymbolId>,
423+
flag: ReferenceFlag,
424+
) -> IdentifierReference<'a> {
425+
self.scoping.create_reference_id(span, name, symbol_id, flag)
426+
}
427+
387428
/// Create reference in current scope, looking up binding for `name`,
388429
///
389430
/// This is a shortcut for `ctx.scoping.create_reference_in_current_scope`.

crates/oxc_traverse/src/context/scoping.rs

+53-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::str;
1+
use std::{cell::Cell, str};
22

33
use compact_str::{format_compact, CompactString};
44
#[allow(clippy::wildcard_imports)]
@@ -7,7 +7,7 @@ use oxc_ast::{
77
visit::{walk, Visit},
88
};
99
use oxc_semantic::{AstNodeId, Reference, ScopeTree, SymbolTable};
10-
use oxc_span::{CompactStr, SPAN};
10+
use oxc_span::{Atom, CompactStr, Span, SPAN};
1111
use oxc_syntax::{
1212
reference::{ReferenceFlag, ReferenceId},
1313
scope::{ScopeFlags, ScopeId},
@@ -270,6 +270,23 @@ impl TraverseScoping {
270270
reference_id
271271
}
272272

273+
/// Create an `IdentifierReference` bound to a `SymbolId`
274+
pub fn create_bound_reference_id<'a>(
275+
&mut self,
276+
span: Span,
277+
name: Atom<'a>,
278+
symbol_id: SymbolId,
279+
flag: ReferenceFlag,
280+
) -> IdentifierReference<'a> {
281+
let reference_id = self.create_bound_reference(name.to_compact_str(), symbol_id, flag);
282+
IdentifierReference {
283+
span,
284+
name,
285+
reference_id: Cell::new(Some(reference_id)),
286+
reference_flag: flag,
287+
}
288+
}
289+
273290
/// Create an unbound reference
274291
pub fn create_unbound_reference(
275292
&mut self,
@@ -282,6 +299,22 @@ impl TraverseScoping {
282299
reference_id
283300
}
284301

302+
/// Create an unbound `IdentifierReference`
303+
pub fn create_unbound_reference_id<'a>(
304+
&mut self,
305+
span: Span,
306+
name: Atom<'a>,
307+
flag: ReferenceFlag,
308+
) -> IdentifierReference<'a> {
309+
let reference_id = self.create_unbound_reference(name.to_compact_str(), flag);
310+
IdentifierReference {
311+
span,
312+
name,
313+
reference_id: Cell::new(Some(reference_id)),
314+
reference_flag: flag,
315+
}
316+
}
317+
285318
/// Create a reference optionally bound to a `SymbolId`.
286319
///
287320
/// If you know if there's a `SymbolId` or not, prefer `TraverseCtx::create_bound_reference`
@@ -299,6 +332,24 @@ impl TraverseScoping {
299332
}
300333
}
301334

335+
/// Create an `IdentifierReference` optionally bound to a `SymbolId`.
336+
///
337+
/// If you know if there's a `SymbolId` or not, prefer `TraverseCtx::create_bound_reference_id`
338+
/// or `TraverseCtx::create_unbound_reference_id`.
339+
pub fn create_reference_id<'a>(
340+
&mut self,
341+
span: Span,
342+
name: Atom<'a>,
343+
symbol_id: Option<SymbolId>,
344+
flag: ReferenceFlag,
345+
) -> IdentifierReference<'a> {
346+
if let Some(symbol_id) = symbol_id {
347+
self.create_bound_reference_id(span, name, symbol_id, flag)
348+
} else {
349+
self.create_unbound_reference_id(span, name, flag)
350+
}
351+
}
352+
302353
/// Create reference in current scope, looking up binding for `name`
303354
pub fn create_reference_in_current_scope(
304355
&mut self,

0 commit comments

Comments
 (0)