feat(traverse): support generate_uid_based_on_node method in TraverseCtx (#4940)

close: #4906

Port completely  `gatherNodeParts` from `Babel`. But we don't have test cases to test it, we can find the bugs(if any) after we use this API in transformer plugins
This commit is contained in:
Dunqing 2024-08-17 02:50:23 +00:00
parent 3f28c77eb6
commit fd34640d65
6 changed files with 541 additions and 89 deletions

View file

@ -1,85 +0,0 @@
use oxc_span::Atom;
use crate::ast::*;
// TODO: <https://github.com/babel/babel/blob/419644f27c5c59deb19e71aaabd417a3bc5483ca/packages/babel-traverse/src/scope/index.ts#L61>
pub trait GatherNodeParts<'a> {
fn gather<F: FnMut(Atom<'a>)>(&self, f: &mut F);
}
impl<'a> GatherNodeParts<'a> for Expression<'a> {
fn gather<F: FnMut(Atom<'a>)>(&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<F: FnMut(Atom<'a>)>(&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<F: FnMut(Atom<'a>)>(&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<F: FnMut(Atom<'a>)>(&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<F: FnMut(Atom<'a>)>(&self, f: &mut F) {
f(self.name.clone());
}
}
impl<'a> GatherNodeParts<'a> for IdentifierName<'a> {
fn gather<F: FnMut(Atom<'a>)>(&self, f: &mut F) {
f(self.name.clone());
}
}
impl<'a> GatherNodeParts<'a> for PrivateIdentifier<'a> {
fn gather<F: FnMut(Atom<'a>)>(&self, f: &mut F) {
f(self.name.clone());
}
}
impl<'a> GatherNodeParts<'a> for StringLiteral<'a> {
fn gather<F: FnMut(Atom<'a>)>(&self, f: &mut F) {
f(self.value.clone());
}
}

View file

@ -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,
};

View file

