Skip to content

Commit

Permalink
feat(ast)!: add ThisExpression variants to JSXElementName and `JS…
Browse files Browse the repository at this point in the history
…XMemberExpressionObject` (#5466)

Close #5352.

Add to AST:

* `JSXElementName::ThisExpression` (`<this>`)
* `JSXMemberExpressionObject::ThisExpression` (`<this.foo>`, `<this.foo.bar>`)
  • Loading branch information
overlookmotel committed Sep 5, 2024
1 parent 82c0a16 commit cba93f5
Show file tree
Hide file tree
Showing 23 changed files with 117 additions and 128 deletions.
3 changes: 3 additions & 0 deletions crates/oxc_ast/src/ast/jsx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ pub enum JSXElementName<'a> {
NamespacedName(Box<'a, JSXNamespacedName<'a>>) = 2,
/// `<Apple.Orange />`
MemberExpression(Box<'a, JSXMemberExpression<'a>>) = 3,
/// `<this />`
ThisExpression(Box<'a, ThisExpression>) = 4,
}

/// JSX Namespaced Name
Expand Down Expand Up @@ -233,6 +235,7 @@ pub struct JSXMemberExpression<'a> {
pub enum JSXMemberExpressionObject<'a> {
IdentifierReference(Box<'a, IdentifierReference<'a>>) = 0,
MemberExpression(Box<'a, JSXMemberExpression<'a>>) = 1,
ThisExpression(Box<'a, ThisExpression>) = 2,
}

/// JSX Expression Container
Expand Down
2 changes: 2 additions & 0 deletions crates/oxc_ast/src/ast_impl/jsx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ impl<'a> fmt::Display for JSXMemberExpressionObject<'a> {
match self {
Self::IdentifierReference(id) => id.fmt(f),
Self::MemberExpression(expr) => expr.fmt(f),
Self::ThisExpression(_) => "this".fmt(f),
}
}
}
Expand All @@ -57,6 +58,7 @@ impl<'a> fmt::Display for JSXElementName<'a> {
Self::IdentifierReference(ident) => ident.fmt(f),
Self::NamespacedName(namespaced) => namespaced.fmt(f),
Self::MemberExpression(member_expr) => member_expr.fmt(f),
Self::ThisExpression(_) => "this".fmt(f),
}
}
}
Expand Down
46 changes: 46 additions & 0 deletions crates/oxc_ast/src/generated/ast_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12903,6 +12903,26 @@ impl<'a> AstBuilder<'a> {
JSXElementName::MemberExpression(inner.into_in(self.allocator))
}

/// Build a [`JSXElementName::ThisExpression`]
///
/// This node contains a [`ThisExpression`] that will be stored in the memory arena.
///
/// ## Parameters
/// - span: The [`Span`] covering this node
#[inline]
pub fn jsx_element_name_this_expression(self, span: Span) -> JSXElementName<'a> {
JSXElementName::ThisExpression(self.alloc(self.this_expression(span)))
}

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

/// Builds a [`JSXNamespacedName`]
///
/// If you want the built node to be allocated in the memory arena, use [`AstBuilder::alloc_jsx_namespaced_name`] instead.
Expand Down Expand Up @@ -13040,6 +13060,32 @@ impl<'a> AstBuilder<'a> {
JSXMemberExpressionObject::MemberExpression(inner.into_in(self.allocator))
}

/// Build a [`JSXMemberExpressionObject::ThisExpression`]
///
/// This node contains a [`ThisExpression`] that will be stored in the memory arena.
///
/// ## Parameters
/// - span: The [`Span`] covering this node
#[inline]
pub fn jsx_member_expression_object_this_expression(
self,
span: Span,
) -> JSXMemberExpressionObject<'a> {
JSXMemberExpressionObject::ThisExpression(self.alloc(self.this_expression(span)))
}

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

