perf(ast): box enum variants (#3058)

Box all enum variants for JSX types (`JSXAttributeName`,
`JSXAttributeValue`, `JSXChild`, `JSXElementName`,
`JSXMemberExpressionObject`). Part of #3047.

I'm not sure how to interpret the benchmark results. As I said on #3047:

> I imagine it may cost a little in performance in the parser due to
extra calls to `alloc`, but in return traversing the AST should be
cheaper, as the data is more compact, so less cache misses.

Sure enough, there is a small impact (1%) on the 2 parser benchmarks for
JSX files. However, the other benchmarks have too much noise in them to
see whether this is repaid in a speed up on transformer etc, especially
as the transformer benchmarks also include parsing.

What do you think @Boshen?
This commit is contained in:
overlookmotel 2024-04-22 02:09:30 +01:00 committed by GitHub
parent 68d082644c
commit 48e20880d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 182 additions and 176 deletions

View file

@ -83,7 +83,7 @@ pub struct JSXClosingFragment {
#[cfg_attr(feature = "serialize", serde(untagged))] #[cfg_attr(feature = "serialize", serde(untagged))]
pub enum JSXElementName<'a> { pub enum JSXElementName<'a> {
/// `<Apple />` /// `<Apple />`
Identifier(JSXIdentifier<'a>), Identifier(Box<'a, JSXIdentifier<'a>>),
/// `<Apple:Orange />` /// `<Apple:Orange />`
NamespacedName(Box<'a, JSXNamespacedName<'a>>), NamespacedName(Box<'a, JSXNamespacedName<'a>>),
/// `<Apple.Orange />` /// `<Apple.Orange />`
@ -131,7 +131,7 @@ impl<'a> JSXMemberExpression<'a> {
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))] #[cfg_attr(feature = "serialize", serde(untagged))]
pub enum JSXMemberExpressionObject<'a> { pub enum JSXMemberExpressionObject<'a> {
Identifier(JSXIdentifier<'a>), Identifier(Box<'a, JSXIdentifier<'a>>),
MemberExpression(Box<'a, JSXMemberExpression<'a>>), MemberExpression(Box<'a, JSXMemberExpression<'a>>),
} }
@ -203,7 +203,7 @@ pub struct JSXSpreadAttribute<'a> {
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))] #[cfg_attr(feature = "serialize", serde(untagged))]
pub enum JSXAttributeName<'a> { pub enum JSXAttributeName<'a> {
Identifier(JSXIdentifier<'a>), Identifier(Box<'a, JSXIdentifier<'a>>),
NamespacedName(Box<'a, JSXNamespacedName<'a>>), NamespacedName(Box<'a, JSXNamespacedName<'a>>),
} }
@ -212,8 +212,8 @@ pub enum JSXAttributeName<'a> {
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))] #[cfg_attr(feature = "serialize", serde(untagged))]
pub enum JSXAttributeValue<'a> { pub enum JSXAttributeValue<'a> {
StringLiteral(StringLiteral<'a>), StringLiteral(Box<'a, StringLiteral<'a>>),
ExpressionContainer(JSXExpressionContainer<'a>), ExpressionContainer(Box<'a, JSXExpressionContainer<'a>>),
Element(Box<'a, JSXElement<'a>>), Element(Box<'a, JSXElement<'a>>),
Fragment(Box<'a, JSXFragment<'a>>), Fragment(Box<'a, JSXFragment<'a>>),
} }
@ -240,11 +240,11 @@ impl<'a> JSXIdentifier<'a> {
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))] #[cfg_attr(feature = "serialize", serde(untagged))]
pub enum JSXChild<'a> { pub enum JSXChild<'a> {
Text(JSXText<'a>), Text(Box<'a, JSXText<'a>>),
Element(Box<'a, JSXElement<'a>>), Element(Box<'a, JSXElement<'a>>),
Fragment(Box<'a, JSXFragment<'a>>), Fragment(Box<'a, JSXFragment<'a>>),
ExpressionContainer(JSXExpressionContainer<'a>), ExpressionContainer(Box<'a, JSXExpressionContainer<'a>>),
Spread(JSXSpreadChild<'a>), Spread(Box<'a, JSXSpreadChild<'a>>),
} }
#[derive(Debug, Hash)] #[derive(Debug, Hash)]

View file

@ -1302,12 +1302,16 @@ impl<'a> AstBuilder<'a> {
&self, &self,
span: Span, span: Span,
expression: JSXExpression<'a>, expression: JSXExpression<'a>,
) -> JSXExpressionContainer<'a> { ) -> Box<'a, JSXExpressionContainer<'a>> {
JSXExpressionContainer { span, expression } self.alloc(JSXExpressionContainer { span, expression })
} }
pub fn jsx_spread_child(&self, span: Span, expression: Expression<'a>) -> JSXSpreadChild<'a> { pub fn jsx_spread_child(
JSXSpreadChild { span, expression } &self,
span: Span,
expression: Expression<'a>,
) -> Box<'a, JSXSpreadChild<'a>> {
self.alloc(JSXSpreadChild { span, expression })
} }
pub fn jsx_empty_expression(&self, span: Span) -> JSXEmptyExpression { pub fn jsx_empty_expression(&self, span: Span) -> JSXEmptyExpression {
@ -1335,8 +1339,8 @@ impl<'a> AstBuilder<'a> {
JSXIdentifier { span, name } JSXIdentifier { span, name }
} }
pub fn jsx_text(&self, span: Span, value: Atom<'a>) -> JSXText<'a> { pub fn jsx_text(&self, span: Span, value: Atom<'a>) -> Box<'a, JSXText<'a>> {
JSXText { span, value } self.alloc(JSXText { span, value })
} }
/* ---------- TypeScript ---------- */ /* ---------- TypeScript ---------- */

View file

@ -1,8 +1,5 @@
use oxc_ast::{ use oxc_ast::{
ast::{ ast::{JSXAttributeItem, JSXAttributeValue, JSXElement, JSXExpression, JSXOpeningElement},
JSXAttributeItem, JSXAttributeValue, JSXElement, JSXExpression, JSXExpressionContainer,
JSXOpeningElement,
},
AstKind, AstKind,
}; };
use oxc_diagnostics::{ use oxc_diagnostics::{
@ -212,10 +209,13 @@ impl Rule for AltText {
fn is_valid_alt_prop(item: &JSXAttributeItem<'_>) -> bool { fn is_valid_alt_prop(item: &JSXAttributeItem<'_>) -> bool {
match get_prop_value(item) { match get_prop_value(item) {
None => false, None => false,
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { Some(JSXAttributeValue::ExpressionContainer(container)) => {
expression: JSXExpression::Expression(expr), if let JSXExpression::Expression(expr) = &container.expression {
.. !expr.is_null_or_undefined()
})) => !expr.is_null_or_undefined(), } else {
true
}
}
_ => true, _ => true,
} }
} }
@ -229,10 +229,13 @@ fn aria_label_has_value<'a>(item: &'a JSXAttributeItem<'a>) -> bool {
match get_prop_value(item) { match get_prop_value(item) {
None => false, None => false,
Some(JSXAttributeValue::StringLiteral(s)) if s.value.is_empty() => false, Some(JSXAttributeValue::StringLiteral(s)) if s.value.is_empty() => false,
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { Some(JSXAttributeValue::ExpressionContainer(container)) => {
expression: JSXExpression::Expression(expr), if let JSXExpression::Expression(expr) = &container.expression {
.. !expr.is_undefined()
})) => !expr.is_undefined(), } else {
true
}
}
_ => true, _ => true,
} }
} }

