diff --git a/crates/oxc_ast/src/syntax_directed_operations/gather_node_parts.rs b/crates/oxc_ast/src/syntax_directed_operations/gather_node_parts.rs deleted file mode 100644 index cb924c0c0..000000000 --- a/crates/oxc_ast/src/syntax_directed_operations/gather_node_parts.rs +++ /dev/null @@ -1,85 +0,0 @@ -use oxc_span::Atom; - -use crate::ast::*; - -// TODO: -pub trait GatherNodeParts<'a> { - fn gather)>(&self, f: &mut F); -} - -impl<'a> GatherNodeParts<'a> for Expression<'a> { - fn gather)>(&self, f: &mut F) { - match self { - Self::Identifier(ident) => f(ident.name.clone()), - match_member_expression!(Self) => self.to_member_expression().gather(f), - Self::AssignmentExpression(expr) => expr.left.gather(f), - Self::UpdateExpression(expr) => expr.argument.gather(f), - Self::StringLiteral(lit) => lit.gather(f), - _ => f(Atom::from("ref")), - } - } -} - -impl<'a> GatherNodeParts<'a> for MemberExpression<'a> { - fn gather)>(&self, f: &mut F) { - match self { - MemberExpression::ComputedMemberExpression(expr) => { - expr.object.gather(f); - expr.expression.gather(f); - } - MemberExpression::StaticMemberExpression(expr) => { - expr.object.gather(f); - expr.property.gather(f); - } - MemberExpression::PrivateFieldExpression(expr) => { - expr.object.gather(f); - expr.field.gather(f); - } - } - } -} - -impl<'a> GatherNodeParts<'a> for AssignmentTarget<'a> { - fn gather)>(&self, f: &mut F) { - match self { - match_simple_assignment_target!(Self) => { - self.to_simple_assignment_target().gather(f); - } - match_assignment_target_pattern!(Self) => {} - } - } -} - -impl<'a> GatherNodeParts<'a> for SimpleAssignmentTarget<'a> { - fn gather)>(&self, f: &mut F) { - match self { - Self::AssignmentTargetIdentifier(ident) => ident.gather(f), - match_member_expression!(Self) => self.to_member_expression().gather(f), - _ => {} - } - } -} - -impl<'a> GatherNodeParts<'a> for IdentifierReference<'a> { - fn gather)>(&self, f: &mut F) { - f(self.name.clone()); - } -} - -impl<'a> GatherNodeParts<'a> for IdentifierName<'a> { - fn gather)>(&self, f: &mut F) { - f(self.name.clone()); - } -} - -impl<'a> GatherNodeParts<'a> for PrivateIdentifier<'a> { - fn gather)>(&self, f: &mut F) { - f(self.name.clone()); - } -} - -impl<'a> GatherNodeParts<'a> for StringLiteral<'a> { - fn gather)>(&self, f: &mut F) { - f(self.value.clone()); - } -} diff --git a/crates/oxc_ast/src/syntax_directed_operations/mod.rs b/crates/oxc_ast/src/syntax_directed_operations/mod.rs index a70cce9f0..f44cc8bd4 100644 --- a/crates/oxc_ast/src/syntax_directed_operations/mod.rs +++ b/crates/oxc_ast/src/syntax_directed_operations/mod.rs @@ -1,13 +1,11 @@ //! [ECMA262 Syntax-Directed Operations](https://tc39.es/ecma262/#sec-syntax-directed-operations) mod bound_names; -mod gather_node_parts; mod is_simple_parameter_list; mod private_bound_identifiers; mod prop_name; pub use self::{ - bound_names::BoundNames, gather_node_parts::GatherNodeParts, - is_simple_parameter_list::IsSimpleParameterList, + bound_names::BoundNames, is_simple_parameter_list::IsSimpleParameterList, private_bound_identifiers::PrivateBoundIdentifiers, prop_name::PropName, }; diff --git a/crates/oxc_traverse/src/context/ast_operations/gather_node_parts.rs b/crates/oxc_traverse/src/context/ast_operations/gather_node_parts.rs new file mode 100644 index 000000000..64134c7b9 --- /dev/null +++ b/crates/oxc_traverse/src/context/ast_operations/gather_node_parts.rs @@ -0,0 +1,497 @@ +//! Gather node parts trait. +//! +//! Ported from: +//! +//! This trait is used to gather all the parts of a node that are identifiers. + +#[allow(clippy::wildcard_imports)] +use oxc_ast::ast::*; +use oxc_ast::syntax_directed_operations::BoundNames; + +pub trait GatherNodeParts<'a> { + fn gather(&self, f: &mut F); +} + +// -------------------- ModuleDeclaration -------------------- +impl<'a> GatherNodeParts<'a> for ImportDeclaration<'a> { + fn gather(&self, f: &mut F) { + self.source.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for ExportAllDeclaration<'a> { + fn gather(&self, f: &mut F) { + self.source.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for ExportNamedDeclaration<'a> { + fn gather(&self, f: &mut F) { + if let Some(source) = &self.source { + source.gather(f); + } else if let Some(declaration) = &self.declaration { + declaration.gather(f); + } else { + for specifier in &self.specifiers { + specifier.gather(f); + } + } + } +} + +impl<'a> GatherNodeParts<'a> for ExportDefaultDeclaration<'a> { + fn gather(&self, f: &mut F) { + self.declaration.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for ExportDefaultDeclarationKind<'a> { + fn gather(&self, f: &mut F) { + match self { + ExportDefaultDeclarationKind::FunctionDeclaration(decl) => decl.gather(f), + ExportDefaultDeclarationKind::ClassDeclaration(decl) => decl.gather(f), + ExportDefaultDeclarationKind::TSInterfaceDeclaration(_) => {} + match_expression!(ExportDefaultDeclarationKind) => self.to_expression().gather(f), + } + } +} + +impl<'a> GatherNodeParts<'a> for ExportSpecifier<'a> { + fn gather(&self, f: &mut F) { + match &self.local { + ModuleExportName::IdentifierName(ident) => ident.gather(f), + ModuleExportName::IdentifierReference(ident) => ident.gather(f), + ModuleExportName::StringLiteral(lit) => lit.gather(f), + } + } +} + +impl<'a> GatherNodeParts<'a> for ImportSpecifier<'a> { + fn gather(&self, f: &mut F) { + self.local.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for ImportDefaultSpecifier<'a> { + fn gather(&self, f: &mut F) { + self.local.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for ImportNamespaceSpecifier<'a> { + fn gather(&self, f: &mut F) { + self.local.gather(f); + } +} + +// -------------------- Declaration -------------------- + +impl<'a> GatherNodeParts<'a> for Declaration<'a> { + fn gather(&self, f: &mut F) { + match self { + Self::FunctionDeclaration(decl) => decl.gather(f), + Self::ClassDeclaration(decl) => decl.gather(f), + _ => (), + } + } +} + +// -------------------- Function -------------------- + +impl<'a> GatherNodeParts<'a> for Function<'a> { + fn gather(&self, f: &mut F) { + if let Some(id) = &self.id { + id.gather(f); + } + } +} + +impl<'a> GatherNodeParts<'a> for BindingRestElement<'a> { + fn gather(&self, f: &mut F) { + self.argument.gather(f); + } +} + +// -------------------- BindingPattern -------------------- + +impl<'a> GatherNodeParts<'a> for VariableDeclarator<'a> { + fn gather(&self, f: &mut F) { + self.id.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for BindingPattern<'a> { + fn gather(&self, f: &mut F) { + self.bound_names(&mut |id| f(id.name.as_str())); + } +} + +impl<'a> GatherNodeParts<'a> for ObjectPattern<'a> { + fn gather(&self, f: &mut F) { + self.bound_names(&mut |id| f(id.name.as_str())); + } +} + +impl<'a> GatherNodeParts<'a> for ArrayPattern<'a> { + fn gather(&self, f: &mut F) { + self.bound_names(&mut |id| f(id.name.as_str())); + } +} + +impl<'a> GatherNodeParts<'a> for AssignmentPattern<'a> { + fn gather(&self, f: &mut F) { + self.bound_names(&mut |id| f(id.name.as_str())); + } +} + +// -------------------- Expression -------------------- + +impl<'a> GatherNodeParts<'a> for Expression<'a> { + fn gather(&self, f: &mut F) { + match self { + match_member_expression!(Self) => self.to_member_expression().gather(f), + Self::Identifier(ident) => ident.gather(f), + Self::CallExpression(expr) => expr.gather(f), + Self::NewExpression(expr) => expr.gather(f), + Self::ObjectExpression(expr) => expr.gather(f), + Self::ThisExpression(expr) => expr.gather(f), + Self::Super(expr) => expr.gather(f), + Self::ImportExpression(expr) => expr.gather(f), + Self::YieldExpression(expr) => expr.gather(f), + Self::AwaitExpression(expr) => expr.gather(f), + Self::AssignmentExpression(expr) => expr.gather(f), + Self::FunctionExpression(expr) => expr.gather(f), + Self::ClassExpression(expr) => expr.gather(f), + Self::ParenthesizedExpression(expr) => expr.gather(f), + Self::UnaryExpression(expr) => expr.gather(f), + Self::UpdateExpression(expr) => expr.gather(f), + Self::MetaProperty(expr) => expr.gather(f), + Self::JSXElement(expr) => expr.gather(f), + Self::JSXFragment(expr) => expr.gather(f), + Self::StringLiteral(expr) => expr.gather(f), + Self::NumericLiteral(expr) => expr.gather(f), + Self::BooleanLiteral(expr) => expr.gather(f), + Self::BigIntLiteral(expr) => expr.gather(f), + _ => (), + } + } +} + +impl<'a> GatherNodeParts<'a> for MemberExpression<'a> { + fn gather(&self, f: &mut F) { + match self { + MemberExpression::ComputedMemberExpression(expr) => { + expr.object.gather(f); + expr.expression.gather(f); + } + MemberExpression::StaticMemberExpression(expr) => { + expr.object.gather(f); + expr.property.gather(f); + } + MemberExpression::PrivateFieldExpression(expr) => { + expr.object.gather(f); + expr.field.gather(f); + } + } + } +} + +impl<'a> GatherNodeParts<'a> for CallExpression<'a> { + fn gather(&self, f: &mut F) { + self.callee.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for NewExpression<'a> { + fn gather(&self, f: &mut F) { + self.callee.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for ObjectExpression<'a> { + fn gather(&self, f: &mut F) { + for prop in &self.properties { + prop.gather(f); + } + } +} + +impl<'a> GatherNodeParts<'a> for ThisExpression { + fn gather(&self, f: &mut F) { + f("this"); + } +} + +impl<'a> GatherNodeParts<'a> for Super { + fn gather(&self, f: &mut F) { + f("super"); + } +} + +impl<'a> GatherNodeParts<'a> for ImportExpression<'a> { + fn gather(&self, f: &mut F) { + f("import"); + } +} + +impl<'a> GatherNodeParts<'a> for YieldExpression<'a> { + fn gather(&self, f: &mut F) { + f("yield"); + if let Some(argument) = &self.argument { + argument.gather(f); + } + } +} + +impl<'a> GatherNodeParts<'a> for AwaitExpression<'a> { + fn gather(&self, f: &mut F) { + f("await"); + self.argument.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for AssignmentExpression<'a> { + fn gather(&self, f: &mut F) { + self.left.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for ParenthesizedExpression<'a> { + fn gather(&self, f: &mut F) { + self.expression.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for UnaryExpression<'a> { + fn gather(&self, f: &mut F) { + self.argument.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for UpdateExpression<'a> { + fn gather(&self, f: &mut F) { + self.argument.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for MetaProperty<'a> { + fn gather(&self, f: &mut F) { + self.meta.gather(f); + self.property.gather(f); + } +} + +// -------------------- AssignmentTarget -------------------- +impl<'a> GatherNodeParts<'a> for AssignmentTarget<'a> { + fn gather(&self, f: &mut F) { + match self { + match_simple_assignment_target!(Self) => { + self.to_simple_assignment_target().gather(f); + } + match_assignment_target_pattern!(Self) => {} + } + } +} + +impl<'a> GatherNodeParts<'a> for SimpleAssignmentTarget<'a> { + fn gather(&self, f: &mut F) { + match self { + Self::AssignmentTargetIdentifier(ident) => ident.gather(f), + match_member_expression!(Self) => self.to_member_expression().gather(f), + _ => {} + } + } +} + +// -------------------- Classes -------------------- + +impl<'a> GatherNodeParts<'a> for Class<'a> { + fn gather(&self, f: &mut F) { + if let Some(id) = &self.id { + id.gather(f); + } + } +} + +impl<'a> GatherNodeParts<'a> for ClassElement<'a> { + fn gather(&self, f: &mut F) { + match self { + ClassElement::PropertyDefinition(def) => def.gather(f), + ClassElement::MethodDefinition(def) => def.gather(f), + ClassElement::AccessorProperty(def) => def.gather(f), + _ => (), + } + } +} + +impl<'a> GatherNodeParts<'a> for PropertyDefinition<'a> { + fn gather(&self, f: &mut F) { + self.key.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for MethodDefinition<'a> { + fn gather(&self, f: &mut F) { + self.key.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for AccessorProperty<'a> { + fn gather(&self, f: &mut F) { + self.key.gather(f); + } +} + +// -------------------- Objects -------------------- + +impl<'a> GatherNodeParts<'a> for ObjectPropertyKind<'a> { + fn gather(&self, f: &mut F) { + match self { + ObjectPropertyKind::ObjectProperty(prop) => prop.gather(f), + ObjectPropertyKind::SpreadProperty(prop) => prop.gather(f), + } + } +} + +impl<'a> GatherNodeParts<'a> for ObjectProperty<'a> { + fn gather(&self, f: &mut F) { + self.key.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for PropertyKey<'a> { + fn gather(&self, f: &mut F) { + match self { + PropertyKey::StaticIdentifier(ident) => ident.gather(f), + PropertyKey::PrivateIdentifier(ident) => ident.gather(f), + match_expression!(Self) => self.to_expression().gather(f), + } + } +} + +impl<'a> GatherNodeParts<'a> for SpreadElement<'a> { + fn gather(&self, f: &mut F) { + self.argument.gather(f); + } +} + +// -------------------- Identifiers -------------------- + +impl<'a> GatherNodeParts<'a> for BindingIdentifier<'a> { + fn gather(&self, f: &mut F) { + f(self.name.as_str()); + } +} + +impl<'a> GatherNodeParts<'a> for IdentifierReference<'a> { + fn gather(&self, f: &mut F) { + f(self.name.as_str()); + } +} + +impl<'a> GatherNodeParts<'a> for IdentifierName<'a> { + fn gather(&self, f: &mut F) { + f(self.name.as_str()); + } +} + +impl<'a> GatherNodeParts<'a> for PrivateIdentifier<'a> { + fn gather(&self, f: &mut F) { + f(self.name.as_str()); + } +} + +// -------------------- Literals -------------------- + +impl<'a> GatherNodeParts<'a> for StringLiteral<'a> { + fn gather(&self, f: &mut F) { + f(self.value.as_str()); + } +} + +impl<'a> GatherNodeParts<'a> for NumericLiteral<'a> { + fn gather(&self, f: &mut F) { + f(self.raw); + } +} + +impl<'a> GatherNodeParts<'a> for BooleanLiteral { + fn gather(&self, f: &mut F) { + if self.value { + f("true"); + } else { + f("false"); + } + } +} + +impl<'a> GatherNodeParts<'a> for BigIntLiteral<'a> { + fn gather(&self, f: &mut F) { + f(self.raw.as_str()); + } +} + +// -------------------- JSX -------------------- + +impl<'a> GatherNodeParts<'a> for JSXElement<'a> { + fn gather(&self, f: &mut F) { + self.opening_element.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for JSXFragment<'a> { + fn gather(&self, f: &mut F) { + self.opening_fragment.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for JSXOpeningElement<'a> { + fn gather(&self, f: &mut F) { + self.name.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for JSXOpeningFragment { + fn gather(&self, f: &mut F) { + f("Fragment"); + } +} + +impl<'a> GatherNodeParts<'a> for JSXElementName<'a> { + fn gather(&self, f: &mut F) { + match self { + JSXElementName::Identifier(ident) => ident.gather(f), + JSXElementName::NamespacedName(ns) => ns.gather(f), + JSXElementName::MemberExpression(expr) => expr.gather(f), + } + } +} + +impl<'a> GatherNodeParts<'a> for JSXNamespacedName<'a> { + fn gather(&self, f: &mut F) { + self.namespace.gather(f); + self.property.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for JSXMemberExpression<'a> { + fn gather(&self, f: &mut F) { + self.object.gather(f); + self.property.gather(f); + } +} + +impl<'a> GatherNodeParts<'a> for JSXMemberExpressionObject<'a> { + fn gather(&self, f: &mut F) { + match self { + JSXMemberExpressionObject::Identifier(ident) => ident.gather(f), + JSXMemberExpressionObject::MemberExpression(expr) => expr.gather(f), + } + } +} + +impl<'a> GatherNodeParts<'a> for JSXIdentifier<'a> { + fn gather(&self, f: &mut F) { + f(self.name.as_str()); + } +} diff --git a/crates/oxc_traverse/src/context/ast_operations/mod.rs b/crates/oxc_traverse/src/context/ast_operations/mod.rs new file mode 100644 index 000000000..07efc6cff --- /dev/null +++ b/crates/oxc_traverse/src/context/ast_operations/mod.rs @@ -0,0 +1,2 @@ +pub mod gather_node_parts; +pub use self::gather_node_parts::GatherNodeParts; diff --git a/crates/oxc_traverse/src/context/mod.rs b/crates/oxc_traverse/src/context/mod.rs index e629a442f..d7e748a84 100644 --- a/crates/oxc_traverse/src/context/mod.rs +++ b/crates/oxc_traverse/src/context/mod.rs @@ -12,8 +12,8 @@ use oxc_syntax::{ }; use crate::ancestor::{Ancestor, AncestorType}; - mod ancestry; +mod ast_operations; pub use ancestry::TraverseAncestry; mod scoping; pub use scoping::TraverseScoping; diff --git a/crates/oxc_traverse/src/context/scoping.rs b/crates/oxc_traverse/src/context/scoping.rs index b4ecbfe2a..184f7e18b 100644 --- a/crates/oxc_traverse/src/context/scoping.rs +++ b/crates/oxc_traverse/src/context/scoping.rs @@ -14,6 +14,8 @@ use oxc_syntax::{ symbol::{SymbolFlags, SymbolId}, }; +use super::ast_operations::GatherNodeParts; + /// Traverse scope context. /// /// Contains the scope tree and symbols table, and provides methods to access them. @@ -225,6 +227,44 @@ impl TraverseScoping { self.generate_uid(name, self.scopes.root_scope_id(), flags) } + /// Generate UID based on node. + /// + /// Recursively gathers the identifying names of a node, and joins them with `$`. + /// + /// Based on Babel's `scope.generateUidBasedOnNode` logic. + /// + pub fn generate_uid_based_on_node<'a, T>( + &mut self, + node: &T, + scope_id: ScopeId, + flags: SymbolFlags, + ) -> SymbolId + where + T: GatherNodeParts<'a>, + { + let mut parts = String::new(); + node.gather(&mut |part| { + if !parts.is_empty() { + parts.push('$'); + } + parts.push_str(part); + }); + let name = if parts.is_empty() { "ref" } else { parts.trim_start_matches('_') }; + self.generate_uid(name, scope_id, flags) + } + + /// Generate UID in current scope based on node. + pub fn generate_uid_in_current_scope_based_on_node<'a, T>( + &mut self, + node: &T, + flags: SymbolFlags, + ) -> SymbolId + where + T: GatherNodeParts<'a>, + { + self.generate_uid_based_on_node(node, self.current_scope_id, flags) + } + /// Create a reference bound to a `SymbolId` pub fn create_bound_reference( &mut self,