Skip to content

Commit

Permalink
feat(ast): add JSXIdentifierReference for JSXElementName::IdentifierR…
Browse files Browse the repository at this point in the history
…eference and JSXMemberExpressionObject::IdentifierReference
  • Loading branch information
Dunqing committed Aug 26, 2024
1 parent 0d3661a commit 6a849f0
Show file tree
Hide file tree
Showing 42 changed files with 679 additions and 763 deletions.
39 changes: 35 additions & 4 deletions crates/oxc_ast/src/ast/jsx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
// Silence erroneous warnings from Rust Analyser for `#[derive(Tsify)]`
#![allow(non_snake_case)]

use std::cell::Cell;

use oxc_allocator::{Box, CloneIn, Vec};
use oxc_ast_macros::ast;
use oxc_span::{Atom, GetSpan, GetSpanMut, Span};
use oxc_syntax::reference::ReferenceId;
#[cfg(feature = "serialize")]
use serde::Serialize;
#[cfg(feature = "serialize")]
Expand Down Expand Up @@ -165,12 +168,14 @@ pub struct JSXClosingFragment {
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[serde(untagged)]
pub enum JSXElementName<'a> {
/// `<div />`
Identifier(Box<'a, JSXIdentifier<'a>>) = 1,
/// `<Apple />`
Identifier(Box<'a, JSXIdentifier<'a>>) = 0,
IdentifierReference(Box<'a, JSXIdentifierReference<'a>>) = 0,
/// `<Apple:Orange />`
NamespacedName(Box<'a, JSXNamespacedName<'a>>) = 1,
NamespacedName(Box<'a, JSXNamespacedName<'a>>) = 2,
/// `<Apple.Orange />`
MemberExpression(Box<'a, JSXMemberExpression<'a>>) = 2,
MemberExpression(Box<'a, JSXMemberExpression<'a>>) = 3,
}

/// JSX Namespaced Name
Expand Down Expand Up @@ -230,7 +235,8 @@ pub struct JSXMemberExpression<'a> {
#[serde(untagged)]
pub enum JSXMemberExpressionObject<'a> {
Identifier(Box<'a, JSXIdentifier<'a>>) = 0,
MemberExpression(Box<'a, JSXMemberExpression<'a>>) = 1,
IdentifierReference(Box<'a, JSXIdentifierReference<'a>>) = 1,
MemberExpression(Box<'a, JSXMemberExpression<'a>>) = 2,
}

/// JSX Expression Container
Expand Down Expand Up @@ -430,6 +436,31 @@ pub struct JSXIdentifier<'a> {
pub name: Atom<'a>,
}

/// JSX Identifier Reference
///
/// Similar to [`IdentifierReference`], but used in JSX elements.
///
/// [`IdentifierReference`]: super::IdentifierReference
#[ast(visit)]
#[derive(Debug)]
#[generate_derive(CloneIn, GetSpan, GetSpanMut)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[serde(tag = "type", rename = "JSXIdentifier")]
pub struct JSXIdentifierReference<'a> {
#[serde(flatten)]
pub span: Span,
/// The name of the identifier.
pub name: Atom<'a>,
/// Reference ID
///
/// Identifies what identifier this refers to, and how it is used. This is
/// set in the bind step of semantic analysis, and will always be [`None`]
/// immediately after parsing.
#[serde(skip)]
#[clone_in(default)]
pub reference_id: Cell<Option<ReferenceId>>,
}

// 1.4 JSX Children

/// JSX Child
Expand Down
10 changes: 10 additions & 0 deletions crates/oxc_ast/src/ast_impl/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,16 @@ impl<'a> fmt::Display for IdentifierReference<'a> {
}
}

impl<'a> From<&JSXIdentifierReference<'a>> for IdentifierReference<'a> {
fn from(value: &JSXIdentifierReference<'a>) -> Self {
IdentifierReference {
span: value.span,
name: value.name.clone(),
reference_id: value.reference_id.clone(),
}
}
}

impl<'a> Hash for BindingIdentifier<'a> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
Expand Down
44 changes: 41 additions & 3 deletions crates/oxc_ast/src/ast_impl/jsx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
use crate::ast::*;
use oxc_span::{Atom, Span};
use std::cell::Cell;
use std::fmt;
use std::hash::Hash;

// 1.2 JSX Elements

Expand All @@ -11,36 +13,67 @@ impl<'a> JSXIdentifier<'a> {
Self { span, name }
}
}

impl<'a> fmt::Display for JSXIdentifier<'a> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.name.fmt(f)
}
}

impl<'a> JSXIdentifier<'a> {
/// Determines whether the given current identifier is a reference
pub fn is_reference(&self) -> bool {
self.name.chars().next().map_or(false, char::is_uppercase)
}
}

impl<'a> Hash for JSXIdentifierReference<'a> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
}
}

impl<'a> fmt::Display for JSXIdentifierReference<'a> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.name.fmt(f)
}
}

impl<'a> From<JSXIdentifier<'a>> for JSXIdentifierReference<'a> {
fn from(value: JSXIdentifier<'a>) -> Self {
JSXIdentifierReference { span: value.span, name: value.name, reference_id: Cell::default() }
}
}

