Skip to content

Commit

Permalink
feat(ast)!: add JSXElementName::IdentifierReference and JSXMemberExpr…
Browse files Browse the repository at this point in the history
…essionObject::IdentifierReference
  • Loading branch information
Dunqing committed Aug 29, 2024
1 parent da8aa18 commit 9d745ae
Show file tree
Hide file tree
Showing 41 changed files with 1,054 additions and 1,629 deletions.
11 changes: 7 additions & 4 deletions crates/oxc_ast/src/ast/jsx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,14 @@ pub struct JSXClosingFragment {
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[serde(untagged)]
pub enum JSXElementName<'a> {
/// `<Apple />`
/// `<div />`
Identifier(Box<'a, JSXIdentifier<'a>>) = 0,
/// `<Apple />`
IdentifierReference(Box<'a, IdentifierReference<'a>>) = 1,
/// `<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 +232,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, IdentifierReference<'a>>) = 1,
MemberExpression(Box<'a, JSXMemberExpression<'a>>) = 2,
}

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

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

impl<'a> Hash for BindingIdentifier<'a> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
Expand Down Expand Up @@ -763,6 +769,7 @@ impl<'a> Declaration<'a> {
Self::VariableDeclaration(decl) => decl.is_typescript_syntax(),
Self::FunctionDeclaration(func) => func.is_typescript_syntax(),
Self::ClassDeclaration(class) => class.is_typescript_syntax(),
Self::UsingDeclaration(_) => false,
_ => true,
}
}
Expand All @@ -788,7 +795,7 @@ impl<'a> Declaration<'a> {
Declaration::TSTypeAliasDeclaration(decl) => decl.declare,
Declaration::TSModuleDeclaration(decl) => decl.declare,
Declaration::TSInterfaceDeclaration(decl) => decl.declare,
Declaration::TSImportEqualsDeclaration(_) => false,
_ => false,
}
}
}
Expand Down Expand Up @@ -816,17 +823,11 @@ impl VariableDeclarationKind {
matches!(self, Self::Const | Self::Let)
}

pub fn is_await(&self) -> bool {
matches!(self, Self::AwaitUsing)
}

pub fn as_str(&self) -> &'static str {
match self {
Self::Var => "var",
Self::Const => "const",
Self::Let => "let",
Self::Using => "using",
Self::AwaitUsing => "await using",
}
}
}
Expand Down
23 changes: 20 additions & 3 deletions crates/oxc_ast/src/ast_impl/jsx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,48 @@ 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> 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 +67,9 @@ impl<'a> JSXMemberExpression<'a> {
JSXMemberExpressionObject::Identifier(ident) => {
break &mut *ident;
}
JSXMemberExpressionObject::IdentifierReference(_) => {
unreachable!()
}
JSXMemberExpressionObject::MemberExpression(expr) => {
member_expr = expr;
}
Expand All @@ -73,6 +88,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 +98,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
57 changes: 57 additions & 0 deletions crates/oxc_ast/src/generated/ast_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12806,6 +12806,30 @@ impl<'a> AstBuilder<'a> {
JSXElementName::Identifier(inner.into_in(self.allocator))
}

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

/// Convert a [`IdentifierReference`] into a [`JSXElementName::IdentifierReference`]
#[inline]
pub fn jsx_element_name_from_identifier_reference<T>(self, inner: T) -> JSXElementName<'a>
where
T: IntoIn<'a, Box<'a, IdentifierReference<'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 @@ -12967,6 +12991,39 @@ impl<'a> AstBuilder<'a> {
JSXMemberExpressionObject::Identifier(inner.into_in(self.allocator))
}

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