/// Builds a [`JSXExpressionContainer`]
///
/// If you want the built node to be allocated in the memory arena, use [`AstBuilder::alloc_jsx_expression_container`] instead.
Expand Down
4 changes: 4 additions & 0 deletions crates/oxc_ast/src/generated/derive_clone_in.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3507,6 +3507,7 @@ impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for JSXElementName<'old_alloc>
}
Self::NamespacedName(it) => JSXElementName::NamespacedName(it.clone_in(allocator)),
Self::MemberExpression(it) => JSXElementName::MemberExpression(it.clone_in(allocator)),
Self::ThisExpression(it) => JSXElementName::ThisExpression(it.clone_in(allocator)),
}
}
}
Expand Down Expand Up @@ -3543,6 +3544,9 @@ impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for JSXMemberExpressionObject<'
Self::MemberExpression(it) => {
JSXMemberExpressionObject::MemberExpression(it.clone_in(allocator))
}
Self::ThisExpression(it) => {
JSXMemberExpressionObject::ThisExpression(it.clone_in(allocator))
}
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions crates/oxc_ast/src/generated/derive_content_eq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3555,6 +3555,9 @@ impl<'a> ContentEq for JSXElementName<'a> {
Self::MemberExpression(it) => {
matches!(other, Self::MemberExpression(other) if it.content_eq(other))
}
Self::ThisExpression(it) => {
matches!(other, Self::ThisExpression(other) if it.content_eq(other))
}
}
}
}
Expand All @@ -3580,6 +3583,9 @@ impl<'a> ContentEq for JSXMemberExpressionObject<'a> {
Self::MemberExpression(it) => {
matches!(other, Self::MemberExpression(other) if it.content_eq(other))
}
Self::ThisExpression(it) => {
matches!(other, Self::ThisExpression(other) if it.content_eq(other))
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/oxc_ast/src/generated/derive_get_span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2042,6 +2042,7 @@ impl<'a> GetSpan for JSXElementName<'a> {
Self::IdentifierReference(it) => it.span(),
Self::NamespacedName(it) => it.span(),
Self::MemberExpression(it) => it.span(),
Self::ThisExpression(it) => it.span(),
}
}
}
Expand All @@ -2065,6 +2066,7 @@ impl<'a> GetSpan for JSXMemberExpressionObject<'a> {
match self {
Self::IdentifierReference(it) => it.span(),
Self::MemberExpression(it) => it.span(),
Self::ThisExpression(it) => it.span(),
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/oxc_ast/src/generated/derive_get_span_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2042,6 +2042,7 @@ impl<'a> GetSpanMut for JSXElementName<'a> {
Self::IdentifierReference(it) => it.span_mut(),
Self::NamespacedName(it) => it.span_mut(),
Self::MemberExpression(it) => it.span_mut(),
Self::ThisExpression(it) => it.span_mut(),
}
}
}
Expand All @@ -2065,6 +2066,7 @@ impl<'a> GetSpanMut for JSXMemberExpressionObject<'a> {
match self {
Self::IdentifierReference(it) => it.span_mut(),
Self::MemberExpression(it) => it.span_mut(),
Self::ThisExpression(it) => it.span_mut(),
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/oxc_ast/src/generated/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3300,6 +3300,7 @@ pub mod walk {
JSXElementName::IdentifierReference(it) => visitor.visit_identifier_reference(it),
JSXElementName::NamespacedName(it) => visitor.visit_jsx_namespaced_name(it),
JSXElementName::MemberExpression(it) => visitor.visit_jsx_member_expression(it),
JSXElementName::ThisExpression(it) => visitor.visit_this_expression(it),
}
visitor.leave_node(kind);
}
Expand Down Expand Up @@ -3346,6 +3347,7 @@ pub mod walk {
JSXMemberExpressionObject::MemberExpression(it) => {
visitor.visit_jsx_member_expression(it)
}
JSXMemberExpressionObject::ThisExpression(it) => visitor.visit_this_expression(it),
}
visitor.leave_node(kind);
}
Expand Down
2 changes: 2 additions & 0 deletions crates/oxc_ast/src/generated/visit_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3464,6 +3464,7 @@ pub mod walk_mut {
JSXElementName::IdentifierReference(it) => visitor.visit_identifier_reference(it),
JSXElementName::NamespacedName(it) => visitor.visit_jsx_namespaced_name(it),
JSXElementName::MemberExpression(it) => visitor.visit_jsx_member_expression(it),
JSXElementName::ThisExpression(it) => visitor.visit_this_expression(it),
}
visitor.leave_node(kind);
}
Expand Down Expand Up @@ -3513,6 +3514,7 @@ pub mod walk_mut {
JSXMemberExpressionObject::MemberExpression(it) => {
visitor.visit_jsx_member_expression(it)
}
JSXMemberExpressionObject::ThisExpression(it) => visitor.visit_this_expression(it),
}
visitor.leave_node(kind);
}
Expand Down
2 changes: 2 additions & 0 deletions crates/oxc_codegen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2249,6 +2249,7 @@ impl<'a> Gen for JSXMemberExpressionObject<'a> {
match self {
Self::IdentifierReference(ident) => ident.gen(p, ctx),
Self::MemberExpression(member_expr) => member_expr.gen(p, ctx),
Self::ThisExpression(expr) => expr.gen(p, ctx),
}
}
}
Expand All @@ -2268,6 +2269,7 @@ impl<'a> Gen for JSXElementName<'a> {
Self::IdentifierReference(identifier) => identifier.gen(p, ctx),
Self::NamespacedName(namespaced_name) => namespaced_name.gen(p, ctx),
Self::MemberExpression(member_expr) => member_expr.gen(p, ctx),
Self::ThisExpression(expr) => expr.gen(p, ctx),
}
}
}
Expand Down
15 changes: 7 additions & 8 deletions crates/oxc_linter/src/rules/react/jsx_no_undef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,19 @@ declare_oxc_lint!(

fn get_resolvable_ident<'a>(node: &'a JSXElementName<'a>) -> Option<&'a IdentifierReference> {
match node {
JSXElementName::Identifier(_) | JSXElementName::NamespacedName(_) => None,
JSXElementName::Identifier(_)
| JSXElementName::NamespacedName(_)
| JSXElementName::ThisExpression(_) => None,
JSXElementName::IdentifierReference(ref ident) => Some(ident),
JSXElementName::MemberExpression(expr) => Some(get_member_ident(expr)),
JSXElementName::MemberExpression(expr) => get_member_ident(expr),
}
}

fn get_member_ident<'a>(mut expr: &'a JSXMemberExpression<'a>) -> &'a IdentifierReference {
fn get_member_ident<'a>(mut expr: &'a JSXMemberExpression<'a>) -> Option<&'a IdentifierReference> {
loop {
match &expr.object {
JSXMemberExpressionObject::IdentifierReference(ident) => return ident,
JSXMemberExpressionObject::IdentifierReference(ident) => return Some(ident),
JSXMemberExpressionObject::ThisExpression(_) => return None,
JSXMemberExpressionObject::MemberExpression(next_expr) => {
expr = next_expr;
}
Expand All @@ -61,10 +64,6 @@ impl Rule for JsxNoUndef {
return;
}
let name = ident.name.as_str();
// TODO: Remove this check once we have `JSXMemberExpressionObject::ThisExpression`
if name == "this" {
return;
}
if ctx.globals().is_enabled(name) {
return;
}
Expand Down
4 changes: 3 additions & 1 deletion crates/oxc_linter/src/rules/react/jsx_no_useless_fragment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,9 @@ fn is_jsx_fragment(elem: &JSXOpeningElement) -> bool {
false
}
}
JSXElementName::NamespacedName(_) | JSXElementName::Identifier(_) => false,
JSXElementName::NamespacedName(_)
| JSXElementName::Identifier(_)
| JSXElementName::ThisExpression(_) => false,
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,7 @@ impl<'a> ListenerMap for JSXElementName<'a> {
Self::Identifier(_) | Self::NamespacedName(_) => {}
Self::IdentifierReference(ident) => ident.report_effects_when_called(options),
Self::MemberExpression(member) => member.report_effects_when_called(options),
Self::ThisExpression(expr) => expr.report_effects_when_called(options),
}
}
}
Expand All @@ -806,6 +807,7 @@ impl<'a> ListenerMap for JSXMemberExpressionObject<'a> {
match self {
Self::IdentifierReference(ident) => ident.report_effects_when_called(options),
Self::MemberExpression(member) => member.report_effects_when_called(options),
Self::ThisExpression(expr) => expr.report_effects_when_called(options),
}
}
}
Expand Down
19 changes: 17 additions & 2 deletions crates/oxc_parser/src/jsx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ impl<'a> ParserImpl<'a> {
let element_name = if is_reference {
let identifier = self.ast.identifier_reference(identifier.span, identifier.name);
JSXElementName::IdentifierReference(self.ast.alloc(identifier))
} else if name == "this" {
JSXElementName::ThisExpression(
self.ast.alloc(self.ast.this_expression(identifier.span)),
)
} else {
JSXElementName::Identifier(self.ast.alloc(identifier))
};
Expand All @@ -185,9 +189,15 @@ impl<'a> ParserImpl<'a> {
span: Span,
object: JSXIdentifier<'a>,
) -> Result<Box<'a, JSXMemberExpression<'a>>> {
let mut object = if object.name == "this" {
let object = self.ast.this_expression(object.span);
JSXMemberExpressionObject::ThisExpression(self.ast.alloc(object))
} else {
let object = self.ast.identifier_reference(object.span, object.name);
JSXMemberExpressionObject::IdentifierReference(self.ast.alloc(object))
};

let mut span = span;
let object = self.ast.identifier_reference(object.span, object.name);
let mut object = JSXMemberExpressionObject::IdentifierReference(self.ast.alloc(object));
let mut property = None;

while self.eat(Kind::Dot) && !self.at(Kind::Eof) {
Expand Down Expand Up @@ -434,6 +444,7 @@ impl<'a> ParserImpl<'a> {
(JSXElementName::MemberExpression(lhs), JSXElementName::MemberExpression(rhs)) => {
Self::jsx_member_expression_eq(lhs, rhs)
}
(JSXElementName::ThisExpression(_), JSXElementName::ThisExpression(_)) => true,
_ => false,
}
}
Expand All @@ -454,6 +465,10 @@ impl<'a> ParserImpl<'a> {
JSXMemberExpressionObject::MemberExpression(lhs),
JSXMemberExpressionObject::MemberExpression(rhs),
) => Self::jsx_member_expression_eq(lhs, rhs),
(
JSXMemberExpressionObject::ThisExpression(_),
JSXMemberExpressionObject::ThisExpression(_),
) => true,
_ => false,
}
}
Expand Down
34 changes: 0 additions & 34 deletions crates/oxc_transformer/src/es2015/arrow_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,40 +131,6 @@ impl<'a> Traverse<'a> for ArrowFunctions<'a> {
self.insert_this_var_statement_at_the_top_of_statements(&mut body.statements);
}