@ -0,0 +1,497 @@
//! Gather node parts trait.
//!
//! Ported from: <https://github.com/babel/babel/blob/419644f27c5c59deb19e71aaabd417a3bc5483ca/packages/babel-traverse/src/scope/index.ts>
//!
//! 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<F: FnMut(&str)>(&self, f: &mut F);
}
// -------------------- ModuleDeclaration --------------------
impl<'a> GatherNodeParts<'a> for ImportDeclaration<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.source.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for ExportAllDeclaration<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.source.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for ExportNamedDeclaration<'a> {
fn gather<F: FnMut(&str)>(&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<F: FnMut(&str)>(&self, f: &mut F) {
self.declaration.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for ExportDefaultDeclarationKind<'a> {
fn gather<F: FnMut(&str)>(&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<F: FnMut(&str)>(&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<F: FnMut(&str)>(&self, f: &mut F) {
self.local.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for ImportDefaultSpecifier<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.local.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for ImportNamespaceSpecifier<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.local.gather(f);
}
}
// -------------------- Declaration --------------------
impl<'a> GatherNodeParts<'a> for Declaration<'a> {
fn gather<F: FnMut(&str)>(&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<F: FnMut(&str)>(&self, f: &mut F) {
if let Some(id) = &self.id {
id.gather(f);
}
}
}
impl<'a> GatherNodeParts<'a> for BindingRestElement<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.argument.gather(f);
}
}
// -------------------- BindingPattern --------------------
impl<'a> GatherNodeParts<'a> for VariableDeclarator<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.id.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for BindingPattern<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.bound_names(&mut |id| f(id.name.as_str()));
}
}
impl<'a> GatherNodeParts<'a> for ObjectPattern<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.bound_names(&mut |id| f(id.name.as_str()));
}
}
impl<'a> GatherNodeParts<'a> for ArrayPattern<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.bound_names(&mut |id| f(id.name.as_str()));
}
}
impl<'a> GatherNodeParts<'a> for AssignmentPattern<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.bound_names(&mut |id| f(id.name.as_str()));
}
}
// -------------------- Expression --------------------
impl<'a> GatherNodeParts<'a> for Expression<'a> {
fn gather<F: FnMut(&str)>(&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<F: FnMut(&str)>(&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<F: FnMut(&str)>(&self, f: &mut F) {
self.callee.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for NewExpression<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.callee.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for ObjectExpression<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
for prop in &self.properties {
prop.gather(f);
}
}
}
impl<'a> GatherNodeParts<'a> for ThisExpression {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
f("this");
}
}
impl<'a> GatherNodeParts<'a> for Super {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
f("super");
}
}
impl<'a> GatherNodeParts<'a> for ImportExpression<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
f("import");
}
}
impl<'a> GatherNodeParts<'a> for YieldExpression<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
f("yield");
if let Some(argument) = &self.argument {
argument.gather(f);
}
}
}
impl<'a> GatherNodeParts<'a> for AwaitExpression<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
f("await");
self.argument.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for AssignmentExpression<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.left.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for ParenthesizedExpression<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.expression.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for UnaryExpression<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.argument.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for UpdateExpression<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.argument.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for MetaProperty<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.meta.gather(f);
self.property.gather(f);
}
}
// -------------------- AssignmentTarget --------------------
impl<'a> GatherNodeParts<'a> for AssignmentTarget<'a> {
fn gather<F: FnMut(&str)>(&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<F: FnMut(&str)>(&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<F: FnMut(&str)>(&self, f: &mut F) {
if let Some(id) = &self.id {
id.gather(f);
}
}
}
impl<'a> GatherNodeParts<'a> for ClassElement<'a> {
fn gather<F: FnMut(&str)>(&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<F: FnMut(&str)>(&self, f: &mut F) {
self.key.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for MethodDefinition<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.key.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for AccessorProperty<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.key.gather(f);
}
}
// -------------------- Objects --------------------
impl<'a> GatherNodeParts<'a> for ObjectPropertyKind<'a> {
fn gather<F: FnMut(&str)>(&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<F: FnMut(&str)>(&self, f: &mut F) {
self.key.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for PropertyKey<'a> {
fn gather<F: FnMut(&str)>(&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<F: FnMut(&str)>(&self, f: &mut F) {
self.argument.gather(f);
}
}
// -------------------- Identifiers --------------------
impl<'a> GatherNodeParts<'a> for BindingIdentifier<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
f(self.name.as_str());
}
}
impl<'a> GatherNodeParts<'a> for IdentifierReference<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
f(self.name.as_str());
}
}
impl<'a> GatherNodeParts<'a> for IdentifierName<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
f(self.name.as_str());
}
}
impl<'a> GatherNodeParts<'a> for PrivateIdentifier<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
f(self.name.as_str());
}
}
// -------------------- Literals --------------------
impl<'a> GatherNodeParts<'a> for StringLiteral<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
f(self.value.as_str());
}
}
impl<'a> GatherNodeParts<'a> for NumericLiteral<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
f(self.raw);
}
}
impl<'a> GatherNodeParts<'a> for BooleanLiteral {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
if self.value {
f("true");
} else {
f("false");
}
}
}
impl<'a> GatherNodeParts<'a> for BigIntLiteral<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
f(self.raw.as_str());
}
}
// -------------------- JSX --------------------
impl<'a> GatherNodeParts<'a> for JSXElement<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.opening_element.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for JSXFragment<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.opening_fragment.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for JSXOpeningElement<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.name.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for JSXOpeningFragment {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
f("Fragment");
}
}
impl<'a> GatherNodeParts<'a> for JSXElementName<'a> {
fn gather<F: FnMut(&str)>(&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<F: FnMut(&str)>(&self, f: &mut F) {
self.namespace.gather(f);
self.property.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for JSXMemberExpression<'a> {
fn gather<F: FnMut(&str)>(&self, f: &mut F) {
self.object.gather(f);
self.property.gather(f);
}
}
impl<'a> GatherNodeParts<'a> for JSXMemberExpressionObject<'a> {
fn gather<F: FnMut(&str)>(&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<F: FnMut(&str)>(&self, f: &mut F) {
f(self.name.as_str());
}
}

View file

@ -0,0 +1,2 @@
pub mod gather_node_parts;
pub use self::gather_node_parts::GatherNodeParts;

View file

@ -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;

View file

@ -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.
/// <https://github.com/babel/babel/blob/419644f27c5c59deb19e71aaabd417a3bc5483ca/packages/babel-traverse/src/scope/index.ts#L543>
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,