/// Convert a [`IdentifierReference`] into a [`JSXMemberExpressionObject::IdentifierReference`]
#[inline]
pub fn jsx_member_expression_object_from_identifier_reference<T>(
self,
inner: T,
) -> JSXMemberExpressionObject<'a>
where
T: IntoIn<'a, Box<'a, IdentifierReference<'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
6 changes: 6 additions & 0 deletions crates/oxc_ast/src/generated/derive_clone_in.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3482,6 +3482,9 @@ impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for JSXElementName<'old_alloc>
fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned {
match self {
Self::Identifier(it) => JSXElementName::Identifier(it.clone_in(allocator)),
Self::IdentifierReference(it) => {
JSXElementName::IdentifierReference(it.clone_in(allocator))
}
Self::NamespacedName(it) => JSXElementName::NamespacedName(it.clone_in(allocator)),
Self::MemberExpression(it) => JSXElementName::MemberExpression(it.clone_in(allocator)),
}
Expand Down Expand Up @@ -3515,6 +3518,9 @@ impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for JSXMemberExpressionObject<'
fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned {
match self {
Self::Identifier(it) => JSXMemberExpressionObject::Identifier(it.clone_in(allocator)),
Self::IdentifierReference(it) => {
JSXMemberExpressionObject::IdentifierReference(it.clone_in(allocator))
}
Self::MemberExpression(it) => {
JSXMemberExpressionObject::MemberExpression(it.clone_in(allocator))
}
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 @@ -2030,6 +2030,7 @@ impl<'a> GetSpan for JSXElementName<'a> {
fn span(&self) -> Span {
match self {
Self::Identifier(it) => it.span(),
Self::IdentifierReference(it) => it.span(),
Self::NamespacedName(it) => it.span(),
Self::MemberExpression(it) => it.span(),
}
Expand All @@ -2054,6 +2055,7 @@ impl<'a> GetSpan for JSXMemberExpressionObject<'a> {
fn span(&self) -> Span {
match self {
Self::Identifier(it) => it.span(),
Self::IdentifierReference(it) => it.span(),
Self::MemberExpression(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 @@ -2030,6 +2030,7 @@ impl<'a> GetSpanMut for JSXElementName<'a> {
fn span_mut(&mut self) -> &mut Span {
match self {
Self::Identifier(it) => it.span_mut(),
Self::IdentifierReference(it) => it.span_mut(),
Self::NamespacedName(it) => it.span_mut(),
Self::MemberExpression(it) => it.span_mut(),
}
Expand All @@ -2054,6 +2055,7 @@ impl<'a> GetSpanMut for JSXMemberExpressionObject<'a> {
fn span_mut(&mut self) -> &mut Span {
match self {
Self::Identifier(it) => it.span_mut(),
Self::IdentifierReference(it) => it.span_mut(),
Self::MemberExpression(it) => it.span_mut(),
}
}
Expand Down
4 changes: 4 additions & 0 deletions crates/oxc_ast/src/generated/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3298,6 +3298,7 @@ pub mod walk {
visitor.enter_node(kind);
match it {
JSXElementName::Identifier(it) => visitor.visit_jsx_identifier(it),
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),
}
Expand Down Expand Up @@ -3341,6 +3342,9 @@ pub mod walk {
visitor.enter_node(kind);
match it {
JSXMemberExpressionObject::Identifier(it) => visitor.visit_jsx_identifier(it),
JSXMemberExpressionObject::IdentifierReference(it) => {
visitor.visit_identifier_reference(it)
}
JSXMemberExpressionObject::MemberExpression(it) => {
visitor.visit_jsx_member_expression(it)
}
Expand Down
4 changes: 4 additions & 0 deletions crates/oxc_ast/src/generated/visit_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3462,6 +3462,7 @@ pub mod walk_mut {
visitor.enter_node(kind);
match it {
JSXElementName::Identifier(it) => visitor.visit_jsx_identifier(it),
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),
}
Expand Down Expand Up @@ -3508,6 +3509,9 @@ pub mod walk_mut {
visitor.enter_node(kind);
match it {
JSXMemberExpressionObject::Identifier(it) => visitor.visit_jsx_identifier(it),
JSXMemberExpressionObject::IdentifierReference(it) => {
visitor.visit_identifier_reference(it)
}
JSXMemberExpressionObject::MemberExpression(it) => {
visitor.visit_jsx_member_expression(it)
}
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 @@ -2244,6 +2244,7 @@ impl<'a> Gen for JSXMemberExpressionObject<'a> {
fn gen(&self, p: &mut Codegen, ctx: Context) {
match self {
Self::Identifier(ident) => ident.gen(p, ctx),
Self::IdentifierReference(ident) => ident.gen(p, ctx),
Self::MemberExpression(member_expr) => member_expr.gen(p, ctx),
}
}
Expand All @@ -2261,6 +2262,7 @@ impl<'a> Gen for JSXElementName<'a> {
fn gen(&self, p: &mut Codegen, ctx: Context) {
match self {
Self::Identifier(identifier) => identifier.gen(p, ctx),
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),
}
Expand Down
16 changes: 7 additions & 9 deletions crates/oxc_linter/src/rules/jsx_a11y/anchor_is_valid.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use std::ops::Deref;

use oxc_ast::{
ast::{JSXAttributeItem, JSXAttributeValue, JSXElementName, JSXExpression},
ast::{JSXAttributeItem, JSXAttributeValue, JSXExpression},
AstKind,
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{CompactStr, Span};
use oxc_span::{CompactStr, GetSpan, Span};
use serde_json::Value;

use crate::{
Expand Down Expand Up @@ -129,15 +129,13 @@ impl Rule for AnchorIsValid {

fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
if let AstKind::JSXElement(jsx_el) = node.kind() {
let JSXElementName::Identifier(ident) = &jsx_el.opening_element.name else {
return;
};
let Some(name) = &get_element_type(ctx, &jsx_el.opening_element) else {
return;
};
if name != "a" {
return;
};
let span = jsx_el.opening_element.name.span();
if let Option::Some(href_attr) =
has_jsx_prop_ignore_case(&jsx_el.opening_element, "href")
{
Expand All @@ -147,17 +145,17 @@ impl Rule for AnchorIsValid {

// Check if the 'a' element has a correct href attribute
let Some(value) = attr.value.as_ref() else {
ctx.diagnostic(incorrect_href(ident.span));
ctx.diagnostic(incorrect_href(span));
return;
};

let is_empty = self.check_value_is_empty(value);
if is_empty {
if has_jsx_prop_ignore_case(&jsx_el.opening_element, "onclick").is_some() {
ctx.diagnostic(cant_be_anchor(ident.span));
ctx.diagnostic(cant_be_anchor(span));
return;
}
ctx.diagnostic(incorrect_href(ident.span));
ctx.diagnostic(incorrect_href(span));
return;
}
return;
Expand All @@ -170,7 +168,7 @@ impl Rule for AnchorIsValid {
if has_spread_attr {
return;
}
ctx.diagnostic(missing_href_attribute(ident.span));
ctx.diagnostic(missing_href_attribute(span));
}
}
}
Expand Down
Loading

0 comments on commit 9d745ae

Please sign in to comment.