/// Change <this></this> to <_this></_this>, and mark it as found
fn enter_jsx_element_name(&mut self, name: &mut JSXElementName<'a>, ctx: &mut TraverseCtx<'a>) {
if !self.is_inside_arrow_function() {
return;
}

if let JSXElementName::Identifier(ident) = name {
if ident.name == "this" {
let mut new_ident = self.get_this_name(ctx).create_read_reference(ctx);
new_ident.span = ident.span;
*name = self.ctx.ast.jsx_element_name_from_identifier_reference(new_ident);
}
}
}

/// Change <this.foo></this.foo> to <_this.foo></_this.foo>, and mark it as found
fn enter_jsx_member_expression_object(
&mut self,
node: &mut JSXMemberExpressionObject<'a>,
ctx: &mut TraverseCtx<'a>,
) {
if !self.is_inside_arrow_function() {
return;
}

if let JSXMemberExpressionObject::IdentifierReference(ident) = node {
if ident.name == "this" {
let new_ident = self.get_this_name(ctx).create_read_reference(ctx);
ident.name = new_ident.name;
ident.reference_id = new_ident.reference_id;
}
}
}

fn enter_expression(&mut self, expr: &mut Expression<'a>, _ctx: &mut TraverseCtx<'a>) {
match expr {
Expression::ArrowFunctionExpression(_) => {
Expand Down
16 changes: 0 additions & 16 deletions crates/oxc_transformer/src/es2015/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,22 +51,6 @@ impl<'a> Traverse<'a> for ES2015<'a> {
}
}

fn enter_jsx_element_name(&mut self, elem: &mut JSXElementName<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.arrow_function.is_some() {
self.arrow_functions.enter_jsx_element_name(elem, ctx);
}
}

fn enter_jsx_member_expression_object(
&mut self,
node: &mut JSXMemberExpressionObject<'a>,
ctx: &mut TraverseCtx<'a>,
) {
if self.options.arrow_function.is_some() {
self.arrow_functions.enter_jsx_member_expression_object(node, ctx);
}
}

fn enter_declaration(&mut self, decl: &mut Declaration<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.arrow_function.is_some() {
self.arrow_functions.enter_declaration(decl, ctx);
Expand Down
Loading

0 comments on commit cba93f5

Please sign in to comment.