diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index 68ace391b9094..c6fe4cae68c4d 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -1590,14 +1590,14 @@ pub struct Class<'a> { #[cfg_attr(feature = "serialize", serde(flatten))] pub span: Span, pub decorators: Vec<'a, Decorator<'a>>, - #[scope(enter_before)] pub id: Option>, + #[scope(enter_before)] + pub type_parameters: Option>>, #[visit_as(ClassHeritage)] pub super_class: Option>, - pub body: Box<'a, ClassBody<'a>>, - pub type_parameters: Option>>, pub super_type_parameters: Option>>, pub implements: Option>>, + pub body: Box<'a, ClassBody<'a>>, pub r#abstract: bool, pub declare: bool, pub scope_id: Cell>, diff --git a/crates/oxc_ast/src/generated/ast_builder.rs b/crates/oxc_ast/src/generated/ast_builder.rs index a254161a27360..5034aef8b361a 100644 --- a/crates/oxc_ast/src/generated/ast_builder.rs +++ b/crates/oxc_ast/src/generated/ast_builder.rs @@ -485,29 +485,29 @@ impl<'a> AstBuilder<'a> { span: Span, decorators: Vec<'a, Decorator<'a>>, id: Option>, + type_parameters: T1, super_class: Option>, - body: T1, - type_parameters: T2, - super_type_parameters: T3, + super_type_parameters: T2, implements: Option>>, + body: T3, r#abstract: bool, declare: bool, ) -> Expression<'a> where - T1: IntoIn<'a, Box<'a, ClassBody<'a>>>, - T2: IntoIn<'a, Option>>>, - T3: IntoIn<'a, Option>>>, + T1: IntoIn<'a, Option>>>, + T2: IntoIn<'a, Option>>>, + T3: IntoIn<'a, Box<'a, ClassBody<'a>>>, { Expression::ClassExpression(self.alloc(self.class( r#type, span, decorators, id, - super_class, - body, type_parameters, + super_class, super_type_parameters, implements, + body, r#abstract, declare, ))) @@ -2728,29 +2728,29 @@ impl<'a> AstBuilder<'a> { span: Span, decorators: Vec<'a, Decorator<'a>>, id: Option>, + type_parameters: T1, super_class: Option>, - body: T1, - type_parameters: T2, - super_type_parameters: T3, + super_type_parameters: T2, implements: Option>>, + body: T3, r#abstract: bool, declare: bool, ) -> Declaration<'a> where - T1: IntoIn<'a, Box<'a, ClassBody<'a>>>, - T2: IntoIn<'a, Option>>>, - T3: IntoIn<'a, Option>>>, + T1: IntoIn<'a, Option>>>, + T2: IntoIn<'a, Option>>>, + T3: IntoIn<'a, Box<'a, ClassBody<'a>>>, { Declaration::ClassDeclaration(self.alloc(self.class( r#type, span, decorators, id, - super_class, - body, type_parameters, + super_class, super_type_parameters, implements, + body, r#abstract, declare, ))) @@ -3942,29 +3942,29 @@ impl<'a> AstBuilder<'a> { span: Span, decorators: Vec<'a, Decorator<'a>>, id: Option>, + type_parameters: T1, super_class: Option>, - body: T1, - type_parameters: T2, - super_type_parameters: T3, + super_type_parameters: T2, implements: Option>>, + body: T3, r#abstract: bool, declare: bool, ) -> Class<'a> where - T1: IntoIn<'a, Box<'a, ClassBody<'a>>>, - T2: IntoIn<'a, Option>>>, - T3: IntoIn<'a, Option>>>, + T1: IntoIn<'a, Option>>>, + T2: IntoIn<'a, Option>>>, + T3: IntoIn<'a, Box<'a, ClassBody<'a>>>, { Class { r#type, span, decorators, id, - super_class, - body: body.into_in(self.allocator), type_parameters: type_parameters.into_in(self.allocator), + super_class, super_type_parameters: super_type_parameters.into_in(self.allocator), implements, + body: body.into_in(self.allocator), r#abstract, declare, scope_id: Default::default(), @@ -3978,29 +3978,29 @@ impl<'a> AstBuilder<'a> { span: Span, decorators: Vec<'a, Decorator<'a>>, id: Option>, + type_parameters: T1, super_class: Option>, - body: T1, - type_parameters: T2, - super_type_parameters: T3, + super_type_parameters: T2, implements: Option>>, + body: T3, r#abstract: bool, declare: bool, ) -> Box<'a, Class<'a>> where - T1: IntoIn<'a, Box<'a, ClassBody<'a>>>, - T2: IntoIn<'a, Option>>>, - T3: IntoIn<'a, Option>>>, + T1: IntoIn<'a, Option>>>, + T2: IntoIn<'a, Option>>>, + T3: IntoIn<'a, Box<'a, ClassBody<'a>>>, { self.class( r#type, span, decorators, id, - super_class, - body, type_parameters, + super_class, super_type_parameters, implements, + body, r#abstract, declare, ) @@ -4943,29 +4943,29 @@ impl<'a> AstBuilder<'a> { span: Span, decorators: Vec<'a, Decorator<'a>>, id: Option>, + type_parameters: T1, super_class: Option>, - body: T1, - type_parameters: T2, - super_type_parameters: T3, + super_type_parameters: T2, implements: Option>>, + body: T3, r#abstract: bool, declare: bool, ) -> ExportDefaultDeclarationKind<'a> where - T1: IntoIn<'a, Box<'a, ClassBody<'a>>>, - T2: IntoIn<'a, Option>>>, - T3: IntoIn<'a, Option>>>, + T1: IntoIn<'a, Option>>>, + T2: IntoIn<'a, Option>>>, + T3: IntoIn<'a, Box<'a, ClassBody<'a>>>, { ExportDefaultDeclarationKind::ClassDeclaration(self.alloc(self.class( r#type, span, decorators, id, - super_class, - body, type_parameters, + super_class, super_type_parameters, implements, + body, r#abstract, declare, ))) diff --git a/crates/oxc_ast/src/generated/visit.rs b/crates/oxc_ast/src/generated/visit.rs index fa2697957a008..d1ee257ae5401 100644 --- a/crates/oxc_ast/src/generated/visit.rs +++ b/crates/oxc_ast/src/generated/visit.rs @@ -803,6 +803,16 @@ pub trait Visit<'a>: Sized { walk_class_heritage(self, it); } + #[inline] + fn visit_ts_class_implementses(&mut self, it: &Vec<'a, TSClassImplements<'a>>) { + walk_ts_class_implementses(self, it); + } + + #[inline] + fn visit_ts_class_implements(&mut self, it: &TSClassImplements<'a>) { + walk_ts_class_implements(self, it); + } + #[inline] fn visit_class_body(&mut self, it: &ClassBody<'a>) { walk_class_body(self, it); @@ -843,16 +853,6 @@ pub trait Visit<'a>: Sized { walk_accessor_property(self, it); } - #[inline] - fn visit_ts_class_implementses(&mut self, it: &Vec<'a, TSClassImplements<'a>>) { - walk_ts_class_implementses(self, it); - } - - #[inline] - fn visit_ts_class_implements(&mut self, it: &TSClassImplements<'a>) { - walk_ts_class_implements(self, it); - } - #[inline] fn visit_conditional_expression(&mut self, it: &ConditionalExpression<'a>) { walk_conditional_expression(self, it); @@ -2925,23 +2925,23 @@ pub mod walk { let kind = AstKind::Class(visitor.alloc(it)); visitor.enter_node(kind); visitor.visit_decorators(&it.decorators); - visitor.enter_scope(ScopeFlags::StrictMode, &it.scope_id); if let Some(id) = &it.id { visitor.visit_binding_identifier(id); } - if let Some(super_class) = &it.super_class { - visitor.visit_class_heritage(super_class); - } - visitor.visit_class_body(&it.body); + visitor.enter_scope(ScopeFlags::StrictMode, &it.scope_id); if let Some(type_parameters) = &it.type_parameters { visitor.visit_ts_type_parameter_declaration(type_parameters); } + if let Some(super_class) = &it.super_class { + visitor.visit_class_heritage(super_class); + } if let Some(super_type_parameters) = &it.super_type_parameters { visitor.visit_ts_type_parameter_instantiation(super_type_parameters); } if let Some(implements) = &it.implements { visitor.visit_ts_class_implementses(implements); } + visitor.visit_class_body(&it.body); visitor.leave_node(kind); visitor.leave_scope(); } @@ -2953,6 +2953,27 @@ pub mod walk { visitor.leave_node(kind); } + #[inline] + pub fn walk_ts_class_implementses<'a, V: Visit<'a>>( + visitor: &mut V, + it: &Vec<'a, TSClassImplements<'a>>, + ) { + for el in it.iter() { + visitor.visit_ts_class_implements(el); + } + } + + #[inline] + pub fn walk_ts_class_implements<'a, V: Visit<'a>>(visitor: &mut V, it: &TSClassImplements<'a>) { + let kind = AstKind::TSClassImplements(visitor.alloc(it)); + visitor.enter_node(kind); + visitor.visit_ts_type_name(&it.expression); + if let Some(type_parameters) = &it.type_parameters { + visitor.visit_ts_type_parameter_instantiation(type_parameters); + } + visitor.leave_node(kind); + } + #[inline] pub fn walk_class_body<'a, V: Visit<'a>>(visitor: &mut V, it: &ClassBody<'a>) { let kind = AstKind::ClassBody(visitor.alloc(it)); @@ -3072,27 +3093,6 @@ pub mod walk { } } - #[inline] - pub fn walk_ts_class_implementses<'a, V: Visit<'a>>( - visitor: &mut V, - it: &Vec<'a, TSClassImplements<'a>>, - ) { - for el in it.iter() { - visitor.visit_ts_class_implements(el); - } - } - - #[inline] - pub fn walk_ts_class_implements<'a, V: Visit<'a>>(visitor: &mut V, it: &TSClassImplements<'a>) { - let kind = AstKind::TSClassImplements(visitor.alloc(it)); - visitor.enter_node(kind); - visitor.visit_ts_type_name(&it.expression); - if let Some(type_parameters) = &it.type_parameters { - visitor.visit_ts_type_parameter_instantiation(type_parameters); - } - visitor.leave_node(kind); - } - #[inline] pub fn walk_conditional_expression<'a, V: Visit<'a>>( visitor: &mut V, diff --git a/crates/oxc_ast/src/generated/visit_mut.rs b/crates/oxc_ast/src/generated/visit_mut.rs index 6c89eb0823911..1de9b398859cc 100644 --- a/crates/oxc_ast/src/generated/visit_mut.rs +++ b/crates/oxc_ast/src/generated/visit_mut.rs @@ -795,6 +795,16 @@ pub trait VisitMut<'a>: Sized { walk_class_heritage(self, it); } + #[inline] + fn visit_ts_class_implementses(&mut self, it: &mut Vec<'a, TSClassImplements<'a>>) { + walk_ts_class_implementses(self, it); + } + + #[inline] + fn visit_ts_class_implements(&mut self, it: &mut TSClassImplements<'a>) { + walk_ts_class_implements(self, it); + } + #[inline] fn visit_class_body(&mut self, it: &mut ClassBody<'a>) { walk_class_body(self, it); @@ -835,16 +845,6 @@ pub trait VisitMut<'a>: Sized { walk_accessor_property(self, it); } - #[inline] - fn visit_ts_class_implementses(&mut self, it: &mut Vec<'a, TSClassImplements<'a>>) { - walk_ts_class_implementses(self, it); - } - - #[inline] - fn visit_ts_class_implements(&mut self, it: &mut TSClassImplements<'a>) { - walk_ts_class_implements(self, it); - } - #[inline] fn visit_conditional_expression(&mut self, it: &mut ConditionalExpression<'a>) { walk_conditional_expression(self, it); @@ -3049,23 +3049,23 @@ pub mod walk_mut { let kind = AstType::Class; visitor.enter_node(kind); visitor.visit_decorators(&mut it.decorators); - visitor.enter_scope(ScopeFlags::StrictMode, &it.scope_id); if let Some(id) = &mut it.id { visitor.visit_binding_identifier(id); } - if let Some(super_class) = &mut it.super_class { - visitor.visit_class_heritage(super_class); - } - visitor.visit_class_body(&mut it.body); + visitor.enter_scope(ScopeFlags::StrictMode, &it.scope_id); if let Some(type_parameters) = &mut it.type_parameters { visitor.visit_ts_type_parameter_declaration(type_parameters); } + if let Some(super_class) = &mut it.super_class { + visitor.visit_class_heritage(super_class); + } if let Some(super_type_parameters) = &mut it.super_type_parameters { visitor.visit_ts_type_parameter_instantiation(super_type_parameters); } if let Some(implements) = &mut it.implements { visitor.visit_ts_class_implementses(implements); } + visitor.visit_class_body(&mut it.body); visitor.leave_node(kind); visitor.leave_scope(); } @@ -3077,6 +3077,30 @@ pub mod walk_mut { visitor.leave_node(kind); } + #[inline] + pub fn walk_ts_class_implementses<'a, V: VisitMut<'a>>( + visitor: &mut V, + it: &mut Vec<'a, TSClassImplements<'a>>, + ) { + for el in it.iter_mut() { + visitor.visit_ts_class_implements(el); + } + } + + #[inline] + pub fn walk_ts_class_implements<'a, V: VisitMut<'a>>( + visitor: &mut V, + it: &mut TSClassImplements<'a>, + ) { + let kind = AstType::TSClassImplements; + visitor.enter_node(kind); + visitor.visit_ts_type_name(&mut it.expression); + if let Some(type_parameters) = &mut it.type_parameters { + visitor.visit_ts_type_parameter_instantiation(type_parameters); + } + visitor.leave_node(kind); + } + #[inline] pub fn walk_class_body<'a, V: VisitMut<'a>>(visitor: &mut V, it: &mut ClassBody<'a>) { let kind = AstType::ClassBody; @@ -3205,30 +3229,6 @@ pub mod walk_mut { } } - #[inline] - pub fn walk_ts_class_implementses<'a, V: VisitMut<'a>>( - visitor: &mut V, - it: &mut Vec<'a, TSClassImplements<'a>>, - ) { - for el in it.iter_mut() { - visitor.visit_ts_class_implements(el); - } - } - - #[inline] - pub fn walk_ts_class_implements<'a, V: VisitMut<'a>>( - visitor: &mut V, - it: &mut TSClassImplements<'a>, - ) { - let kind = AstType::TSClassImplements; - visitor.enter_node(kind); - visitor.visit_ts_type_name(&mut it.expression); - if let Some(type_parameters) = &mut it.type_parameters { - visitor.visit_ts_type_parameter_instantiation(type_parameters); - } - visitor.leave_node(kind); - } - #[inline] pub fn walk_conditional_expression<'a, V: VisitMut<'a>>( visitor: &mut V, diff --git a/crates/oxc_isolated_declarations/src/class.rs b/crates/oxc_isolated_declarations/src/class.rs index 7c93e6f7300fd..20ef7f00915d8 100644 --- a/crates/oxc_isolated_declarations/src/class.rs +++ b/crates/oxc_isolated_declarations/src/class.rs @@ -490,11 +490,11 @@ impl<'a> IsolatedDeclarations<'a> { decl.span, self.ast.vec(), self.ast.copy(&decl.id), - self.ast.copy(&decl.super_class), - body, self.ast.copy(&decl.type_parameters), + self.ast.copy(&decl.super_class), self.ast.copy(&decl.super_type_parameters), self.ast.copy(&decl.implements), + body, decl.r#abstract, declare.unwrap_or_else(|| self.is_declare()), )) diff --git a/crates/oxc_linter/src/rules/nextjs/no_duplicate_head.rs b/crates/oxc_linter/src/rules/nextjs/no_duplicate_head.rs index 4be6dd539e167..ae376e75072ba 100644 --- a/crates/oxc_linter/src/rules/nextjs/no_duplicate_head.rs +++ b/crates/oxc_linter/src/rules/nextjs/no_duplicate_head.rs @@ -52,7 +52,7 @@ impl Rule for NoDuplicateHead { return; } - let scope_id = symbols.get_scope_id(symbol_id); + let Some(scope_id) = symbols.get_scope_id(symbol_id) else { return }; if scope_id != ctx.scopes().root_scope_id() { return; } diff --git a/crates/oxc_parser/src/js/class.rs b/crates/oxc_parser/src/js/class.rs index 77ab8288b3369..f606af61cf96a 100644 --- a/crates/oxc_parser/src/js/class.rs +++ b/crates/oxc_parser/src/js/class.rs @@ -99,11 +99,11 @@ impl<'a> ParserImpl<'a> { self.end_span(start_span), decorators, id, - super_class, - body, type_parameters, + super_class, super_type_parameters, implements, + body, modifiers.contains_abstract(), modifiers.contains_declare(), )) diff --git a/crates/oxc_semantic/src/binder.rs b/crates/oxc_semantic/src/binder.rs index ce14e8a08a682..fc548926c35bb 100644 --- a/crates/oxc_semantic/src/binder.rs +++ b/crates/oxc_semantic/src/binder.rs @@ -34,7 +34,12 @@ impl<'a> Binder for VariableDeclarator<'a> { if self.kind.is_lexical() { self.id.bound_names(&mut |ident| { - let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes); + let symbol_id = builder.declare_symbol_on_current_scope( + ident.span, + &ident.name, + includes, + excludes, + ); ident.symbol_id.set(Some(symbol_id)); }); return; @@ -80,12 +85,16 @@ impl<'a> Binder for Class<'a> { fn bind(&self, builder: &mut SemanticBuilder) { let Some(ident) = &self.id else { return }; if !self.declare { - let symbol_id = builder.declare_symbol( - ident.span, - &ident.name, - SymbolFlags::Class, - SymbolFlags::ClassExcludes, - ); + let symbol_id = if self.is_declaration() { + builder.declare_symbol_on_current_scope( + ident.span, + &ident.name, + SymbolFlags::Class, + SymbolFlags::ClassExcludes, + ) + } else { + builder.declare_symbol(ident.span, &ident.name, SymbolFlags::Class) + }; ident.symbol_id.set(Some(symbol_id)); } } @@ -143,12 +152,8 @@ impl<'a> Binder for Function<'a> { } else if self.r#type == FunctionType::FunctionExpression { // https://tc39.es/ecma262/#sec-runtime-semantics-instantiateordinaryfunctionexpression // 5. Perform ! funcEnv.CreateImmutableBinding(name, false). - let symbol_id = builder.declare_symbol( - ident.span, - &ident.name, - SymbolFlags::Function, - SymbolFlags::empty(), - ); + let symbol_id = + builder.declare_symbol(ident.span, &ident.name, SymbolFlags::Function); ident.symbol_id.set(Some(symbol_id)); } } @@ -196,7 +201,12 @@ impl<'a> Binder for BindingRestElement<'a> { let excludes = SymbolFlags::FunctionScopedVariable | SymbolFlags::FunctionScopedVariableExcludes; self.bound_names(&mut |ident| { - let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes); + let symbol_id = builder.declare_symbol_on_current_scope( + ident.span, + &ident.name, + includes, + excludes, + ); ident.symbol_id.set(Some(symbol_id)); }); } @@ -235,7 +245,12 @@ impl<'a> Binder for FormalParameter<'a> { }; self.bound_names(&mut |ident| { - let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes); + let symbol_id = builder.declare_symbol_on_current_scope( + ident.span, + &ident.name, + includes, + excludes, + ); ident.symbol_id.set(Some(symbol_id)); }); } @@ -254,7 +269,7 @@ impl<'a> Binder for CatchParameter<'a> { ident.symbol_id.set(Some(symbol_id)); } else { self.pattern.bound_names(&mut |ident| { - let symbol_id = builder.declare_symbol( + let symbol_id = builder.declare_symbol_on_current_scope( ident.span, &ident.name, SymbolFlags::BlockScopedVariable | SymbolFlags::CatchVariable, @@ -267,7 +282,7 @@ impl<'a> Binder for CatchParameter<'a> { } fn declare_symbol_for_import_specifier(ident: &BindingIdentifier, builder: &mut SemanticBuilder) { - let symbol_id = builder.declare_symbol( + let symbol_id = builder.declare_symbol_on_current_scope( ident.span, &ident.name, SymbolFlags::ImportBinding, @@ -302,7 +317,7 @@ impl<'a> Binder for TSImportEqualsDeclaration<'a> { impl<'a> Binder for TSTypeAliasDeclaration<'a> { fn bind(&self, builder: &mut SemanticBuilder) { - let symbol_id = builder.declare_symbol( + let symbol_id = builder.declare_symbol_on_current_scope( self.id.span, &self.id.name, SymbolFlags::TypeAlias, @@ -314,7 +329,7 @@ impl<'a> Binder for TSTypeAliasDeclaration<'a> { impl<'a> Binder for TSInterfaceDeclaration<'a> { fn bind(&self, builder: &mut SemanticBuilder) { - let symbol_id = builder.declare_symbol( + let symbol_id = builder.declare_symbol_on_current_scope( self.id.span, &self.id.name, SymbolFlags::Interface, @@ -333,7 +348,12 @@ impl<'a> Binder for TSEnumDeclaration<'a> { } else { SymbolFlags::RegularEnumExcludes }; - let symbol_id = builder.declare_symbol(self.id.span, &self.id.name, includes, excludes); + let symbol_id = builder.declare_symbol_on_current_scope( + self.id.span, + &self.id.name, + includes, + excludes, + ); self.id.symbol_id.set(Some(symbol_id)); } } @@ -350,7 +370,7 @@ impl<'a> Binder for TSEnumMember<'a> { TSEnumMemberName::StaticNumericLiteral(n) => Cow::Owned(n.value.to_string()), match_expression!(TSEnumMemberName) => panic!("TODO: implement"), }; - builder.declare_symbol( + builder.declare_symbol_on_current_scope( self.span, &name, SymbolFlags::EnumMember, @@ -364,7 +384,7 @@ impl<'a> Binder for TSModuleDeclaration<'a> { // At declaration time a module has no value declaration it is only when a value declaration // is made inside a the scope of a module that the symbol is modified let ambient = if self.declare { SymbolFlags::Ambient } else { SymbolFlags::None }; - builder.declare_symbol( + builder.declare_symbol_on_current_scope( self.span, self.id.name().as_str(), SymbolFlags::NameSpaceModule | ambient, @@ -375,7 +395,7 @@ impl<'a> Binder for TSModuleDeclaration<'a> { impl<'a> Binder for TSTypeParameter<'a> { fn bind(&self, builder: &mut SemanticBuilder) { - let symbol_id = builder.declare_symbol( + let symbol_id = builder.declare_symbol_on_current_scope( self.name.span, &self.name.name, SymbolFlags::TypeParameter, diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index 5fcb1ef40e0b3..652985896eb5c 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -275,6 +275,15 @@ impl<'a> SemanticBuilder<'a> { } } + /// Declares a `Symbol` for the node, adds it to symbol table + /// + /// includes: the `SymbolFlags` that node has in addition to its declaration type (eg: export, ambient, etc.) + pub fn declare_symbol(&mut self, span: Span, name: &str, includes: SymbolFlags) -> SymbolId { + let includes = includes | self.current_symbol_flags; + let name = CompactStr::new(name); + self.symbols.create_symbol(span, name, self.current_node_id, includes, None) + } + /// Declares a `Symbol` for the node, adds it to symbol table, and binds it to the scope. /// /// includes: the `SymbolFlags` that node has in addition to its declaration type (eg: export, ambient, etc.) @@ -296,14 +305,13 @@ impl<'a> SemanticBuilder<'a> { } let includes = includes | self.current_symbol_flags; - let name = CompactStr::new(name); - let symbol_id = self.symbols.create_symbol(span, name.clone(), includes, scope_id); - self.symbols.add_declaration(self.current_node_id); - self.scope.add_binding(scope_id, name, symbol_id); + let symbol_id = self.declare_symbol(span, name, includes); + self.symbols.set_scope_id(symbol_id, scope_id); + self.scope.add_binding(scope_id, CompactStr::new(name), symbol_id); symbol_id } - pub fn declare_symbol( + pub fn declare_symbol_on_current_scope( &mut self, span: Span, name: &str, @@ -354,11 +362,9 @@ impl<'a> SemanticBuilder<'a> { includes: SymbolFlags, ) -> SymbolId { let includes = includes | self.current_symbol_flags; - let name = CompactStr::new(name); - let symbol_id = - self.symbols.create_symbol(span, name.clone(), includes, self.current_scope_id); - self.symbols.add_declaration(self.current_node_id); - self.scope.get_bindings_mut(scope_id).insert(name, symbol_id); + let symbol_id = self.declare_symbol(span, name, includes); + self.symbols.set_scope_id(symbol_id, scope_id); + self.scope.get_bindings_mut(scope_id).insert(CompactStr::new(name), symbol_id); symbol_id } @@ -372,7 +378,22 @@ impl<'a> SemanticBuilder<'a> { } fn resolve_reference_ids(&mut self, name: CompactStr, reference_ids: Vec) { - if let Some(symbol_id) = self.scope.get_binding(self.current_scope_id, &name) { + if let Some(symbol_id) = + self.scope.get_binding(self.current_scope_id, &name).or_else(|| { + self.symbols.get_symbol_id_from_declaration(self.current_node_id).and_then( + |symbol_id| { + let flag = self.symbols.get_flag(symbol_id); + if (flag.is_class() || flag.is_function()) + && self.symbols.get_name(symbol_id) == name + { + Some(symbol_id) + } else { + None + } + }, + ) + }) + { for reference_id in &reference_ids { self.symbols.references[*reference_id].set_symbol_id(symbol_id); } @@ -1442,46 +1463,6 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> { self.leave_scope(); } - fn visit_class(&mut self, class: &Class<'a>) { - // Class level decorators are transpiled as functions outside of the class taking the class - // itself as argument. They should be visited before class is entered. E.g., they inherit - // strict mode from the enclosing scope rather than from class. - for decorator in &class.decorators { - self.visit_decorator(decorator); - } - let kind = AstKind::Class(self.alloc(class)); - - // FIXME(don): Should we enter a scope when visiting class declarations? - let is_class_expr = class.r#type == ClassType::ClassExpression; - if is_class_expr { - // Class expressions create a temporary scope with the class name as its only variable - // E.g., `let c = class A { foo() { console.log(A) } }` - self.enter_scope(ScopeFlags::empty(), &class.scope_id); - } - - self.enter_node(kind); - - if let Some(id) = &class.id { - self.visit_binding_identifier(id); - } - if let Some(parameters) = &class.type_parameters { - self.visit_ts_type_parameter_declaration(parameters); - } - - if let Some(super_class) = &class.super_class { - self.visit_class_heritage(super_class); - } - if let Some(super_parameters) = &class.super_type_parameters { - self.visit_ts_type_parameter_instantiation(super_parameters); - } - self.visit_class_body(&class.body); - - self.leave_node(kind); - if is_class_expr { - self.leave_scope(); - } - } - fn visit_arrow_function_expression(&mut self, expr: &ArrowFunctionExpression<'a>) { let kind = AstKind::ArrowFunctionExpression(self.alloc(expr)); self.enter_scope(ScopeFlags::Function | ScopeFlags::Arrow, &expr.scope_id); diff --git a/crates/oxc_semantic/src/lib.rs b/crates/oxc_semantic/src/lib.rs index 52376d4ff4974..1a856d968f4d0 100644 --- a/crates/oxc_semantic/src/lib.rs +++ b/crates/oxc_semantic/src/lib.rs @@ -121,7 +121,7 @@ impl<'a> Semantic<'a> { } /// Find which scope a symbol is declared in - pub fn symbol_scope(&self, symbol_id: SymbolId) -> ScopeId { + pub fn symbol_scope(&self, symbol_id: SymbolId) -> Option { self.symbols.get_scope_id(symbol_id) } @@ -198,7 +198,7 @@ mod tests { .iter_bindings() .find(|(_scope_id, _symbol_id, name)| name.as_str() == "Fn") .unwrap(); - assert_eq!(semantic.symbols.get_scope_id(top_level_a.1), top_level_a.0); + assert_eq!(semantic.symbols.get_scope_id(top_level_a.1), Some(top_level_a.0)); } #[test] diff --git a/crates/oxc_semantic/src/symbol.rs b/crates/oxc_semantic/src/symbol.rs index 80bc798c1a0fa..7fe1cbf16aa6a 100644 --- a/crates/oxc_semantic/src/symbol.rs +++ b/crates/oxc_semantic/src/symbol.rs @@ -7,6 +7,8 @@ pub use oxc_syntax::{ scope::ScopeId, symbol::{SymbolFlags, SymbolId}, }; +use rustc_hash::FxHashMap; + #[cfg(feature = "serialize")] use serde::Serialize; #[cfg(feature = "serialize")] @@ -32,9 +34,10 @@ pub struct SymbolTable { pub spans: IndexVec, pub names: IndexVec, pub flags: IndexVec, - pub scope_ids: IndexVec, + pub scope_ids: IndexVec>, /// Pointer to the AST Node where this symbol is declared pub declarations: IndexVec, + pub declaration_symbol: FxHashMap, pub resolved_references: IndexVec>, pub references: IndexVec, pub redeclare_variables: IndexVec>, @@ -63,6 +66,10 @@ impl SymbolTable { .find_map(|(symbol, inner_span)| if inner_span == span { Some(symbol) } else { None }) } + pub fn get_symbol_id_from_declaration(&self, declaration: AstNodeId) -> Option { + self.declaration_symbol.get(&declaration).copied() + } + pub fn get_symbol_id_from_name(&self, name: &str) -> Option { self.names.iter_enumerated().find_map(|(symbol, inner_name)| { if inner_name.as_str() == name { @@ -97,16 +104,20 @@ impl SymbolTable { self.flags[symbol_id] |= includes; } - pub fn get_scope_id(&self, symbol_id: SymbolId) -> ScopeId { + pub fn set_scope_id(&mut self, symbol_id: SymbolId, scope_id: ScopeId) { + self.scope_ids[symbol_id] = Some(scope_id); + } + + pub fn get_scope_id(&self, symbol_id: SymbolId) -> Option { self.scope_ids[symbol_id] } pub fn get_scope_id_from_span(&self, span: &Span) -> Option { - self.get_symbol_id_from_span(span).map(|symbol_id| self.get_scope_id(symbol_id)) + self.get_symbol_id_from_span(span).and_then(|symbol_id| self.get_scope_id(symbol_id)) } pub fn get_scope_id_from_name(&self, name: &str) -> Option { - self.get_symbol_id_from_name(name).map(|symbol_id| self.get_scope_id(symbol_id)) + self.get_symbol_id_from_name(name).and_then(|symbol_id| self.get_scope_id(symbol_id)) } pub fn get_declaration(&self, symbol_id: SymbolId) -> AstNodeId { @@ -117,19 +128,19 @@ impl SymbolTable { &mut self, span: Span, name: CompactStr, + declaration: AstNodeId, flag: SymbolFlags, - scope_id: ScopeId, + scope_id: Option, ) -> SymbolId { _ = self.spans.push(span); _ = self.names.push(name); + _ = self.declarations.push(declaration); _ = self.flags.push(flag); _ = self.scope_ids.push(scope_id); _ = self.resolved_references.push(vec![]); - self.redeclare_variables.push(vec![]) - } - - pub fn add_declaration(&mut self, node_id: AstNodeId) { - self.declarations.push(node_id); + let symbol_id = self.redeclare_variables.push(vec![]); + self.declaration_symbol.insert(declaration, symbol_id); + symbol_id } pub fn add_redeclare_variable(&mut self, symbol_id: SymbolId, span: Span) { diff --git a/crates/oxc_semantic/tests/integration/scopes.rs b/crates/oxc_semantic/tests/integration/scopes.rs index be50154e2b5d5..235d0b979ab17 100644 --- a/crates/oxc_semantic/tests/integration/scopes.rs +++ b/crates/oxc_semantic/tests/integration/scopes.rs @@ -76,7 +76,7 @@ fn test_function_level_strict() { .is_in_scope(ScopeFlags::StrictMode | ScopeFlags::Function) .expect(|(semantic, symbol_id)| -> Result<(), &'static str> { let scope_id = semantic.symbol_scope(symbol_id); - let Some(parent_scope_id) = semantic.scopes().get_parent_id(scope_id) else { + let Some(parent_scope_id) = scope_id.and_then(|scope_id| semantic.scopes().get_parent_id(scope_id)) else { return Err("Expected x's scope to have a parent") }; let parent_flags = semantic.scopes().get_flags(parent_scope_id); diff --git a/crates/oxc_semantic/tests/integration/symbols.rs b/crates/oxc_semantic/tests/integration/symbols.rs index 8b43297749a19..0c55cd10bf1c3 100644 --- a/crates/oxc_semantic/tests/integration/symbols.rs +++ b/crates/oxc_semantic/tests/integration/symbols.rs @@ -27,10 +27,6 @@ fn test_function_simple() { #[test] fn test_function_expressions() { - SemanticTester::js("const x = function y() {}") - .has_some_symbol("y") - .contains_flags(SymbolFlags::Function) - .test(); SemanticTester::js("const x = () => {}") .has_some_symbol("x") .contains_flags(SymbolFlags::BlockScopedVariable | SymbolFlags::ConstVariable) diff --git a/crates/oxc_semantic/tests/integration/util/symbol_tester.rs b/crates/oxc_semantic/tests/integration/util/symbol_tester.rs index ccc1929e9f78b..e4e65ab0059e5 100644 --- a/crates/oxc_semantic/tests/integration/util/symbol_tester.rs +++ b/crates/oxc_semantic/tests/integration/util/symbol_tester.rs @@ -198,8 +198,9 @@ impl<'a> SymbolTester<'a> { self.test_result = match self.test_result { Ok(symbol_id) => { let scope_id = self.semantic.symbol_scope(symbol_id); - let scope_flags = self.semantic.scopes().get_flags(scope_id); - if scope_flags.contains(expected_flags) { + let scope_flags = + scope_id.map(|scope_id| self.semantic.scopes().get_flags(scope_id)); + if scope_flags.is_some_and(|flags| flags.contains(expected_flags)) { Ok(symbol_id) } else { Err(OxcDiagnostic::error(format!( @@ -219,8 +220,9 @@ impl<'a> SymbolTester<'a> { self.test_result = match self.test_result { Ok(symbol_id) => { let scope_id = self.semantic.symbol_scope(symbol_id); - let scope_flags = self.semantic.scopes().get_flags(scope_id); - if scope_flags.contains(excluded_flags) { + let scope_flags = + scope_id.map(|scope_id| self.semantic.scopes().get_flags(scope_id)); + if scope_flags.is_some_and(|flag| flag.contains(excluded_flags)) { Err(OxcDiagnostic::error(format!( "Binding {target_name} is in a scope with excluded flags.\n\tExpected: not {excluded_flags:?}\n\tActual: {scope_flags:?}" ))) diff --git a/crates/oxc_transformer/src/typescript/enum.rs b/crates/oxc_transformer/src/typescript/enum.rs index ba1de0af45093..1438a903d7d74 100644 --- a/crates/oxc_transformer/src/typescript/enum.rs +++ b/crates/oxc_transformer/src/typescript/enum.rs @@ -2,6 +2,7 @@ use std::cell::Cell; use oxc_allocator::Vec; use oxc_ast::{ast::*, visit::walk_mut, VisitMut}; +use oxc_semantic::AstNodeId; use oxc_span::{Atom, Span, SPAN}; use oxc_syntax::{ number::{NumberBase, ToJsInt32, ToJsString}, @@ -77,8 +78,9 @@ impl<'a> TypeScriptEnum<'a> { let param_symbol_id = ctx.symbols_mut().create_symbol( decl.id.span, enum_name.to_compact_str(), + AstNodeId::dummy(), SymbolFlags::FunctionScopedVariable, - func_scope_id, + Some(func_scope_id), ); let ident = BindingIdentifier { span: decl.id.span, diff --git a/crates/oxc_traverse/src/ancestor.rs b/crates/oxc_traverse/src/ancestor.rs index 02f78af89c23e..e79b91ef385d1 100644 --- a/crates/oxc_traverse/src/ancestor.rs +++ b/crates/oxc_traverse/src/ancestor.rs @@ -158,11 +158,11 @@ pub(crate) enum AncestorType { YieldExpressionArgument = 126, ClassDecorators = 127, ClassId = 128, - ClassSuperClass = 129, - ClassBody = 130, - ClassTypeParameters = 131, - ClassSuperTypeParameters = 132, - ClassImplements = 133, + ClassTypeParameters = 129, + ClassSuperClass = 130, + ClassSuperTypeParameters = 131, + ClassImplements = 132, + ClassBody = 133, ClassBodyBody = 134, MethodDefinitionDecorators = 135, MethodDefinitionKey = 136, @@ -557,12 +557,12 @@ pub enum Ancestor<'a> { AncestorType::YieldExpressionArgument as u16, ClassDecorators(ClassWithoutDecorators<'a>) = AncestorType::ClassDecorators as u16, ClassId(ClassWithoutId<'a>) = AncestorType::ClassId as u16, - ClassSuperClass(ClassWithoutSuperClass<'a>) = AncestorType::ClassSuperClass as u16, - ClassBody(ClassWithoutBody<'a>) = AncestorType::ClassBody as u16, ClassTypeParameters(ClassWithoutTypeParameters<'a>) = AncestorType::ClassTypeParameters as u16, + ClassSuperClass(ClassWithoutSuperClass<'a>) = AncestorType::ClassSuperClass as u16, ClassSuperTypeParameters(ClassWithoutSuperTypeParameters<'a>) = AncestorType::ClassSuperTypeParameters as u16, ClassImplements(ClassWithoutImplements<'a>) = AncestorType::ClassImplements as u16, + ClassBody(ClassWithoutBody<'a>) = AncestorType::ClassBody as u16, ClassBodyBody(ClassBodyWithoutBody<'a>) = AncestorType::ClassBodyBody as u16, MethodDefinitionDecorators(MethodDefinitionWithoutDecorators<'a>) = AncestorType::MethodDefinitionDecorators as u16, @@ -1284,11 +1284,11 @@ impl<'a> Ancestor<'a> { self, Self::ClassDecorators(_) | Self::ClassId(_) - | Self::ClassSuperClass(_) - | Self::ClassBody(_) | Self::ClassTypeParameters(_) + | Self::ClassSuperClass(_) | Self::ClassSuperTypeParameters(_) | Self::ClassImplements(_) + | Self::ClassBody(_) ) } @@ -6135,12 +6135,12 @@ pub(crate) const OFFSET_CLASS_TYPE: usize = offset_of!(Class, r#type); pub(crate) const OFFSET_CLASS_SPAN: usize = offset_of!(Class, span); pub(crate) const OFFSET_CLASS_DECORATORS: usize = offset_of!(Class, decorators); pub(crate) const OFFSET_CLASS_ID: usize = offset_of!(Class, id); -pub(crate) const OFFSET_CLASS_SUPER_CLASS: usize = offset_of!(Class, super_class); -pub(crate) const OFFSET_CLASS_BODY: usize = offset_of!(Class, body); pub(crate) const OFFSET_CLASS_TYPE_PARAMETERS: usize = offset_of!(Class, type_parameters); +pub(crate) const OFFSET_CLASS_SUPER_CLASS: usize = offset_of!(Class, super_class); pub(crate) const OFFSET_CLASS_SUPER_TYPE_PARAMETERS: usize = offset_of!(Class, super_type_parameters); pub(crate) const OFFSET_CLASS_IMPLEMENTS: usize = offset_of!(Class, implements); +pub(crate) const OFFSET_CLASS_BODY: usize = offset_of!(Class, body); pub(crate) const OFFSET_CLASS_ABSTRACT: usize = offset_of!(Class, r#abstract); pub(crate) const OFFSET_CLASS_DECLARE: usize = offset_of!(Class, declare); pub(crate) const OFFSET_CLASS_SCOPE_ID: usize = offset_of!(Class, scope_id); @@ -6168,22 +6168,17 @@ impl<'a> ClassWithoutDecorators<'a> { } #[inline] - pub fn super_class(&self) -> &Option> { + pub fn type_parameters(&self) -> &Option>> { unsafe { - &*((self.0 as *const u8).add(OFFSET_CLASS_SUPER_CLASS) as *const Option>) + &*((self.0 as *const u8).add(OFFSET_CLASS_TYPE_PARAMETERS) + as *const Option>>) } } #[inline] - pub fn body(&self) -> &Box<'a, ClassBody<'a>> { - unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_BODY) as *const Box<'a, ClassBody<'a>>) } - } - - #[inline] - pub fn type_parameters(&self) -> &Option>> { + pub fn super_class(&self) -> &Option> { unsafe { - &*((self.0 as *const u8).add(OFFSET_CLASS_TYPE_PARAMETERS) - as *const Option>>) + &*((self.0 as *const u8).add(OFFSET_CLASS_SUPER_CLASS) as *const Option>) } } @@ -6203,6 +6198,11 @@ impl<'a> ClassWithoutDecorators<'a> { } } + #[inline] + pub fn body(&self) -> &Box<'a, ClassBody<'a>> { + unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_BODY) as *const Box<'a, ClassBody<'a>>) } + } + #[inline] pub fn r#abstract(&self) -> &bool { unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_ABSTRACT) as *const bool) } @@ -6244,22 +6244,17 @@ impl<'a> ClassWithoutId<'a> { } #[inline] - pub fn super_class(&self) -> &Option> { + pub fn type_parameters(&self) -> &Option>> { unsafe { - &*((self.0 as *const u8).add(OFFSET_CLASS_SUPER_CLASS) as *const Option>) + &*((self.0 as *const u8).add(OFFSET_CLASS_TYPE_PARAMETERS) + as *const Option>>) } } #[inline] - pub fn body(&self) -> &Box<'a, ClassBody<'a>> { - unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_BODY) as *const Box<'a, ClassBody<'a>>) } - } - - #[inline] - pub fn type_parameters(&self) -> &Option>> { + pub fn super_class(&self) -> &Option> { unsafe { - &*((self.0 as *const u8).add(OFFSET_CLASS_TYPE_PARAMETERS) - as *const Option>>) + &*((self.0 as *const u8).add(OFFSET_CLASS_SUPER_CLASS) as *const Option>) } } @@ -6279,6 +6274,11 @@ impl<'a> ClassWithoutId<'a> { } } + #[inline] + pub fn body(&self) -> &Box<'a, ClassBody<'a>> { + unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_BODY) as *const Box<'a, ClassBody<'a>>) } + } + #[inline] pub fn r#abstract(&self) -> &bool { unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_ABSTRACT) as *const bool) } @@ -6299,9 +6299,9 @@ impl<'a> ClassWithoutId<'a> { #[repr(transparent)] #[derive(Debug)] -pub struct ClassWithoutSuperClass<'a>(pub(crate) *const Class<'a>); +pub struct ClassWithoutTypeParameters<'a>(pub(crate) *const Class<'a>); -impl<'a> ClassWithoutSuperClass<'a> { +impl<'a> ClassWithoutTypeParameters<'a> { #[inline] pub fn r#type(&self) -> &ClassType { unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_TYPE) as *const ClassType) } @@ -6327,15 +6327,9 @@ impl<'a> ClassWithoutSuperClass<'a> { } #[inline] - pub fn body(&self) -> &Box<'a, ClassBody<'a>> { - unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_BODY) as *const Box<'a, ClassBody<'a>>) } - } - - #[inline] - pub fn type_parameters(&self) -> &Option>> { + pub fn super_class(&self) -> &Option> { unsafe { - &*((self.0 as *const u8).add(OFFSET_CLASS_TYPE_PARAMETERS) - as *const Option>>) + &*((self.0 as *const u8).add(OFFSET_CLASS_SUPER_CLASS) as *const Option>) } } @@ -6355,6 +6349,11 @@ impl<'a> ClassWithoutSuperClass<'a> { } } + #[inline] + pub fn body(&self) -> &Box<'a, ClassBody<'a>> { + unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_BODY) as *const Box<'a, ClassBody<'a>>) } + } + #[inline] pub fn r#abstract(&self) -> &bool { unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_ABSTRACT) as *const bool) } @@ -6375,9 +6374,9 @@ impl<'a> ClassWithoutSuperClass<'a> { #[repr(transparent)] #[derive(Debug)] -pub struct ClassWithoutBody<'a>(pub(crate) *const Class<'a>); +pub struct ClassWithoutSuperClass<'a>(pub(crate) *const Class<'a>); -impl<'a> ClassWithoutBody<'a> { +impl<'a> ClassWithoutSuperClass<'a> { #[inline] pub fn r#type(&self) -> &ClassType { unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_TYPE) as *const ClassType) } @@ -6402,13 +6401,6 @@ impl<'a> ClassWithoutBody<'a> { } } - #[inline] - pub fn super_class(&self) -> &Option> { - unsafe { - &*((self.0 as *const u8).add(OFFSET_CLASS_SUPER_CLASS) as *const Option>) - } - } - #[inline] pub fn type_parameters(&self) -> &Option>> { unsafe { @@ -6433,6 +6425,11 @@ impl<'a> ClassWithoutBody<'a> { } } + #[inline] + pub fn body(&self) -> &Box<'a, ClassBody<'a>> { + unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_BODY) as *const Box<'a, ClassBody<'a>>) } + } + #[inline] pub fn r#abstract(&self) -> &bool { unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_ABSTRACT) as *const bool) } @@ -6453,9 +6450,9 @@ impl<'a> ClassWithoutBody<'a> { #[repr(transparent)] #[derive(Debug)] -pub struct ClassWithoutTypeParameters<'a>(pub(crate) *const Class<'a>); +pub struct ClassWithoutSuperTypeParameters<'a>(pub(crate) *const Class<'a>); -impl<'a> ClassWithoutTypeParameters<'a> { +impl<'a> ClassWithoutSuperTypeParameters<'a> { #[inline] pub fn r#type(&self) -> &ClassType { unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_TYPE) as *const ClassType) } @@ -6481,22 +6478,17 @@ impl<'a> ClassWithoutTypeParameters<'a> { } #[inline] - pub fn super_class(&self) -> &Option> { + pub fn type_parameters(&self) -> &Option>> { unsafe { - &*((self.0 as *const u8).add(OFFSET_CLASS_SUPER_CLASS) as *const Option>) + &*((self.0 as *const u8).add(OFFSET_CLASS_TYPE_PARAMETERS) + as *const Option>>) } } #[inline] - pub fn body(&self) -> &Box<'a, ClassBody<'a>> { - unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_BODY) as *const Box<'a, ClassBody<'a>>) } - } - - #[inline] - pub fn super_type_parameters(&self) -> &Option>> { + pub fn super_class(&self) -> &Option> { unsafe { - &*((self.0 as *const u8).add(OFFSET_CLASS_SUPER_TYPE_PARAMETERS) - as *const Option>>) + &*((self.0 as *const u8).add(OFFSET_CLASS_SUPER_CLASS) as *const Option>) } } @@ -6508,6 +6500,11 @@ impl<'a> ClassWithoutTypeParameters<'a> { } } + #[inline] + pub fn body(&self) -> &Box<'a, ClassBody<'a>> { + unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_BODY) as *const Box<'a, ClassBody<'a>>) } + } + #[inline] pub fn r#abstract(&self) -> &bool { unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_ABSTRACT) as *const bool) } @@ -6528,9 +6525,9 @@ impl<'a> ClassWithoutTypeParameters<'a> { #[repr(transparent)] #[derive(Debug)] -pub struct ClassWithoutSuperTypeParameters<'a>(pub(crate) *const Class<'a>); +pub struct ClassWithoutImplements<'a>(pub(crate) *const Class<'a>); -impl<'a> ClassWithoutSuperTypeParameters<'a> { +impl<'a> ClassWithoutImplements<'a> { #[inline] pub fn r#type(&self) -> &ClassType { unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_TYPE) as *const ClassType) } @@ -6556,31 +6553,31 @@ impl<'a> ClassWithoutSuperTypeParameters<'a> { } #[inline] - pub fn super_class(&self) -> &Option> { + pub fn type_parameters(&self) -> &Option>> { unsafe { - &*((self.0 as *const u8).add(OFFSET_CLASS_SUPER_CLASS) as *const Option>) + &*((self.0 as *const u8).add(OFFSET_CLASS_TYPE_PARAMETERS) + as *const Option>>) } } #[inline] - pub fn body(&self) -> &Box<'a, ClassBody<'a>> { - unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_BODY) as *const Box<'a, ClassBody<'a>>) } + pub fn super_class(&self) -> &Option> { + unsafe { + &*((self.0 as *const u8).add(OFFSET_CLASS_SUPER_CLASS) as *const Option>) + } } #[inline] - pub fn type_parameters(&self) -> &Option>> { + pub fn super_type_parameters(&self) -> &Option>> { unsafe { - &*((self.0 as *const u8).add(OFFSET_CLASS_TYPE_PARAMETERS) - as *const Option>>) + &*((self.0 as *const u8).add(OFFSET_CLASS_SUPER_TYPE_PARAMETERS) + as *const Option>>) } } #[inline] - pub fn implements(&self) -> &Option>> { - unsafe { - &*((self.0 as *const u8).add(OFFSET_CLASS_IMPLEMENTS) - as *const Option>>) - } + pub fn body(&self) -> &Box<'a, ClassBody<'a>> { + unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_BODY) as *const Box<'a, ClassBody<'a>>) } } #[inline] @@ -6603,9 +6600,9 @@ impl<'a> ClassWithoutSuperTypeParameters<'a> { #[repr(transparent)] #[derive(Debug)] -pub struct ClassWithoutImplements<'a>(pub(crate) *const Class<'a>); +pub struct ClassWithoutBody<'a>(pub(crate) *const Class<'a>); -impl<'a> ClassWithoutImplements<'a> { +impl<'a> ClassWithoutBody<'a> { #[inline] pub fn r#type(&self) -> &ClassType { unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_TYPE) as *const ClassType) } @@ -6631,22 +6628,17 @@ impl<'a> ClassWithoutImplements<'a> { } #[inline] - pub fn super_class(&self) -> &Option> { + pub fn type_parameters(&self) -> &Option>> { unsafe { - &*((self.0 as *const u8).add(OFFSET_CLASS_SUPER_CLASS) as *const Option>) + &*((self.0 as *const u8).add(OFFSET_CLASS_TYPE_PARAMETERS) + as *const Option>>) } } #[inline] - pub fn body(&self) -> &Box<'a, ClassBody<'a>> { - unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_BODY) as *const Box<'a, ClassBody<'a>>) } - } - - #[inline] - pub fn type_parameters(&self) -> &Option>> { + pub fn super_class(&self) -> &Option> { unsafe { - &*((self.0 as *const u8).add(OFFSET_CLASS_TYPE_PARAMETERS) - as *const Option>>) + &*((self.0 as *const u8).add(OFFSET_CLASS_SUPER_CLASS) as *const Option>) } } @@ -6658,6 +6650,14 @@ impl<'a> ClassWithoutImplements<'a> { } } + #[inline] + pub fn implements(&self) -> &Option>> { + unsafe { + &*((self.0 as *const u8).add(OFFSET_CLASS_IMPLEMENTS) + as *const Option>>) + } + } + #[inline] pub fn r#abstract(&self) -> &bool { unsafe { &*((self.0 as *const u8).add(OFFSET_CLASS_ABSTRACT) as *const bool) } diff --git a/crates/oxc_traverse/src/context/scoping.rs b/crates/oxc_traverse/src/context/scoping.rs index b67a3adaa72e3..46da7b45d6b0d 100644 --- a/crates/oxc_traverse/src/context/scoping.rs +++ b/crates/oxc_traverse/src/context/scoping.rs @@ -245,7 +245,13 @@ impl TraverseScoping { let name = CompactStr::new(&self.find_uid_name(name)); // Add binding to scope - let symbol_id = self.symbols.create_symbol(SPAN, name.clone(), flags, scope_id); + let symbol_id = self.symbols.create_symbol( + SPAN, + name.clone(), + AstNodeId::dummy(), + flags, + Some(scope_id), + ); self.scopes.add_binding(scope_id, name, symbol_id); symbol_id } diff --git a/crates/oxc_traverse/src/walk.rs b/crates/oxc_traverse/src/walk.rs index 9361a1aa78dda..8f6f9174a0fc8 100644 --- a/crates/oxc_traverse/src/walk.rs +++ b/crates/oxc_traverse/src/walk.rs @@ -2459,6 +2459,12 @@ pub(crate) unsafe fn walk_class<'a, Tr: Traverse<'a>>( { walk_decorator(traverser, item as *mut _, ctx); } + if let Some(field) = + &mut *((node as *mut u8).add(ancestor::OFFSET_CLASS_ID) as *mut Option) + { + ctx.retag_stack(AncestorType::ClassId); + walk_binding_identifier(traverser, field as *mut _, ctx); + } let mut previous_scope_id = None; if let Some(scope_id) = (*((node as *mut u8).add(ancestor::OFFSET_CLASS_SCOPE_ID) as *mut Cell>)) @@ -2467,11 +2473,11 @@ pub(crate) unsafe fn walk_class<'a, Tr: Traverse<'a>>( previous_scope_id = Some(ctx.current_scope_id()); ctx.set_current_scope_id(scope_id); } - if let Some(field) = - &mut *((node as *mut u8).add(ancestor::OFFSET_CLASS_ID) as *mut Option) + if let Some(field) = &mut *((node as *mut u8).add(ancestor::OFFSET_CLASS_TYPE_PARAMETERS) + as *mut Option>) { - ctx.retag_stack(AncestorType::ClassId); - walk_binding_identifier(traverser, field as *mut _, ctx); + ctx.retag_stack(AncestorType::ClassTypeParameters); + walk_ts_type_parameter_declaration(traverser, (&mut **field) as *mut _, ctx); } if let Some(field) = &mut *((node as *mut u8).add(ancestor::OFFSET_CLASS_SUPER_CLASS) as *mut Option) @@ -2479,19 +2485,6 @@ pub(crate) unsafe fn walk_class<'a, Tr: Traverse<'a>>( ctx.retag_stack(AncestorType::ClassSuperClass); walk_expression(traverser, field as *mut _, ctx); } - ctx.retag_stack(AncestorType::ClassBody); - walk_class_body( - traverser, - (&mut **((node as *mut u8).add(ancestor::OFFSET_CLASS_BODY) as *mut Box)) - as *mut _, - ctx, - ); - if let Some(field) = &mut *((node as *mut u8).add(ancestor::OFFSET_CLASS_TYPE_PARAMETERS) - as *mut Option>) - { - ctx.retag_stack(AncestorType::ClassTypeParameters); - walk_ts_type_parameter_declaration(traverser, (&mut **field) as *mut _, ctx); - } if let Some(field) = &mut *((node as *mut u8).add(ancestor::OFFSET_CLASS_SUPER_TYPE_PARAMETERS) as *mut Option>) { @@ -2506,6 +2499,13 @@ pub(crate) unsafe fn walk_class<'a, Tr: Traverse<'a>>( walk_ts_class_implements(traverser, item as *mut _, ctx); } } + ctx.retag_stack(AncestorType::ClassBody); + walk_class_body( + traverser, + (&mut **((node as *mut u8).add(ancestor::OFFSET_CLASS_BODY) as *mut Box)) + as *mut _, + ctx, + ); ctx.pop_stack(); if let Some(previous_scope_id) = previous_scope_id { ctx.set_current_scope_id(previous_scope_id); diff --git a/tasks/coverage/parser_test262.snap b/tasks/coverage/parser_test262.snap index 214a54144fb7c..ad84a4c5b0c87 100644 --- a/tasks/coverage/parser_test262.snap +++ b/tasks/coverage/parser_test262.snap @@ -2,12 +2,60 @@ commit: a1587416 parser_test262 Summary: AST Parsed : 45859/45859 (100.00%) -Positive Passed: 45859/45859 (100.00%) +Positive Passed: 45853/45859 (99.99%) Negative Passed: 3925/3929 (99.90%) Expect Syntax Error: "language/import/import-assertions/json-invalid.js" Expect Syntax Error: "language/import/import-assertions/json-named-bindings.js" Expect Syntax Error: "language/import/import-attributes/json-invalid.js" Expect Syntax Error: "language/import/import-attributes/json-named-bindings.js" +Expect to Parse: "language/expressions/class/decorator/syntax/valid/decorator-call-expr-identifier-reference-yield.js" + + × The keyword 'yield' is reserved + ╭─[language/expressions/class/decorator/syntax/valid/decorator-call-expr-identifier-reference-yield.js:44:10] + 43 │ + 44 │ var C = @yield() class {}; + · ───── + ╰──── +Expect to Parse: "language/expressions/class/decorator/syntax/valid/decorator-member-expr-identifier-reference-yield.js" + + × The keyword 'yield' is reserved + ╭─[language/expressions/class/decorator/syntax/valid/decorator-member-expr-identifier-reference-yield.js:33:10] + 32 │ + 33 │ var C = @yield class {}; + · ───── + ╰──── +Expect to Parse: "language/expressions/class/decorator/syntax/valid/decorator-parenthesized-expr-identifier-reference-yield.js" + + × The keyword 'yield' is reserved + ╭─[language/expressions/class/decorator/syntax/valid/decorator-parenthesized-expr-identifier-reference-yield.js:51:11] + 50 │ + 51 │ var C = @(yield) class {}; + · ───── + ╰──── +Expect to Parse: "language/statements/class/decorator/syntax/valid/decorator-call-expr-identifier-reference-yield.js" + + × The keyword 'yield' is reserved + ╭─[language/statements/class/decorator/syntax/valid/decorator-call-expr-identifier-reference-yield.js:45:2] + 44 │ + 45 │ @yield() class C {} + · ───── + ╰──── +Expect to Parse: "language/statements/class/decorator/syntax/valid/decorator-member-expr-identifier-reference-yield.js" + + × The keyword 'yield' is reserved + ╭─[language/statements/class/decorator/syntax/valid/decorator-member-expr-identifier-reference-yield.js:34:2] + 33 │ + 34 │ @yield class C {} + · ───── + ╰──── +Expect to Parse: "language/statements/class/decorator/syntax/valid/decorator-parenthesized-expr-identifier-reference-yield.js" + + × The keyword 'yield' is reserved + ╭─[language/statements/class/decorator/syntax/valid/decorator-parenthesized-expr-identifier-reference-yield.js:52:3] + 51 │ + 52 │ @(yield) class C {} + · ───── + ╰──── × '0'-prefixed octal literals and octal escape sequences are deprecated ╭─[annexB/language/expressions/template-literal/legacy-octal-escape-sequence-strict.js:18:4] @@ -12629,6 +12677,14 @@ Expect Syntax Error: "language/import/import-attributes/json-named-bindings.js" × Cannot use `await` as an identifier in an async context ╭─[language/expressions/class/static-init-await-binding.js:21:12] 20 │ static { + 21 │ (class await {}); + · ───── + 22 │ } + ╰──── + + × Cannot use await in class static initialization block + ╭─[language/expressions/class/static-init-await-binding.js:21:12] + 20 │ static { 21 │ (class await {}); · ───── 22 │ } diff --git a/tasks/coverage/parser_typescript.snap b/tasks/coverage/parser_typescript.snap index 2f46024cd6191..959966aa80f4a 100644 --- a/tasks/coverage/parser_typescript.snap +++ b/tasks/coverage/parser_typescript.snap @@ -9735,6 +9735,30 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts" 22 │ ╰──── + × The keyword 'public' is reserved + ╭─[compiler/strictModeReservedWordInClassDeclaration.ts:23:20] + 22 │ + 23 │ class E implements public { } + · ────── + 24 │ + ╰──── + + × The keyword 'public' is reserved + ╭─[compiler/strictModeReservedWordInClassDeclaration.ts:25:20] + 24 │ + 25 │ class F implements public.private.B { } + · ────── + 26 │ class F1 implements public.private.implements { } + ╰──── + + × The keyword 'public' is reserved + ╭─[compiler/strictModeReservedWordInClassDeclaration.ts:26:21] + 25 │ class F implements public.private.B { } + 26 │ class F1 implements public.private.implements { } + · ────── + 27 │ class G extends package { } + ╰──── + × The keyword 'package' is reserved ╭─[compiler/strictModeReservedWordInClassDeclaration.ts:27:17] 26 │ class F1 implements public.private.implements { }