impl<'a> fmt::Display for JSXNamespacedName<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.namespace.name, self.property.name)
}
}

impl<'a> JSXElementName<'a> {
pub fn as_identifier(&self) -> Option<&JSXIdentifier<'a>> {
pub fn get_identifier_name(&self) -> Option<Atom<'a>> {
match self {
Self::Identifier(id) => Some(id.as_ref()),
Self::Identifier(id) => Some(id.as_ref().name.clone()),
Self::IdentifierReference(id) => Some(id.as_ref().name.clone()),
_ => None,
}
}
}

impl<'a> JSXMemberExpression<'a> {
pub fn get_object_identifier(&self) -> &JSXIdentifier {
pub fn get_object_identifier(&self) -> &JSXIdentifier<'a> {
let mut member_expr = self;
loop {
match &member_expr.object {
JSXMemberExpressionObject::Identifier(ident) => {
break ident;
}
JSXMemberExpressionObject::IdentifierReference(_) => {
unreachable!()
}
JSXMemberExpressionObject::MemberExpression(expr) => {
member_expr = expr;
}
Expand All @@ -55,6 +88,9 @@ impl<'a> JSXMemberExpression<'a> {
JSXMemberExpressionObject::Identifier(ident) => {
break &mut *ident;
}
JSXMemberExpressionObject::IdentifierReference(_) => {
unreachable!()
}
JSXMemberExpressionObject::MemberExpression(expr) => {
member_expr = expr;
}
Expand All @@ -73,6 +109,7 @@ impl<'a> fmt::Display for JSXMemberExpressionObject<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Identifier(id) => id.fmt(f),
Self::IdentifierReference(id) => id.fmt(f),
Self::MemberExpression(expr) => expr.fmt(f),
}
}
Expand All @@ -82,6 +119,7 @@ impl<'a> fmt::Display for JSXElementName<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Identifier(ident) => ident.fmt(f),
Self::IdentifierReference(ident) => ident.fmt(f),
Self::NamespacedName(namespaced) => namespaced.fmt(f),
Self::MemberExpression(member_expr) => member_expr.fmt(f),
}
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_ast/src/ast_kind_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ impl<'a> AstKind<'a> {
Self::JSXText(_) => "JSXText".into(),
Self::JSXExpressionContainer(_) => "JSXExpressionContainer".into(),
Self::JSXIdentifier(id) => format!("JSXIdentifier({id})").into(),
Self::JSXIdentifierReference(id) => format!("JSXIdentifierReference({id})").into(),
Self::JSXMemberExpression(_) => "JSXMemberExpression".into(),
Self::JSXMemberExpressionObject(_) => "JSXMemberExpressionObject".into(),
Self::JSXNamespacedName(_) => "JSXNamespacedName".into(),
Expand Down
12 changes: 12 additions & 0 deletions crates/oxc_ast/src/generated/assert_layouts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,12 @@ const _: () = {
assert!(offset_of!(JSXIdentifier, span) == 0usize);
assert!(offset_of!(JSXIdentifier, name) == 8usize);

assert!(size_of::<JSXIdentifierReference>() == 32usize);
assert!(align_of::<JSXIdentifierReference>() == 8usize);
assert!(offset_of!(JSXIdentifierReference, span) == 0usize);
assert!(offset_of!(JSXIdentifierReference, name) == 8usize);
assert!(offset_of!(JSXIdentifierReference, reference_id) == 24usize);

assert!(size_of::<JSXChild>() == 16usize);
assert!(align_of::<JSXChild>() == 8usize);

Expand Down Expand Up @@ -2766,6 +2772,12 @@ const _: () = {
assert!(offset_of!(JSXIdentifier, span) == 0usize);
assert!(offset_of!(JSXIdentifier, name) == 8usize);

assert!(size_of::<JSXIdentifierReference>() == 20usize);
assert!(align_of::<JSXIdentifierReference>() == 4usize);
assert!(offset_of!(JSXIdentifierReference, span) == 0usize);
assert!(offset_of!(JSXIdentifierReference, name) == 8usize);
assert!(offset_of!(JSXIdentifierReference, reference_id) == 16usize);

assert!(size_of::<JSXChild>() == 8usize);
assert!(align_of::<JSXChild>() == 4usize);

Expand Down
99 changes: 99 additions & 0 deletions crates/oxc_ast/src/generated/ast_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12926,6 +12926,34 @@ impl<'a> AstBuilder<'a> {
JSXElementName::Identifier(inner.into_in(self.allocator))
}

/// Build a [`JSXElementName::IdentifierReference`]
///
/// This node contains a [`JSXIdentifierReference`] that will be stored in the memory arena.
///
/// ## Parameters
/// - span: The [`Span`] covering this node
/// - name: The name of the identifier.
#[inline]
pub fn jsx_element_name_jsx_identifier_reference<A>(
self,
span: Span,
name: A,
) -> JSXElementName<'a>
where
A: IntoIn<'a, Atom<'a>>,
{
JSXElementName::IdentifierReference(self.alloc(self.jsx_identifier_reference(span, name)))
}

/// Convert a [`JSXIdentifierReference`] into a [`JSXElementName::IdentifierReference`]
#[inline]
pub fn jsx_element_name_from_jsx_identifier_reference<T>(self, inner: T) -> JSXElementName<'a>
where
T: IntoIn<'a, Box<'a, JSXIdentifierReference<'a>>>,
{
JSXElementName::IdentifierReference(inner.into_in(self.allocator))
}

/// Build a [`JSXElementName::NamespacedName`]
///
/// This node contains a [`JSXNamespacedName`] that will be stored in the memory arena.
Expand Down Expand Up @@ -13087,6 +13115,39 @@ impl<'a> AstBuilder<'a> {
JSXMemberExpressionObject::Identifier(inner.into_in(self.allocator))
}

/// Build a [`JSXMemberExpressionObject::IdentifierReference`]
///
/// This node contains a [`JSXIdentifierReference`] that will be stored in the memory arena.
///
/// ## Parameters
/// - span: The [`Span`] covering this node
/// - name: The name of the identifier.
#[inline]
pub fn jsx_member_expression_object_jsx_identifier_reference<A>(
self,
span: Span,
name: A,
) -> JSXMemberExpressionObject<'a>
where
A: IntoIn<'a, Atom<'a>>,
{
JSXMemberExpressionObject::IdentifierReference(
self.alloc(self.jsx_identifier_reference(span, name)),
)
}

/// Convert a [`JSXIdentifierReference`] into a [`JSXMemberExpressionObject::IdentifierReference`]
#[inline]
pub fn jsx_member_expression_object_from_jsx_identifier_reference<T>(
self,
inner: T,
) -> JSXMemberExpressionObject<'a>
where
T: IntoIn<'a, Box<'a, JSXIdentifierReference<'a>>>,
{
JSXMemberExpressionObject::IdentifierReference(inner.into_in(self.allocator))
}

/// Build a [`JSXMemberExpressionObject::MemberExpression`]
///
/// This node contains a [`JSXMemberExpression`] that will be stored in the memory arena.
Expand Down Expand Up @@ -13529,6 +13590,44 @@ impl<'a> AstBuilder<'a> {
Box::new_in(self.jsx_identifier(span, name), self.allocator)
}

/// Builds a [`JSXIdentifierReference`]
///
/// If you want the built node to be allocated in the memory arena, use [`AstBuilder::alloc_jsx_identifier_reference`] instead.
///
/// ## Parameters
/// - span: The [`Span`] covering this node
/// - name: The name of the identifier.
#[inline]
pub fn jsx_identifier_reference<A>(self, span: Span, name: A) -> JSXIdentifierReference<'a>
where
A: IntoIn<'a, Atom<'a>>,
{
JSXIdentifierReference {
span,
name: name.into_in(self.allocator),
reference_id: Default::default(),
}
}

/// Builds a [`JSXIdentifierReference`] and stores it in the memory arena.
///
/// Returns a [`Box`] containing the newly-allocated node. If you want a stack-allocated node, use [`AstBuilder::jsx_identifier_reference`] instead.
///
/// ## Parameters
/// - span: The [`Span`] covering this node
/// - name: The name of the identifier.
#[inline]
pub fn alloc_jsx_identifier_reference<A>(
self,
span: Span,
name: A,
) -> Box<'a, JSXIdentifierReference<'a>>
where
A: IntoIn<'a, Atom<'a>>,
{
Box::new_in(self.jsx_identifier_reference(span, name), self.allocator)
}

/// Build a [`JSXChild::Text`]
///
/// This node contains a [`JSXText`] that will be stored in the memory arena.
Expand Down
3 changes: 3 additions & 0 deletions crates/oxc_ast/src/generated/ast_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ pub enum AstType {
JSXAttributeItem,
JSXSpreadAttribute,
JSXIdentifier,
JSXIdentifierReference,
JSXText,
ExpressionArrayElement,
}
Expand Down Expand Up @@ -349,6 +350,7 @@ pub enum AstKind<'a> {
JSXAttributeItem(&'a JSXAttributeItem<'a>),
JSXSpreadAttribute(&'a JSXSpreadAttribute<'a>),
JSXIdentifier(&'a JSXIdentifier<'a>),
JSXIdentifierReference(&'a JSXIdentifierReference<'a>),
JSXText(&'a JSXText<'a>),
ExpressionArrayElement(&'a Expression<'a>),
}
Expand Down Expand Up @@ -524,6 +526,7 @@ impl<'a> GetSpan for AstKind<'a> {
Self::JSXAttributeItem(it) => it.span(),
Self::JSXSpreadAttribute(it) => it.span(),
Self::JSXIdentifier(it) => it.span(),
Self::JSXIdentifierReference(it) => it.span(),
Self::JSXText(it) => it.span(),
Self::ExpressionArrayElement(it) => it.span(),
}
Expand Down
Loading

0 comments on commit 6a849f0

Please sign in to comment.