use std::cell::Cell;
use rustc_hash::FxHashMap;
use oxc_ast::ast::*;
use oxc_ast_visit::Visit;
use oxc_data_structures::stack::Stack;
use oxc_str::Ident;
use oxc_syntax::{
scope::{ScopeFlags, ScopeId},
symbol::SymbolId,
};
use crate::context::TraverseCtx;
use super::ClassProperties;
impl<'a> ClassProperties<'a> {
pub(super) fn reparent_initializers_scope(
&mut self,
inits: &[Expression<'a>],
instance_inits_scope_id: ScopeId,
instance_inits_constructor_scope_id: Option<ScopeId>,
ctx: &mut TraverseCtx<'a>,
) {
if let Some(constructor_scope_id) = instance_inits_constructor_scope_id {
let mut updater = InstanceInitializerVisitor::new(
instance_inits_scope_id,
constructor_scope_id,
self,
ctx,
);
for init in inits {
updater.visit_expression(init);
}
} else {
let mut updater = FastInstanceInitializerVisitor::new(instance_inits_scope_id, ctx);
for init in inits {
updater.visit_expression(init);
}
}
}
}
struct InstanceInitializerVisitor<'a, 'v> {
scope_ids_stack: Stack<ScopeId>,
parent_scope_id: ScopeId,
constructor_scope_id: ScopeId,
clashing_symbols: &'v mut FxHashMap<SymbolId, Ident<'a>>,
ctx: &'v mut TraverseCtx<'a>,
}
impl<'a, 'v> InstanceInitializerVisitor<'a, 'v> {
fn new(
instance_inits_scope_id: ScopeId,
constructor_scope_id: ScopeId,
class_properties: &'v mut ClassProperties<'a>,
ctx: &'v mut TraverseCtx<'a>,
) -> Self {
Self {
scope_ids_stack: Stack::new(),
parent_scope_id: instance_inits_scope_id,
constructor_scope_id,
clashing_symbols: &mut class_properties.clashing_constructor_symbols,
ctx,
}
}
}
impl<'a> Visit<'a> for InstanceInitializerVisitor<'a, '_> {
#[inline]
fn enter_scope(&mut self, _flags: ScopeFlags, scope_id: &Cell<Option<ScopeId>>) {
let scope_id = scope_id.get().unwrap();
if self.scope_ids_stack.is_empty() {
self.reparent_scope(scope_id);
}
self.scope_ids_stack.push(scope_id);
}
#[inline]
fn leave_scope(&mut self) {
self.scope_ids_stack.pop();
}
#[inline]
fn visit_identifier_reference(&mut self, ident: &IdentifierReference<'a>) {
self.check_for_symbol_clash(ident);
}
}
impl<'a> InstanceInitializerVisitor<'a, '_> {
fn reparent_scope(&mut self, scope_id: ScopeId) {
self.ctx.scoping_mut().change_scope_parent_id(scope_id, Some(self.parent_scope_id));
}
fn check_for_symbol_clash(&mut self, ident: &IdentifierReference<'a>) {
let Some(constructor_symbol_id) =
self.ctx.scoping().get_binding(self.constructor_scope_id, ident.name)
else {
return;
};
let reference_id = ident.reference_id();
if let Some(ident_symbol_id) = self.ctx.scoping().get_reference(reference_id).symbol_id() {
let scope_id = self.ctx.scoping().symbol_scope_id(ident_symbol_id);
if self.scope_ids_stack.contains(&scope_id) {
return;
}
}
self.clashing_symbols.entry(constructor_symbol_id).or_insert(ident.name);
}
}
struct FastInstanceInitializerVisitor<'a, 'v> {
parent_scope_id: ScopeId,
ctx: &'v mut TraverseCtx<'a>,
}
impl<'a, 'v> FastInstanceInitializerVisitor<'a, 'v> {
fn new(instance_inits_scope_id: ScopeId, ctx: &'v mut TraverseCtx<'a>) -> Self {
Self { parent_scope_id: instance_inits_scope_id, ctx }
}
}
impl<'a> Visit<'a> for FastInstanceInitializerVisitor<'a, '_> {
#[inline]
fn visit_function(&mut self, func: &Function<'a>, _flags: ScopeFlags) {
self.reparent_scope(&func.scope_id);
}
#[inline]
fn visit_arrow_function_expression(&mut self, func: &ArrowFunctionExpression<'a>) {
self.reparent_scope(&func.scope_id);
}
#[inline]
fn visit_class(&mut self, class: &Class<'a>) {
self.visit_decorators(&class.decorators);
self.reparent_scope(&class.scope_id);
}
#[inline]
fn visit_ts_conditional_type(&mut self, conditional: &TSConditionalType<'a>) {
self.visit_ts_type(&conditional.check_type);
self.reparent_scope(&conditional.scope_id);
self.visit_ts_type(&conditional.false_type);
}
#[inline]
fn visit_ts_method_signature(&mut self, signature: &TSMethodSignature<'a>) {
self.reparent_scope(&signature.scope_id);
}
#[inline]
fn visit_ts_construct_signature_declaration(
&mut self,
signature: &TSConstructSignatureDeclaration<'a>,
) {
self.reparent_scope(&signature.scope_id);
}
#[inline]
fn visit_ts_mapped_type(&mut self, mapped: &TSMappedType<'a>) {
self.reparent_scope(&mapped.scope_id);
}
}
impl FastInstanceInitializerVisitor<'_, '_> {
fn reparent_scope(&mut self, scope_id: &Cell<Option<ScopeId>>) {
let scope_id = scope_id.get().unwrap();
self.ctx.scoping_mut().change_scope_parent_id(scope_id, Some(self.parent_scope_id));
}
}