View file

@ -1,5 +1,5 @@
use oxc_ast::{ use oxc_ast::{
ast::{JSXAttributeValue, JSXExpression, JSXExpressionContainer}, ast::{JSXAttributeValue, JSXExpression},
AstKind, AstKind,
}; };
use oxc_diagnostics::{ use oxc_diagnostics::{
@ -134,12 +134,11 @@ impl Rule for AriaRole {
}; };
match get_prop_value(aria_role) { match get_prop_value(aria_role) {
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { Some(JSXAttributeValue::ExpressionContainer(container)) => {
expression: JSXExpression::Expression(expr), if let JSXExpression::Expression(expr) = &container.expression {
.. if expr.is_undefined() || expr.is_null() {
})) => { ctx.diagnostic(AriaRoleDiagnostic(attr.span, String::new()));
if expr.is_undefined() || expr.is_null() { }
ctx.diagnostic(AriaRoleDiagnostic(attr.span, String::new()));
} }
} }
Some(JSXAttributeValue::StringLiteral(str)) => { Some(JSXAttributeValue::StringLiteral(str)) => {

View file

@ -1,7 +1,5 @@
use oxc_ast::{ use oxc_ast::{
ast::{ ast::{JSXAttributeItem, JSXAttributeValue, JSXElementName, JSXExpression},
JSXAttributeItem, JSXAttributeValue, JSXElementName, JSXExpression, JSXExpressionContainer,
},
AstKind, AstKind,
}; };
use oxc_diagnostics::{ use oxc_diagnostics::{
@ -87,10 +85,13 @@ impl Rule for HtmlHasLang {
fn is_valid_lang_prop(item: &JSXAttributeItem) -> bool { fn is_valid_lang_prop(item: &JSXAttributeItem) -> bool {
match get_prop_value(item) { match get_prop_value(item) {
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { Some(JSXAttributeValue::ExpressionContainer(container)) => {
expression: JSXExpression::Expression(expr), if let JSXExpression::Expression(expr) = &container.expression {
.. !expr.is_undefined()
})) => !expr.is_undefined(), } else {
true
}
}
Some(JSXAttributeValue::StringLiteral(str)) => !str.value.as_str().is_empty(), Some(JSXAttributeValue::StringLiteral(str)) => !str.value.as_str().is_empty(),
_ => true, _ => true,
} }

View file

@ -1,5 +1,5 @@
use oxc_ast::{ use oxc_ast::{
ast::{Expression, JSXAttributeValue, JSXElementName, JSXExpression, JSXExpressionContainer}, ast::{Expression, JSXAttributeValue, JSXElementName, JSXExpression},
AstKind, AstKind,
}; };
use oxc_diagnostics::{ use oxc_diagnostics::{
@ -88,28 +88,27 @@ impl Rule for IframeHasTitle {
return; return;
} }
} }
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { Some(JSXAttributeValue::ExpressionContainer(container)) => {
expression: JSXExpression::Expression(expr), if let JSXExpression::Expression(expr) = &container.expression {
.. if expr.is_string_literal() {
})) => { if let Expression::StringLiteral(str) = expr {
if expr.is_string_literal() { if !str.value.as_str().is_empty() {
if let Expression::StringLiteral(str) = expr { return;
if !str.value.as_str().is_empty() { }
return; }
if let Expression::TemplateLiteral(tmpl) = expr {
if !tmpl.quasis.is_empty()
& !tmpl.expressions.is_empty()
& tmpl.quasis.iter().any(|q| !q.value.raw.as_str().is_empty())
{
return;
}
} }
} }
if let Expression::TemplateLiteral(tmpl) = expr {
if !tmpl.quasis.is_empty()
& !tmpl.expressions.is_empty()
& tmpl.quasis.iter().any(|q| !q.value.raw.as_str().is_empty())
{
return;
}
}
}
if expr.is_identifier_reference() & !expr.is_undefined() { if expr.is_identifier_reference() & !expr.is_undefined() {
return; return;
}
} }
} }
_ => {} _ => {}

View file

@ -1,10 +1,7 @@
use regex::Regex; use regex::Regex;
use oxc_ast::{ use oxc_ast::{
ast::{ ast::{Expression, JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXExpression},
Expression, JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXExpression,
JSXExpressionContainer,
},
AstKind, AstKind,
}; };
use oxc_diagnostics::{ use oxc_diagnostics::{
@ -149,18 +146,15 @@ impl Rule for ImgRedundantAlt {
ctx.diagnostic(ImgRedundantAltDiagnostic(alt_attribute_name_span)); ctx.diagnostic(ImgRedundantAltDiagnostic(alt_attribute_name_span));
} }
} }
JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { JSXAttributeValue::ExpressionContainer(container) => match &container.expression {
expression: JSXExpression::Expression(expression), JSXExpression::Expression(Expression::StringLiteral(lit)) => {
..
}) => match expression {
Expression::StringLiteral(lit) => {
let alt_text = lit.value.as_str(); let alt_text = lit.value.as_str();
if is_redundant_alt_text(alt_text, &self.redundant_words) { if is_redundant_alt_text(alt_text, &self.redundant_words) {
ctx.diagnostic(ImgRedundantAltDiagnostic(alt_attribute_name_span)); ctx.diagnostic(ImgRedundantAltDiagnostic(alt_attribute_name_span));
} }
} }
Expression::TemplateLiteral(lit) => { JSXExpression::Expression(Expression::TemplateLiteral(lit)) => {
for quasi in &lit.quasis { for quasi in &lit.quasis {
let alt_text = quasi.value.raw.as_str(); let alt_text = quasi.value.raw.as_str();

View file

@ -1,8 +1,6 @@
use language_tags::LanguageTag; use language_tags::LanguageTag;
use oxc_ast::{ use oxc_ast::{
ast::{ ast::{JSXAttributeItem, JSXAttributeValue, JSXElementName, JSXExpression},
JSXAttributeItem, JSXAttributeValue, JSXElementName, JSXExpression, JSXExpressionContainer,
},
AstKind, AstKind,
}; };
use oxc_diagnostics::{ use oxc_diagnostics::{
@ -94,10 +92,13 @@ impl Rule for Lang {
fn is_valid_lang_prop(item: &JSXAttributeItem) -> bool { fn is_valid_lang_prop(item: &JSXAttributeItem) -> bool {
match get_prop_value(item) { match get_prop_value(item) {
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { Some(JSXAttributeValue::ExpressionContainer(container)) => {
expression: JSXExpression::Expression(expr), if let JSXExpression::Expression(expr) = &container.expression {
.. !expr.is_undefined()
})) => !expr.is_undefined(), } else {
true
}
}
Some(JSXAttributeValue::StringLiteral(str)) => { Some(JSXAttributeValue::StringLiteral(str)) => {
let language_tag = LanguageTag::parse(str.value.as_str()).unwrap(); let language_tag = LanguageTag::parse(str.value.as_str()).unwrap();
language_tag.is_valid() language_tag.is_valid()

View file

@ -1,5 +1,5 @@
use oxc_ast::{ use oxc_ast::{
ast::{JSXAttributeValue, JSXExpression, JSXExpressionContainer}, ast::{JSXAttributeValue, JSXExpression},
AstKind, AstKind,
}; };
use oxc_diagnostics::{ use oxc_diagnostics::{
@ -119,15 +119,14 @@ impl Rule for MouseEventsHaveKeyEvents {
} }
match has_jsx_prop(jsx_opening_el, "onFocus").and_then(get_prop_value) { match has_jsx_prop(jsx_opening_el, "onFocus").and_then(get_prop_value) {
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { Some(JSXAttributeValue::ExpressionContainer(container)) => {
expression: JSXExpression::Expression(expr), if let JSXExpression::Expression(expr) = &container.expression {
.. if expr.is_undefined() {
})) => { ctx.diagnostic(MouseEventsHaveKeyEventsDiagnostic::MissOnFocus(
if expr.is_undefined() { jsx_attr.span(),
ctx.diagnostic(MouseEventsHaveKeyEventsDiagnostic::MissOnFocus( String::from(handler),
jsx_attr.span(), ));
String::from(handler), }
));
} }
} }
None => { None => {
@ -150,15 +149,14 @@ impl Rule for MouseEventsHaveKeyEvents {
} }
match has_jsx_prop(jsx_opening_el, "onBlur").and_then(get_prop_value) { match has_jsx_prop(jsx_opening_el, "onBlur").and_then(get_prop_value) {
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { Some(JSXAttributeValue::ExpressionContainer(container)) => {
expression: JSXExpression::Expression(expr), if let JSXExpression::Expression(expr) = &container.expression {
.. if expr.is_undefined() {
})) => { ctx.diagnostic(MouseEventsHaveKeyEventsDiagnostic::MissOnBlur(
if expr.is_undefined() { jsx_attr.span(),
ctx.diagnostic(MouseEventsHaveKeyEventsDiagnostic::MissOnBlur( String::from(handler),
jsx_attr.span(), ));
String::from(handler), }
));
} }
} }
None => { None => {

View file

@ -1,5 +1,5 @@
use oxc_ast::{ use oxc_ast::{
ast::{JSXAttributeItem, JSXAttributeValue, JSXExpression, JSXExpressionContainer}, ast::{JSXAttributeItem, JSXAttributeValue, JSXExpression},
AstKind, AstKind,
}; };
use oxc_diagnostics::{ use oxc_diagnostics::{
@ -48,14 +48,13 @@ impl Rule for NoAccessKey {
Some(JSXAttributeValue::StringLiteral(_)) => { Some(JSXAttributeValue::StringLiteral(_)) => {
ctx.diagnostic(NoAccessKeyDiagnostic(attr.span)); ctx.diagnostic(NoAccessKeyDiagnostic(attr.span));
} }
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { Some(JSXAttributeValue::ExpressionContainer(container)) => {
expression: JSXExpression::Expression(expr), if let JSXExpression::Expression(expr) = &container.expression {
.. if expr.is_identifier_reference() & expr.is_undefined() {
})) => { return;
if expr.is_identifier_reference() & expr.is_undefined() { }
return; ctx.diagnostic(NoAccessKeyDiagnostic(attr.span));
} }
ctx.diagnostic(NoAccessKeyDiagnostic(attr.span));
} }
_ => {} _ => {}
} }

View file

@ -1,5 +1,5 @@
use oxc_ast::{ use oxc_ast::{
ast::{JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXElementName, JSXIdentifier}, ast::{JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXElementName},
AstKind, AstKind,
}; };
use oxc_diagnostics::{ use oxc_diagnostics::{
@ -48,8 +48,9 @@ impl Rule for NoBeforeInteractiveScriptOutsideDocument {
if is_in_app_dir(file_path) { if is_in_app_dir(file_path) {
return; return;
} }
let JSXElementName::Identifier(JSXIdentifier { name: tag_name, .. }) = &jsx_el.name let tag_name = if let JSXElementName::Identifier(ident) = &jsx_el.name {
else { &ident.name
} else {
return; return;
}; };
if jsx_el.attributes.len() == 0 { if jsx_el.attributes.len() == 0 {

View file

@ -1,5 +1,5 @@
use oxc_ast::{ use oxc_ast::{
ast::{JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXElementName, JSXIdentifier}, ast::{JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXElementName},
AstKind, AstKind,
}; };
use oxc_diagnostics::{ use oxc_diagnostics::{
@ -111,8 +111,9 @@ const NEXT_POLYFILLED_FEATURES: Set<&'static str> = phf_set! {
impl Rule for NoUnwantedPolyfillio { impl Rule for NoUnwantedPolyfillio {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
if let AstKind::JSXOpeningElement(jsx_el) = node.kind() { if let AstKind::JSXOpeningElement(jsx_el) = node.kind() {
let JSXElementName::Identifier(JSXIdentifier { name: tag_name, .. }) = &jsx_el.name let tag_name = if let JSXElementName::Identifier(ident) = &jsx_el.name {
else { &ident.name
} else {
return; return;
}; };

View file

@ -1,7 +1,7 @@
use oxc_ast::{ use oxc_ast::{
ast::{ ast::{
Argument, Expression, JSXAttributeItem, JSXAttributeValue, JSXElementName, JSXExpression, Argument, Expression, JSXAttributeItem, JSXAttributeValue, JSXElementName, JSXExpression,
JSXExpressionContainer, ObjectPropertyKind, ObjectPropertyKind,
}, },
AstKind, AstKind,
}; };
@ -163,10 +163,13 @@ impl Rule for ButtonHasType {
impl ButtonHasType { impl ButtonHasType {
fn is_valid_button_type_prop(&self, item: &JSXAttributeItem) -> bool { fn is_valid_button_type_prop(&self, item: &JSXAttributeItem) -> bool {
match get_prop_value(item) { match get_prop_value(item) {
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { Some(JSXAttributeValue::ExpressionContainer(container)) => {
expression: JSXExpression::Expression(expr), if let JSXExpression::Expression(expr) = &container.expression {
.. self.is_valid_button_type_prop_expression(expr)
})) => self.is_valid_button_type_prop_expression(expr), } else {
false
}
}
Some(JSXAttributeValue::StringLiteral(str)) => { Some(JSXAttributeValue::StringLiteral(str)) => {
self.is_valid_button_type_prop_string_literal(str.value.as_str()) self.is_valid_button_type_prop_string_literal(str.value.as_str())
} }

View file

@ -1,5 +1,5 @@
use oxc_ast::{ use oxc_ast::{
ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression, JSXExpressionContainer}, ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression},
AstKind, AstKind,
}; };
use oxc_diagnostics::{ use oxc_diagnostics::{
@ -52,12 +52,11 @@ fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) {
for item in &jsx_elem.opening_element.attributes { for item in &jsx_elem.opening_element.attributes {
match get_prop_value(item) { match get_prop_value(item) {
None => return, None => return,
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { Some(JSXAttributeValue::ExpressionContainer(container)) => {
expression: JSXExpression::Expression(expr), if let JSXExpression::Expression(expr) = &container.expression {
.. if let Some(span) = check_expression(expr) {
})) => { ctx.diagnostic(JsxNoJsxAsPropDiagnostic(span));
if let Some(span) = check_expression(expr) { }
ctx.diagnostic(JsxNoJsxAsPropDiagnostic(span));
} }
} }
_ => {} _ => {}

View file

@ -1,5 +1,5 @@
use oxc_ast::{ use oxc_ast::{
ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression, JSXExpressionContainer}, ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression},
AstKind, AstKind,
}; };
use oxc_diagnostics::{ use oxc_diagnostics::{
@ -58,12 +58,11 @@ fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) {
for item in &jsx_elem.opening_element.attributes { for item in &jsx_elem.opening_element.attributes {
match get_prop_value(item) { match get_prop_value(item) {
None => return, None => return,
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { Some(JSXAttributeValue::ExpressionContainer(container)) => {
expression: JSXExpression::Expression(expr), if let JSXExpression::Expression(expr) = &container.expression {
.. if let Some(span) = check_expression(expr) {
})) => { ctx.diagnostic(JsxNoNewArrayAsPropDiagnostic(span));
if let Some(span) = check_expression(expr) { }
ctx.diagnostic(JsxNoNewArrayAsPropDiagnostic(span));
} }
} }
_ => {} _ => {}

View file

@ -1,8 +1,5 @@
use oxc_ast::{ use oxc_ast::{
ast::{ ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression, MemberExpression},
Expression, JSXAttributeValue, JSXElement, JSXExpression, JSXExpressionContainer,
MemberExpression,
},
AstKind, AstKind,
}; };
use oxc_diagnostics::{ use oxc_diagnostics::{
@ -56,12 +53,11 @@ fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) {
for item in &jsx_elem.opening_element.attributes { for item in &jsx_elem.opening_element.attributes {
match get_prop_value(item) { match get_prop_value(item) {
None => return, None => return,
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { Some(JSXAttributeValue::ExpressionContainer(container)) => {
expression: JSXExpression::Expression(expr), if let JSXExpression::Expression(expr) = &container.expression {
.. if let Some(span) = check_expression(expr) {
})) => { ctx.diagnostic(JsxNoNewFunctionAsPropDiagnostic(span));
if let Some(span) = check_expression(expr) { }
ctx.diagnostic(JsxNoNewFunctionAsPropDiagnostic(span));
} }
} }
_ => {} _ => {}

View file

@ -1,5 +1,5 @@
use oxc_ast::{ use oxc_ast::{
ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression, JSXExpressionContainer}, ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression},
AstKind, AstKind,
}; };
use oxc_diagnostics::{ use oxc_diagnostics::{
@ -57,12 +57,11 @@ fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) {
for item in &jsx_elem.opening_element.attributes { for item in &jsx_elem.opening_element.attributes {
match get_prop_value(item) { match get_prop_value(item) {
None => return, None => return,
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { Some(JSXAttributeValue::ExpressionContainer(container)) => {
expression: JSXExpression::Expression(expr), if let JSXExpression::Expression(expr) = &container.expression {
.. if let Some(span) = check_expression(expr) {
})) => { ctx.diagnostic(JsxNoNewObjectAsPropDiagnostic(span));
if let Some(span) = check_expression(expr) { }
ctx.diagnostic(JsxNoNewObjectAsPropDiagnostic(span));
} }
} }
_ => {} _ => {}

View file

@ -1,8 +1,7 @@
use oxc_ast::{ use oxc_ast::{
ast::{ ast::{
CallExpression, Expression, JSXAttributeItem, JSXAttributeName, JSXAttributeValue, CallExpression, Expression, JSXAttributeItem, JSXAttributeName, JSXAttributeValue,
JSXChild, JSXElement, JSXElementName, JSXExpression, JSXExpressionContainer, JSXChild, JSXElement, JSXElementName, JSXExpression, JSXOpeningElement,
JSXOpeningElement,
}, },
AstKind, AstKind,
}; };
@ -94,10 +93,13 @@ pub fn is_hidden_from_screen_reader(ctx: &LintContext, node: &JSXOpeningElement)
has_jsx_prop_lowercase(node, "aria-hidden").map_or(false, |v| match get_prop_value(v) { has_jsx_prop_lowercase(node, "aria-hidden").map_or(false, |v| match get_prop_value(v) {
None => true, None => true,
Some(JSXAttributeValue::StringLiteral(s)) if s.value == "true" => true, Some(JSXAttributeValue::StringLiteral(s)) if s.value == "true" => true,
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { Some(JSXAttributeValue::ExpressionContainer(container)) => {
expression: JSXExpression::Expression(expr), if let JSXExpression::Expression(expr) = &container.expression {
.. expr.get_boolean_value().unwrap_or(false)
})) => expr.get_boolean_value().unwrap_or(false), } else {
false
}
}
_ => false, _ => false,
}) })
} }
@ -107,10 +109,13 @@ pub fn object_has_accessible_child(ctx: &LintContext, node: &JSXElement<'_>) ->
node.children.iter().any(|child| match child { node.children.iter().any(|child| match child {
JSXChild::Text(text) => !text.value.is_empty(), JSXChild::Text(text) => !text.value.is_empty(),
JSXChild::Element(el) => !is_hidden_from_screen_reader(ctx, &el.opening_element), JSXChild::Element(el) => !is_hidden_from_screen_reader(ctx, &el.opening_element),
JSXChild::ExpressionContainer(JSXExpressionContainer { JSXChild::ExpressionContainer(container) => {
expression: JSXExpression::Expression(expr), if let JSXExpression::Expression(expr) = &container.expression {
.. !expr.is_undefined() && !expr.is_null()
}) => !expr.is_undefined() && !expr.is_null(), } else {
false
}
}
_ => false, _ => false,
}) || has_jsx_prop_lowercase(&node.opening_element, "dangerouslySetInnerHTML").is_some() }) || has_jsx_prop_lowercase(&node.opening_element, "dangerouslySetInnerHTML").is_some()
|| has_jsx_prop_lowercase(&node.opening_element, "children").is_some() || has_jsx_prop_lowercase(&node.opening_element, "children").is_some()
@ -252,15 +257,14 @@ pub fn get_element_type(context: &LintContext, element: &JSXOpeningElement) -> O
pub fn parse_jsx_value(value: &JSXAttributeValue) -> Result<f64, ()> { pub fn parse_jsx_value(value: &JSXAttributeValue) -> Result<f64, ()> {
match value { match value {
JSXAttributeValue::StringLiteral(str) => str.value.parse().or(Err(())), JSXAttributeValue::StringLiteral(str) => str.value.parse().or(Err(())),
JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { JSXAttributeValue::ExpressionContainer(container) => match &container.expression {
expression: JSXExpression::Expression(expression), JSXExpression::Expression(Expression::StringLiteral(str)) => {
.. str.value.parse().or(Err(()))
}) => match expression { }
Expression::StringLiteral(str) => str.value.parse().or(Err(())), JSXExpression::Expression(Expression::TemplateLiteral(tmpl)) => {
Expression::TemplateLiteral(tmpl) => {
tmpl.quasis.first().unwrap().value.raw.parse().or(Err(())) tmpl.quasis.first().unwrap().value.raw.parse().or(Err(()))
} }
Expression::NumericLiteral(num) => Ok(num.value), JSXExpression::Expression(Expression::NumericLiteral(num)) => Ok(num.value),
_ => Err(()), _ => Err(()),
}, },
_ => Err(()), _ => Err(()),

View file

@ -144,7 +144,7 @@ impl<'a> ParserImpl<'a> {
.map(JSXElementName::MemberExpression); .map(JSXElementName::MemberExpression);
} }
Ok(JSXElementName::Identifier(identifier)) Ok(JSXElementName::Identifier(self.ast.alloc(identifier)))
} }
/// `JSXMemberExpression` : /// `JSXMemberExpression` :
@ -156,7 +156,7 @@ impl<'a> ParserImpl<'a> {
object: JSXIdentifier<'a>, object: JSXIdentifier<'a>,
) -> Result<Box<'a, JSXMemberExpression<'a>>> { ) -> Result<Box<'a, JSXMemberExpression<'a>>> {
let mut span = span; let mut span = span;
let mut object = JSXMemberExpressionObject::Identifier(object); let mut object = JSXMemberExpressionObject::Identifier(self.ast.alloc(object));
let mut property = None; let mut property = None;
while self.eat(Kind::Dot) && !self.at(Kind::Eof) { while self.eat(Kind::Dot) && !self.at(Kind::Eof) {
@ -214,10 +214,11 @@ impl<'a> ParserImpl<'a> {
self.parse_jsx_spread_child().map(JSXChild::Spread).map(Some) self.parse_jsx_spread_child().map(JSXChild::Spread).map(Some)
} }
// {expr} // {expr}
Kind::LCurly => self Kind::LCurly => {
.parse_jsx_expression_container(/* is_jsx_child */ true) self.parse_jsx_expression_container(/* is_jsx_child */ true)
.map(JSXChild::ExpressionContainer) .map(JSXChild::ExpressionContainer)
.map(Some), .map(Some)
}
// text // text
Kind::JSXText => Ok(Some(JSXChild::Text(self.parse_jsx_text()))), Kind::JSXText => Ok(Some(JSXChild::Text(self.parse_jsx_text()))),
_ => Err(self.unexpected()), _ => Err(self.unexpected()),
@ -228,7 +229,7 @@ impl<'a> ParserImpl<'a> {
fn parse_jsx_expression_container( fn parse_jsx_expression_container(
&mut self, &mut self,
in_jsx_child: bool, in_jsx_child: bool,
) -> Result<JSXExpressionContainer<'a>> { ) -> Result<Box<'a, JSXExpressionContainer<'a>>> {
let span = self.start_span(); let span = self.start_span();
self.bump_any(); // bump `{` self.bump_any(); // bump `{`
@ -269,7 +270,7 @@ impl<'a> ParserImpl<'a> {
/// `JSXChildExpression` : /// `JSXChildExpression` :
/// { ... `AssignmentExpression` } /// { ... `AssignmentExpression` }
fn parse_jsx_spread_child(&mut self) -> Result<JSXSpreadChild<'a>> { fn parse_jsx_spread_child(&mut self) -> Result<Box<'a, JSXSpreadChild<'a>>> {
let span = self.start_span(); let span = self.start_span();
self.bump_any(); // bump `{` self.bump_any(); // bump `{`
self.expect(Kind::Dot3)?; self.expect(Kind::Dot3)?;
@ -336,12 +337,14 @@ impl<'a> ParserImpl<'a> {
))); )));
} }
Ok(JSXAttributeName::Identifier(identifier)) Ok(JSXAttributeName::Identifier(self.ast.alloc(identifier)))
} }
fn parse_jsx_attribute_value(&mut self) -> Result<JSXAttributeValue<'a>> { fn parse_jsx_attribute_value(&mut self) -> Result<JSXAttributeValue<'a>> {
match self.cur_kind() { match self.cur_kind() {
Kind::Str => self.parse_literal_string().map(JSXAttributeValue::StringLiteral), Kind::Str => self
.parse_literal_string()
.map(|str_lit| JSXAttributeValue::StringLiteral(self.ast.alloc(str_lit))),
Kind::LCurly => { Kind::LCurly => {
let expr = self.parse_jsx_expression_container(/* is_jsx_child */ false)?; let expr = self.parse_jsx_expression_container(/* is_jsx_child */ false)?;
Ok(JSXAttributeValue::ExpressionContainer(expr)) Ok(JSXAttributeValue::ExpressionContainer(expr))
@ -374,7 +377,7 @@ impl<'a> ParserImpl<'a> {
Ok(self.ast.jsx_identifier(span, name.into())) Ok(self.ast.jsx_identifier(span, name.into()))
} }
fn parse_jsx_text(&mut self) -> JSXText<'a> { fn parse_jsx_text(&mut self) -> Box<'a, JSXText<'a>> {
let span = self.start_span(); let span = self.start_span();
let value = Atom::from(self.cur_string()); let value = Atom::from(self.cur_string());
self.bump_any(); self.bump_any();

View file

@ -66,7 +66,8 @@ impl<'a> ReactJsxSelf<'a> {
} }
} }
let name = JSXAttributeName::Identifier(JSXIdentifier::new(SPAN, SELF.into())); let name =
JSXAttributeName::Identifier(self.ctx.ast.alloc(JSXIdentifier::new(SPAN, SELF.into())));
let value = { let value = {
let jsx_expr = JSXExpression::Expression(self.ctx.ast.this_expression(SPAN)); let jsx_expr = JSXExpression::Expression(self.ctx.ast.this_expression(SPAN));
let container = self.ctx.ast.jsx_expression_container(SPAN, jsx_expr); let container = self.ctx.ast.jsx_expression_container(SPAN, jsx_expr);

View file

@ -80,7 +80,9 @@ impl<'a> ReactJsxSource<'a> {
self.should_add_jsx_file_name_variable = true; self.should_add_jsx_file_name_variable = true;
let key = JSXAttributeName::Identifier(self.ctx.ast.jsx_identifier(SPAN, SOURCE.into())); let key = JSXAttributeName::Identifier(
self.ctx.ast.alloc(self.ctx.ast.jsx_identifier(SPAN, SOURCE.into())),
);
let object = self.get_source_object(); let object = self.get_source_object();
let expr = self.ctx.ast.jsx_expression_container(SPAN, JSXExpression::Expression(object)); let expr = self.ctx.ast.jsx_expression_container(SPAN, JSXExpression::Expression(object));
let value = JSXAttributeValue::ExpressionContainer(expr); let value = JSXAttributeValue::ExpressionContainer(expr);