diff --git a/crates/oxc_ast/src/ast/jsx.rs b/crates/oxc_ast/src/ast/jsx.rs index b2e8b514daba1..3f450926c39ad 100644 --- a/crates/oxc_ast/src/ast/jsx.rs +++ b/crates/oxc_ast/src/ast/jsx.rs @@ -111,12 +111,6 @@ pub struct JSXNamespacedName<'a> { pub property: JSXIdentifier<'a>, } -impl<'a> std::fmt::Display for JSXNamespacedName<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}:{}", self.namespace.name, self.property.name) - } -} - /// JSX Member Expression #[visited_node] #[derive(Debug, Hash)] @@ -129,36 +123,6 @@ pub struct JSXMemberExpression<'a> { pub property: JSXIdentifier<'a>, } -impl<'a> JSXMemberExpression<'a> { - pub fn get_object_identifier(&self) -> &JSXIdentifier { - let mut member_expr = self; - loop { - match &member_expr.object { - JSXMemberExpressionObject::Identifier(ident) => { - break ident; - } - JSXMemberExpressionObject::MemberExpression(expr) => { - member_expr = expr; - } - } - } - } - - pub fn get_object_identifier_mut(&mut self) -> &mut JSXIdentifier<'a> { - let mut member_expr = self; - loop { - match &mut member_expr.object { - JSXMemberExpressionObject::Identifier(ident) => { - break &mut *ident; - } - JSXMemberExpressionObject::MemberExpression(expr) => { - member_expr = expr; - } - } - } - } -} - #[visited_node] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -196,13 +160,6 @@ pub enum JSXExpression<'a> { } } -impl<'a> JSXExpression<'a> { - /// Determines whether the given expr is a `undefined` literal - pub fn is_undefined(&self) -> bool { - matches!(self, Self::Identifier(ident) if ident.name == "undefined") - } -} - #[visited_node] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -236,16 +193,6 @@ pub struct JSXAttribute<'a> { pub value: Option>, } -impl<'a> JSXAttribute<'a> { - pub fn is_identifier(&self, name: &str) -> bool { - matches!(&self.name, JSXAttributeName::Identifier(ident) if ident.name == name) - } - - pub fn is_key(&self) -> bool { - self.is_identifier("key") - } -} - /// JSX Spread Attribute #[visited_node] #[derive(Debug, Hash)] @@ -289,12 +236,6 @@ pub struct JSXIdentifier<'a> { pub name: Atom<'a>, } -impl<'a> JSXIdentifier<'a> { - pub fn new(span: Span, name: Atom<'a>) -> Self { - Self { span, name } - } -} - // 1.4 JSX Children /// JSX Child diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index 2c02efb7578a2..b0be99d6b91e6 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -13,7 +13,7 @@ use std::{cell::Cell, hash::Hash}; use oxc_allocator::{Box, Vec}; use oxc_ast_macros::visited_node; -use oxc_span::{Atom, GetSpan, Span}; +use oxc_span::{Atom, Span}; use oxc_syntax::scope::ScopeId; #[cfg(feature = "serialize")] use serde::Serialize; @@ -60,25 +60,6 @@ pub struct TSEnumDeclaration<'a> { pub scope_id: Cell>, } -impl<'a> TSEnumDeclaration<'a> { - pub fn new( - span: Span, - id: BindingIdentifier<'a>, - members: Vec<'a, TSEnumMember<'a>>, - modifiers: Modifiers<'a>, - ) -> Self { - Self { span, id, members, modifiers, scope_id: Cell::default() } - } -} - -impl<'a> Hash for TSEnumDeclaration<'a> { - fn hash(&self, state: &mut H) { - self.id.hash(state); - self.members.hash(state); - self.modifiers.hash(state); - } -} - #[visited_node] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -238,49 +219,6 @@ macro_rules! match_ts_type { } pub use match_ts_type; -impl<'a> TSType<'a> { - pub fn get_identifier_reference(&self) -> Option> { - match self { - TSType::TSTypeReference(reference) => { - Some(TSTypeName::get_first_name(&reference.type_name)) - } - TSType::TSQualifiedName(qualified) => Some(TSTypeName::get_first_name(&qualified.left)), - TSType::TSTypeQuery(query) => match &query.expr_name { - TSTypeQueryExprName::IdentifierReference(ident) => Some((*ident).clone()), - _ => None, - }, - _ => None, - } - } - - pub fn is_const_type_reference(&self) -> bool { - matches!(self, TSType::TSTypeReference(reference) if reference.type_name.is_const()) - } - - /// Check if type maybe `undefined` - pub fn is_maybe_undefined(&self) -> bool { - match self { - TSType::TSUndefinedKeyword(_) => true, - TSType::TSUnionType(un) => un.types.iter().any(Self::is_maybe_undefined), - _ => false, - } - } - - #[rustfmt::skip] - pub fn is_keyword(&self) -> bool { - matches!(self, TSType::TSAnyKeyword(_) | TSType::TSBigIntKeyword(_) | TSType::TSBooleanKeyword(_) - | TSType::TSNeverKeyword(_) | TSType::TSNullKeyword(_) | TSType::TSNumberKeyword(_) - | TSType::TSObjectKeyword(_) | TSType::TSStringKeyword(_)| TSType::TSVoidKeyword(_) - | TSType::TSIntrinsicKeyword(_) | TSType::TSSymbolKeyword(_) | TSType::TSThisType(_) - | TSType::TSUndefinedKeyword(_) | TSType::TSUnknownKeyword(_) - ) - } - - pub fn is_keyword_or_literal(&self) -> bool { - self.is_keyword() || matches!(self, TSType::TSLiteralType(_)) - } -} - /// `SomeType extends OtherType ? TrueType : FalseType;` /// /// @@ -602,41 +540,6 @@ macro_rules! match_ts_type_name { } pub use match_ts_type_name; -impl<'a> TSTypeName<'a> { - pub fn get_first_name(name: &TSTypeName<'a>) -> IdentifierReference<'a> { - match name { - TSTypeName::IdentifierReference(name) => (*name).clone(), - TSTypeName::QualifiedName(name) => TSTypeName::get_first_name(&name.left), - } - } - - pub fn is_const(&self) -> bool { - if let TSTypeName::IdentifierReference(ident) = self { - if ident.name == "const" { - return true; - } - } - false - } - - pub fn is_identifier(&self) -> bool { - matches!(self, Self::IdentifierReference(_)) - } - - pub fn is_qualified_name(&self) -> bool { - matches!(self, Self::QualifiedName(_)) - } -} - -impl GetSpan for TSTypeName<'_> { - fn span(&self) -> Span { - match self { - TSTypeName::IdentifierReference(ident) => ident.span, - TSTypeName::QualifiedName(name) => name.span, - } - } -} - #[visited_node] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -674,31 +577,6 @@ pub struct TSTypeParameter<'a> { pub scope_id: Cell>, } -impl<'a> TSTypeParameter<'a> { - pub fn new( - span: Span, - name: BindingIdentifier<'a>, - constraint: Option>, - default: Option>, - r#in: bool, - out: bool, - r#const: bool, - ) -> Self { - Self { span, name, constraint, default, r#in, out, r#const, scope_id: Cell::default() } - } -} - -impl<'a> Hash for TSTypeParameter<'a> { - fn hash(&self, state: &mut H) { - self.name.hash(state); - self.constraint.hash(state); - self.default.hash(state); - self.r#in.hash(state); - self.out.hash(state); - self.r#const.hash(state); - } -} - #[visited_node] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -732,12 +610,6 @@ pub enum TSAccessibility { Public, } -impl TSAccessibility { - pub fn is_private(&self) -> bool { - matches!(self, TSAccessibility::Private) - } -} - #[visited_node] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -936,27 +808,6 @@ pub struct TSModuleDeclaration<'a> { pub scope_id: Cell>, } -impl<'a> TSModuleDeclaration<'a> { - pub fn new( - span: Span, - id: TSModuleDeclarationName<'a>, - body: Option>, - kind: TSModuleDeclarationKind, - modifiers: Modifiers<'a>, - ) -> Self { - Self { span, id, body, kind, modifiers, scope_id: Cell::default() } - } -} - -impl<'a> Hash for TSModuleDeclaration<'a> { - fn hash(&self, state: &mut H) { - self.id.hash(state); - self.body.hash(state); - self.kind.hash(state); - self.modifiers.hash(state); - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(rename_all = "lowercase"))] @@ -975,19 +826,6 @@ pub enum TSModuleDeclarationName<'a> { StringLiteral(StringLiteral<'a>), } -impl<'a> TSModuleDeclarationName<'a> { - pub fn is_string_literal(&self) -> bool { - matches!(self, Self::StringLiteral(_)) - } - - pub fn name(&self) -> Atom<'a> { - match self { - Self::Identifier(ident) => ident.name.clone(), - Self::StringLiteral(lit) => lit.value.clone(), - } - } -} - #[visited_node] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -1256,29 +1094,6 @@ pub struct Decorator<'a> { pub expression: Expression<'a>, } -impl<'a> Decorator<'a> { - /// Get the name of the decorator - /// ```ts - /// @decorator - /// @decorator.a.b - /// @decorator(xx) - /// @decorator.a.b(xx) - /// The name of the decorator is `decorator` - /// ``` - pub fn name(&self) -> Option<&str> { - match &self.expression { - Expression::Identifier(ident) => Some(&ident.name), - expr @ match_member_expression!(Expression) => { - expr.to_member_expression().static_property_name() - } - Expression::CallExpression(call) => { - call.callee.get_member_expr().map(|member| member.static_property_name())? - } - _ => None, - } - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(rename_all = "camelCase"))] @@ -1300,12 +1115,6 @@ pub enum ModifierKind { Override, } -impl ModifierKind { - pub fn is_typescript_syntax(&self) -> bool { - !matches!(self, Self::Async | Self::Default | Self::Export | Self::Static) - } -} - #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))] @@ -1318,63 +1127,7 @@ pub struct Modifier { #[derive(Debug, Default, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(transparent))] -pub struct Modifiers<'a>(Option>); - -impl<'a> Modifiers<'a> { - pub fn new(modifiers: Vec<'a, Modifier>) -> Self { - Self(Some(modifiers)) - } - - pub fn empty() -> Self { - Self(None) - } - - pub fn is_none(&self) -> bool { - self.0.is_none() - } - - pub fn contains(&self, target: ModifierKind) -> bool { - self.0 - .as_ref() - .map_or(false, |modifiers| modifiers.iter().any(|modifier| modifier.kind == target)) - } - - pub fn iter(&self) -> impl Iterator + '_ { - self.0.as_ref().into_iter().flat_map(|modifiers| modifiers.iter()) - } - - /// Find a modifier by kind - pub fn find(&self, kind: ModifierKind) -> Option<&Modifier> { - self.find_where(|modifier| modifier.kind == kind) - } - - pub fn find_where(&self, f: F) -> Option<&Modifier> - where - F: Fn(&Modifier) -> bool, - { - self.0.as_ref().and_then(|modifiers| modifiers.iter().find(|modifier| f(modifier))) - } - - pub fn is_contains_declare(&self) -> bool { - self.contains(ModifierKind::Declare) - } - - pub fn is_contains_abstract(&self) -> bool { - self.contains(ModifierKind::Abstract) - } - - pub fn remove_type_modifiers(&mut self) { - if let Some(list) = &mut self.0 { - list.retain(|m| !m.kind.is_typescript_syntax()); - } - } - - pub fn add_modifier(&mut self, modifier: Modifier) { - if let Some(list) = self.0.as_mut() { - list.push(modifier); - } - } -} +pub struct Modifiers<'a>(pub(crate) Option>); /// Export Assignment in non-module files /// @@ -1421,16 +1174,6 @@ pub enum ImportOrExportKind { Type, } -impl ImportOrExportKind { - pub fn is_value(&self) -> bool { - matches!(self, Self::Value) - } - - pub fn is_type(&self) -> bool { - matches!(self, Self::Type) - } -} - // [`JSDoc`](https://github.com/microsoft/TypeScript/blob/54a554d8af2657630307cbfa8a3e4f3946e36507/src/compiler/types.ts#L393) /// `type foo = ty?` or `type foo = ?ty` diff --git a/crates/oxc_ast/src/ast_impl/jsx.rs b/crates/oxc_ast/src/ast_impl/jsx.rs new file mode 100644 index 0000000000000..42624cb0a9006 --- /dev/null +++ b/crates/oxc_ast/src/ast_impl/jsx.rs @@ -0,0 +1,65 @@ +//! [JSX](https://facebook.github.io/jsx) + +use crate::ast::*; +use oxc_span::{Atom, Span}; + +// 1.2 JSX Elements + +impl<'a> std::fmt::Display for JSXNamespacedName<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}:{}", self.namespace.name, self.property.name) + } +} + +impl<'a> JSXMemberExpression<'a> { + pub fn get_object_identifier(&self) -> &JSXIdentifier { + let mut member_expr = self; + loop { + match &member_expr.object { + JSXMemberExpressionObject::Identifier(ident) => { + break ident; + } + JSXMemberExpressionObject::MemberExpression(expr) => { + member_expr = expr; + } + } + } + } + + pub fn get_object_identifier_mut(&mut self) -> &mut JSXIdentifier<'a> { + let mut member_expr = self; + loop { + match &mut member_expr.object { + JSXMemberExpressionObject::Identifier(ident) => { + break &mut *ident; + } + JSXMemberExpressionObject::MemberExpression(expr) => { + member_expr = expr; + } + } + } + } +} + +impl<'a> JSXExpression<'a> { + /// Determines whether the given expr is a `undefined` literal + pub fn is_undefined(&self) -> bool { + matches!(self, Self::Identifier(ident) if ident.name == "undefined") + } +} + +impl<'a> JSXAttribute<'a> { + pub fn is_identifier(&self, name: &str) -> bool { + matches!(&self.name, JSXAttributeName::Identifier(ident) if ident.name == name) + } + + pub fn is_key(&self) -> bool { + self.is_identifier("key") + } +} + +impl<'a> JSXIdentifier<'a> { + pub fn new(span: Span, name: Atom<'a>) -> Self { + Self { span, name } + } +} diff --git a/crates/oxc_ast/src/ast_impl/mod.rs b/crates/oxc_ast/src/ast_impl/mod.rs index 0008512a63a27..85d8b974b984c 100644 --- a/crates/oxc_ast/src/ast_impl/mod.rs +++ b/crates/oxc_ast/src/ast_impl/mod.rs @@ -1,2 +1,4 @@ mod js; +mod jsx; mod literal; +mod ts; diff --git a/crates/oxc_ast/src/ast_impl/ts.rs b/crates/oxc_ast/src/ast_impl/ts.rs new file mode 100644 index 0000000000000..3425a9fb97a87 --- /dev/null +++ b/crates/oxc_ast/src/ast_impl/ts.rs @@ -0,0 +1,271 @@ +//! TypeScript Definitions +//! +//! [AST Spec](https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/ast-spec) +//! [Archived TypeScript spec](https://github.com/microsoft/TypeScript/blob/3c99d50da5a579d9fa92d02664b1b66d4ff55944/doc/spec-ARCHIVED.md) + +// NB: `#[visited_node]` attribute on AST nodes does not do anything to the code in this file. +// It is purely a marker for codegen used in `oxc_traverse`. See docs in that crate. + +use crate::ast::*; + +use std::{cell::Cell, hash::Hash}; + +use oxc_allocator::Vec; +use oxc_span::{Atom, GetSpan, Span}; + +impl<'a> TSEnumDeclaration<'a> { + pub fn new( + span: Span, + id: BindingIdentifier<'a>, + members: Vec<'a, TSEnumMember<'a>>, + modifiers: Modifiers<'a>, + ) -> Self { + Self { span, id, members, modifiers, scope_id: Cell::default() } + } +} + +impl<'a> Hash for TSEnumDeclaration<'a> { + fn hash(&self, state: &mut H) { + self.id.hash(state); + self.members.hash(state); + self.modifiers.hash(state); + } +} + +impl<'a> TSType<'a> { + pub fn get_identifier_reference(&self) -> Option> { + match self { + TSType::TSTypeReference(reference) => { + Some(TSTypeName::get_first_name(&reference.type_name)) + } + TSType::TSQualifiedName(qualified) => Some(TSTypeName::get_first_name(&qualified.left)), + TSType::TSTypeQuery(query) => match &query.expr_name { + TSTypeQueryExprName::IdentifierReference(ident) => Some((*ident).clone()), + _ => None, + }, + _ => None, + } + } + + pub fn is_const_type_reference(&self) -> bool { + matches!(self, TSType::TSTypeReference(reference) if reference.type_name.is_const()) + } + + /// Check if type maybe `undefined` + pub fn is_maybe_undefined(&self) -> bool { + match self { + TSType::TSUndefinedKeyword(_) => true, + TSType::TSUnionType(un) => un.types.iter().any(Self::is_maybe_undefined), + _ => false, + } + } + + #[rustfmt::skip] + pub fn is_keyword(&self) -> bool { + matches!(self, TSType::TSAnyKeyword(_) | TSType::TSBigIntKeyword(_) | TSType::TSBooleanKeyword(_) + | TSType::TSNeverKeyword(_) | TSType::TSNullKeyword(_) | TSType::TSNumberKeyword(_) + | TSType::TSObjectKeyword(_) | TSType::TSStringKeyword(_)| TSType::TSVoidKeyword(_) + | TSType::TSIntrinsicKeyword(_) | TSType::TSSymbolKeyword(_) | TSType::TSThisType(_) + | TSType::TSUndefinedKeyword(_) | TSType::TSUnknownKeyword(_) + ) + } + + pub fn is_keyword_or_literal(&self) -> bool { + self.is_keyword() || matches!(self, TSType::TSLiteralType(_)) + } +} + +impl<'a> TSTypeName<'a> { + pub fn get_first_name(name: &TSTypeName<'a>) -> IdentifierReference<'a> { + match name { + TSTypeName::IdentifierReference(name) => (*name).clone(), + TSTypeName::QualifiedName(name) => TSTypeName::get_first_name(&name.left), + } + } + + pub fn is_const(&self) -> bool { + if let TSTypeName::IdentifierReference(ident) = self { + if ident.name == "const" { + return true; + } + } + false + } + + pub fn is_identifier(&self) -> bool { + matches!(self, Self::IdentifierReference(_)) + } + + pub fn is_qualified_name(&self) -> bool { + matches!(self, Self::QualifiedName(_)) + } +} + +impl GetSpan for TSTypeName<'_> { + fn span(&self) -> Span { + match self { + TSTypeName::IdentifierReference(ident) => ident.span, + TSTypeName::QualifiedName(name) => name.span, + } + } +} + +impl<'a> TSTypeParameter<'a> { + pub fn new( + span: Span, + name: BindingIdentifier<'a>, + constraint: Option>, + default: Option>, + r#in: bool, + out: bool, + r#const: bool, + ) -> Self { + Self { span, name, constraint, default, r#in, out, r#const, scope_id: Cell::default() } + } +} + +impl<'a> Hash for TSTypeParameter<'a> { + fn hash(&self, state: &mut H) { + self.name.hash(state); + self.constraint.hash(state); + self.default.hash(state); + self.r#in.hash(state); + self.out.hash(state); + self.r#const.hash(state); + } +} + +impl TSAccessibility { + pub fn is_private(&self) -> bool { + matches!(self, TSAccessibility::Private) + } +} + +impl<'a> TSModuleDeclaration<'a> { + pub fn new( + span: Span, + id: TSModuleDeclarationName<'a>, + body: Option>, + kind: TSModuleDeclarationKind, + modifiers: Modifiers<'a>, + ) -> Self { + Self { span, id, body, kind, modifiers, scope_id: Cell::default() } + } +} + +impl<'a> Hash for TSModuleDeclaration<'a> { + fn hash(&self, state: &mut H) { + self.id.hash(state); + self.body.hash(state); + self.kind.hash(state); + self.modifiers.hash(state); + } +} + +impl<'a> TSModuleDeclarationName<'a> { + pub fn is_string_literal(&self) -> bool { + matches!(self, Self::StringLiteral(_)) + } + + pub fn name(&self) -> Atom<'a> { + match self { + Self::Identifier(ident) => ident.name.clone(), + Self::StringLiteral(lit) => lit.value.clone(), + } + } +} + +impl<'a> Decorator<'a> { + /// Get the name of the decorator + /// ```ts + /// @decorator + /// @decorator.a.b + /// @decorator(xx) + /// @decorator.a.b(xx) + /// The name of the decorator is `decorator` + /// ``` + pub fn name(&self) -> Option<&str> { + match &self.expression { + Expression::Identifier(ident) => Some(&ident.name), + expr @ match_member_expression!(Expression) => { + expr.to_member_expression().static_property_name() + } + Expression::CallExpression(call) => { + call.callee.get_member_expr().map(|member| member.static_property_name())? + } + _ => None, + } + } +} + +impl ModifierKind { + pub fn is_typescript_syntax(&self) -> bool { + !matches!(self, Self::Async | Self::Default | Self::Export | Self::Static) + } +} + +impl<'a> Modifiers<'a> { + pub fn new(modifiers: Vec<'a, Modifier>) -> Self { + Self(Some(modifiers)) + } + + pub fn empty() -> Self { + Self(None) + } + + pub fn is_none(&self) -> bool { + self.0.is_none() + } + + pub fn contains(&self, target: ModifierKind) -> bool { + self.0 + .as_ref() + .map_or(false, |modifiers| modifiers.iter().any(|modifier| modifier.kind == target)) + } + + pub fn iter(&self) -> impl Iterator + '_ { + self.0.as_ref().into_iter().flat_map(|modifiers| modifiers.iter()) + } + + /// Find a modifier by kind + pub fn find(&self, kind: ModifierKind) -> Option<&Modifier> { + self.find_where(|modifier| modifier.kind == kind) + } + + pub fn find_where(&self, f: F) -> Option<&Modifier> + where + F: Fn(&Modifier) -> bool, + { + self.0.as_ref().and_then(|modifiers| modifiers.iter().find(|modifier| f(modifier))) + } + + pub fn is_contains_declare(&self) -> bool { + self.contains(ModifierKind::Declare) + } + + pub fn is_contains_abstract(&self) -> bool { + self.contains(ModifierKind::Abstract) + } + + pub fn remove_type_modifiers(&mut self) { + if let Some(list) = &mut self.0 { + list.retain(|m| !m.kind.is_typescript_syntax()); + } + } + + pub fn add_modifier(&mut self, modifier: Modifier) { + if let Some(list) = self.0.as_mut() { + list.push(modifier); + } + } +} + +impl ImportOrExportKind { + pub fn is_value(&self) -> bool { + matches!(self, Self::Value) + } + + pub fn is_type(&self) -> bool { + matches!(self, Self::Type) + } +}