diff --git a/crates/oxc_ast/src/ast/mod.rs b/crates/oxc_ast/src/ast/mod.rs index 2c9b1fb49b9137..54c88b0b6258a1 100644 --- a/crates/oxc_ast/src/ast/mod.rs +++ b/crates/oxc_ast/src/ast/mod.rs @@ -183,132 +183,3 @@ mod ts; use macros::inherit_variants; pub use self::{js::*, jsx::*, literal::*, ts::*}; - -#[cfg(feature = "serialize")] -use serde::Serialize; -#[cfg(feature = "serialize")] -use tsify::Tsify; - -use oxc_allocator::Vec; -use oxc_span::Span; - -#[derive(Debug, Hash)] -#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] -#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))] -pub struct Modifier { - #[cfg_attr(feature = "serialize", serde(flatten))] - pub span: Span, - pub kind: ModifierKind, -} - -#[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_const(&self) -> bool { - self.contains(ModifierKind::Const) - } - - 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); - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] -#[cfg_attr(feature = "serialize", serde(rename_all = "camelCase"))] -pub enum ModifierKind { - Abstract, - Accessor, - Async, - Const, - Declare, - Default, - Export, - In, - Public, - Private, - Protected, - Readonly, - Static, - Out, - Override, -} - -impl ModifierKind { - pub fn is_typescript_syntax(&self) -> bool { - !matches!(self, Self::Async | Self::Default | Self::Export | Self::Static) - } - - pub fn as_str(self) -> &'static str { - match self { - Self::Abstract => "abstract", - Self::Accessor => "accessor", - Self::Async => "async", - Self::Const => "const", - Self::Declare => "declare", - Self::Default => "default", - Self::Export => "export", - Self::In => "in", - Self::Public => "public", - Self::Private => "private", - Self::Protected => "protected", - Self::Readonly => "readonly", - Self::Static => "static", - Self::Out => "out", - Self::Override => "override", - } - } -} diff --git a/crates/oxc_parser/src/js/class.rs b/crates/oxc_parser/src/js/class.rs index fbb534fa3ba24a..71b0735a98a281 100644 --- a/crates/oxc_parser/src/js/class.rs +++ b/crates/oxc_parser/src/js/class.rs @@ -4,7 +4,13 @@ use oxc_diagnostics::Result; use oxc_span::{GetSpan, Span}; use super::list::ClassElements; -use crate::{diagnostics, lexer::Kind, list::NormalList, Context, ParserImpl, StatementContext}; +use crate::{ + diagnostics, + lexer::Kind, + list::NormalList, + modifiers::{ModifierKind, Modifiers}, + Context, ParserImpl, StatementContext, +}; type Extends<'a> = Vec<'a, (Expression<'a>, Option>>, Span)>; diff --git a/crates/oxc_parser/src/js/declaration.rs b/crates/oxc_parser/src/js/declaration.rs index c286cb3ec10806..beb87237014218 100644 --- a/crates/oxc_parser/src/js/declaration.rs +++ b/crates/oxc_parser/src/js/declaration.rs @@ -4,7 +4,12 @@ use oxc_diagnostics::Result; use oxc_span::{GetSpan, Span}; use super::{VariableDeclarationContext, VariableDeclarationParent}; -use crate::{diagnostics, lexer::Kind, ParserImpl, StatementContext}; +use crate::{ + diagnostics, + lexer::Kind, + modifiers::{ModifierKind, Modifiers}, + ParserImpl, StatementContext, +}; impl<'a> ParserImpl<'a> { pub(crate) fn parse_let(&mut self, stmt_ctx: StatementContext) -> Result> { diff --git a/crates/oxc_parser/src/js/function.rs b/crates/oxc_parser/src/js/function.rs index d2ec4e684f7be2..0417dd8ac8f6f7 100644 --- a/crates/oxc_parser/src/js/function.rs +++ b/crates/oxc_parser/src/js/function.rs @@ -6,7 +6,13 @@ use oxc_diagnostics::Result; use oxc_span::Span; use super::{list::FormalParameterList, FunctionKind}; -use crate::{diagnostics, lexer::Kind, list::SeparatedList, Context, ParserImpl, StatementContext}; +use crate::{ + diagnostics, + lexer::Kind, + list::SeparatedList, + modifiers::{ModifierKind, Modifiers}, + Context, ParserImpl, StatementContext, +}; impl FunctionKind { pub(crate) fn is_id_required(self) -> bool { diff --git a/crates/oxc_parser/src/js/module.rs b/crates/oxc_parser/src/js/module.rs index 949e4d4ab3f515..0ca9d89bada0ae 100644 --- a/crates/oxc_parser/src/js/module.rs +++ b/crates/oxc_parser/src/js/module.rs @@ -7,7 +7,9 @@ use super::{ list::{AssertEntries, ExportNamedSpecifiers, ImportSpecifierList}, FunctionKind, }; -use crate::{diagnostics, lexer::Kind, list::SeparatedList, Context, ParserImpl}; +use crate::{ + diagnostics, lexer::Kind, list::SeparatedList, modifiers::Modifiers, Context, ParserImpl, +}; impl<'a> ParserImpl<'a> { /// [Import Call](https://tc39.es/ecma262/#sec-import-calls) diff --git a/crates/oxc_parser/src/js/statement.rs b/crates/oxc_parser/src/js/statement.rs index 109b1d118d5155..2e6a73b0257ba0 100644 --- a/crates/oxc_parser/src/js/statement.rs +++ b/crates/oxc_parser/src/js/statement.rs @@ -6,7 +6,10 @@ use oxc_span::{Atom, GetSpan, Span}; use super::{ grammar::CoverGrammar, list::SwitchCases, VariableDeclarationContext, VariableDeclarationParent, }; -use crate::{diagnostics, lexer::Kind, list::NormalList, Context, ParserImpl, StatementContext}; +use crate::{ + diagnostics, lexer::Kind, list::NormalList, modifiers::Modifiers, Context, ParserImpl, + StatementContext, +}; impl<'a> ParserImpl<'a> { // Section 12 diff --git a/crates/oxc_parser/src/lib.rs b/crates/oxc_parser/src/lib.rs index e432adbff1f84c..ee959c463b218b 100644 --- a/crates/oxc_parser/src/lib.rs +++ b/crates/oxc_parser/src/lib.rs @@ -64,6 +64,7 @@ mod context; mod cursor; mod list; +mod modifiers; mod state; mod js; @@ -313,19 +314,6 @@ impl<'a> ParserImpl<'a> { } } - /// Backdoor to create a `ParserImpl` without holding a `UniquePromise`, for unit tests. - /// This function must NOT be exposed in public API as it breaks safety invariants. - #[cfg(test)] - fn new_for_tests( - allocator: &'a Allocator, - source_text: &'a str, - source_type: SourceType, - options: ParserOptions, - ) -> Self { - let unique = UniquePromise::new_for_tests(); - Self::new(allocator, source_text, source_type, options, unique) - } - /// Main entry point /// /// Returns an empty `Program` on unrecoverable error, diff --git a/crates/oxc_parser/src/modifiers.rs b/crates/oxc_parser/src/modifiers.rs new file mode 100644 index 00000000000000..24f9ec6c044c8c --- /dev/null +++ b/crates/oxc_parser/src/modifiers.rs @@ -0,0 +1,283 @@ +use bitflags::bitflags; + +use oxc_allocator::Vec; +use oxc_ast::ast::TSAccessibility; +use oxc_span::Span; + +use crate::{lexer::Kind, ParserImpl}; + +bitflags! { + /// Bitflag of modifiers and contextual modifiers. + /// Useful to cheaply track all already seen modifiers of a member (instead of using a HashSet). + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct ModifierFlags: u16 { + const DECLARE = 1 << 0; + const PRIVATE = 1 << 1; + const PROTECTED = 1 << 2; + const PUBLIC = 1 << 3; + const STATIC = 1 << 4; + const READONLY = 1 << 5; + const ABSTRACT = 1 << 6; + const OVERRIDE = 1 << 7; + const ASYNC = 1 << 8; + const CONST = 1 << 9; + const IN = 1 << 10; + const OUT = 1 << 11; + const EXPORT = 1 << 12; + const DEFAULT = 1 << 13; + const ACCESSOR = 1 << 14; + const ACCESSIBILITY = Self::PRIVATE.bits() | Self::PROTECTED.bits() | Self::PUBLIC.bits(); + } +} + +/// It is the caller's safety to always check by `Kind::is_modifier_kind` +/// before converting [`Kind`] to [`ModifierFlags`] so that we can assume here that +/// the conversion always succeeds. +impl From for ModifierFlags { + fn from(value: Kind) -> Self { + match value { + Kind::Abstract => Self::ABSTRACT, + Kind::Declare => Self::DECLARE, + Kind::Private => Self::PRIVATE, + Kind::Protected => Self::PROTECTED, + Kind::Public => Self::PUBLIC, + Kind::Static => Self::STATIC, + Kind::Readonly => Self::READONLY, + Kind::Override => Self::OVERRIDE, + Kind::Async => Self::ASYNC, + Kind::Const => Self::CONST, + Kind::In => Self::IN, + Kind::Out => Self::OUT, + Kind::Export => Self::EXPORT, + Kind::Default => Self::DEFAULT, + Kind::Accessor => Self::ACCESSOR, + _ => unreachable!(), + } + } +} + +impl ModifierFlags { + pub(crate) fn accessibility(self) -> Option { + if self.contains(Self::PUBLIC) { + return Some(TSAccessibility::Public); + } + if self.contains(Self::PROTECTED) { + return Some(TSAccessibility::Protected); + } + + if self.contains(Self::PRIVATE) { + return Some(TSAccessibility::Private); + } + None + } + + pub(crate) fn readonly(self) -> bool { + self.contains(Self::READONLY) + } + + pub(crate) fn declare(self) -> bool { + self.contains(Self::DECLARE) + } + + pub(crate) fn r#async(self) -> bool { + self.contains(Self::ASYNC) + } + + pub(crate) fn r#override(self) -> bool { + self.contains(Self::OVERRIDE) + } + + pub(crate) fn r#abstract(self) -> bool { + self.contains(Self::ABSTRACT) + } + + pub(crate) fn r#static(self) -> bool { + self.contains(Self::STATIC) + } +} + +#[derive(Debug, Hash)] +pub struct Modifier { + pub span: Span, + pub kind: ModifierKind, +} + +#[derive(Debug, Default, Hash)] +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 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()) + } + + pub fn is_contains_const(&self) -> bool { + self.contains(ModifierKind::Const) + } + + pub fn is_contains_declare(&self) -> bool { + self.contains(ModifierKind::Declare) + } + + pub fn is_contains_abstract(&self) -> bool { + self.contains(ModifierKind::Abstract) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ModifierKind { + Abstract, + Accessor, + Async, + Const, + Declare, + Default, + Export, + In, + Public, + Private, + Protected, + Readonly, + Static, + Out, + Override, +} + +impl ModifierKind { + pub fn as_str(self) -> &'static str { + match self { + Self::Abstract => "abstract", + Self::Accessor => "accessor", + Self::Async => "async", + Self::Const => "const", + Self::Declare => "declare", + Self::Default => "default", + Self::Export => "export", + Self::In => "in", + Self::Public => "public", + Self::Private => "private", + Self::Protected => "protected", + Self::Readonly => "readonly", + Self::Static => "static", + Self::Out => "out", + Self::Override => "override", + } + } +} + +impl<'a> ParserImpl<'a> { + pub(crate) fn eat_modifiers_before_declaration(&mut self) -> (ModifierFlags, Modifiers<'a>) { + let mut flags = ModifierFlags::empty(); + let mut modifiers = self.ast.new_vec(); + while self.at_modifier() { + let span = self.start_span(); + let modifier_flag = self.cur_kind().into(); + flags.set(modifier_flag, true); + let kind = self.cur_kind(); + self.bump_any(); + modifiers.push(Self::modifier(kind, self.end_span(span))); + } + (flags, Modifiers::new(modifiers)) + } + + fn at_modifier(&mut self) -> bool { + self.lookahead(Self::at_modifier_worker) + } + + fn at_modifier_worker(&mut self) -> bool { + if !self.cur_kind().is_modifier_kind() { + return false; + } + + match self.cur_kind() { + Kind::Const => !self.peek_token().is_on_new_line && self.peek_kind() == Kind::Enum, + Kind::Export => { + self.bump_any(); + match self.cur_kind() { + Kind::Default => { + self.bump_any(); + self.can_follow_default() + } + Kind::Type => { + self.bump_any(); + self.can_follow_export() + } + _ => self.can_follow_export(), + } + } + Kind::Default => { + self.bump_any(); + self.can_follow_default() + } + Kind::Accessor | Kind::Static | Kind::Get | Kind::Set => { + // These modifiers can cross line. + self.bump_any(); + Self::can_follow_modifier(self.cur_kind()) + } + // Rest modifiers cannot cross line + _ => { + self.bump_any(); + Self::can_follow_modifier(self.cur_kind()) && !self.cur_token().is_on_new_line + } + } + } + + fn can_follow_default(&mut self) -> bool { + let at_declaration = + matches!(self.cur_kind(), Kind::Class | Kind::Function | Kind::Interface); + let at_abstract_declaration = self.at(Kind::Abstract) + && self.peek_at(Kind::Class) + && !self.peek_token().is_on_new_line; + let at_async_function = self.at(Kind::Async) + && self.peek_at(Kind::Function) + && !self.peek_token().is_on_new_line; + at_declaration | at_abstract_declaration | at_async_function + } + + fn can_follow_export(&mut self) -> bool { + // Note that the `export` in export assignment is not a modifier + // and are handled explicitly in the parser. + !matches!(self.cur_kind(), Kind::Star | Kind::As | Kind::LCurly) + && Self::can_follow_modifier(self.cur_kind()) + } + + fn can_follow_modifier(kind: Kind) -> bool { + kind.is_literal_property_name() + || matches!(kind, Kind::LCurly | Kind::LBrack | Kind::Star | Kind::Dot3) + } + + fn modifier(kind: Kind, span: Span) -> Modifier { + let modifier_kind = match kind { + Kind::Abstract => ModifierKind::Abstract, + Kind::Declare => ModifierKind::Declare, + Kind::Private => ModifierKind::Private, + Kind::Protected => ModifierKind::Protected, + Kind::Public => ModifierKind::Public, + Kind::Static => ModifierKind::Static, + Kind::Readonly => ModifierKind::Readonly, + Kind::Override => ModifierKind::Override, + Kind::Async => ModifierKind::Async, + Kind::Const => ModifierKind::Const, + Kind::In => ModifierKind::In, + Kind::Out => ModifierKind::Out, + Kind::Export => ModifierKind::Export, + Kind::Default => ModifierKind::Default, + Kind::Accessor => ModifierKind::Accessor, + _ => unreachable!(), + }; + Modifier { span, kind: modifier_kind } + } +} diff --git a/crates/oxc_parser/src/ts/declaration.rs b/crates/oxc_parser/src/ts/declaration.rs deleted file mode 100644 index 36f8136b6fad11..00000000000000 --- a/crates/oxc_parser/src/ts/declaration.rs +++ /dev/null @@ -1,141 +0,0 @@ -use crate::{lexer::Kind, ParserImpl}; - -impl<'a> ParserImpl<'a> { - /// Check if the parser is at a start of a declaration - fn at_start_of_ts_declaration_worker(&mut self) -> bool { - loop { - match self.cur_kind() { - Kind::Var | Kind::Let | Kind::Const | Kind::Function | Kind::Class | Kind::Enum => { - return true; - } - Kind::Interface | Kind::Type => { - self.bump_any(); - return self.cur_kind().is_binding_identifier() - && !self.cur_token().is_on_new_line; - } - Kind::Module | Kind::Namespace => { - self.bump_any(); - return !self.cur_token().is_on_new_line - && (self.cur_kind().is_binding_identifier() - || self.cur_kind() == Kind::Str); - } - Kind::Abstract - | Kind::Accessor - | Kind::Async - | Kind::Declare - | Kind::Private - | Kind::Protected - | Kind::Public - | Kind::Readonly => { - self.bump_any(); - if self.cur_token().is_on_new_line { - return false; - } - } - Kind::Global => { - self.bump_any(); - return matches!(self.cur_kind(), Kind::Ident | Kind::LCurly | Kind::Export); - } - Kind::Import => { - self.bump_any(); - return matches!(self.cur_kind(), Kind::Str | Kind::Star | Kind::LCurly) - || self.cur_kind().is_identifier(); - } - Kind::Export => { - self.bump_any(); - let kind = if self.cur_kind() == Kind::Type { - self.peek_kind() - } else { - self.cur_kind() - }; - // This allows constructs like - // `export *`, `export default`, `export {}`, `export = {}` along with all - // export [declaration] - if matches!( - kind, - Kind::Eq | Kind::Star | Kind::Default | Kind::LCurly | Kind::At | Kind::As - ) { - return true; - } - // falls through to check next token - } - Kind::Static => { - self.bump_any(); - } - _ => { - return false; - } - } - } - } - - pub(crate) fn at_start_of_ts_declaration(&mut self) -> bool { - self.lookahead(Self::at_start_of_ts_declaration_worker) - } -} - -#[cfg(test)] -mod test_is_declaration { - use oxc_allocator::Allocator; - use oxc_span::SourceType; - - use super::*; - use crate::ParserOptions; - - fn run_check(source: &str, expected: bool) { - let alloc = Allocator::default(); - let source_type = SourceType::default().with_typescript(true); - let mut parser = - ParserImpl::new_for_tests(&alloc, source, source_type, ParserOptions::default()); - // Get the parser to the first token. - parser.bump_any(); - assert_eq!(expected, parser.at_start_of_ts_declaration()); - } - - #[test] - fn test_lexical_declaration() { - run_check("const a = 1", true); - run_check("let a = 1", true); - } - - #[test] - fn test_combined_modifier() { - // The order of modifiers shouldn't matter - let source = "abstract async function a() { return 123; }"; - let source2 = "async abstract class C{}"; - run_check(source, true); - run_check(source2, true); - } - - #[test] - fn test_contextual_keyword() { - // Here abstract should not be parsed as starting a declaration - run_check("abstract = 1", false); - run_check("private = 'abc'", false); - run_check("abstract\nclass A {}", false); - } - - #[test] - fn test_export() { - run_check("export = {}", true); - run_check("export *", true); - // modifiers can be combined with expory - run_check("abstract export type T", true); - } - - #[test] - fn test_declare_module() { - run_check("declare module 'external1' {}", true); - } - - #[test] - fn test_const_enum() { - run_check("const enum A {}", true); - } - - #[test] - fn test_type_alias() { - run_check("type string = I", true); - run_check("type void = I", false); - } -} diff --git a/crates/oxc_parser/src/ts/mod.rs b/crates/oxc_parser/src/ts/mod.rs index 5a5f341b596096..a3308d7bb48e6e 100644 --- a/crates/oxc_parser/src/ts/mod.rs +++ b/crates/oxc_parser/src/ts/mod.rs @@ -1,4 +1,3 @@ -mod declaration; mod list; mod statement; mod types; diff --git a/crates/oxc_parser/src/ts/statement.rs b/crates/oxc_parser/src/ts/statement.rs index 9f20939c225bd5..92dfc9059c08f5 100644 --- a/crates/oxc_parser/src/ts/statement.rs +++ b/crates/oxc_parser/src/ts/statement.rs @@ -3,15 +3,13 @@ use oxc_ast::ast::*; use oxc_diagnostics::Result; use oxc_span::Span; -use super::{ - list::{TSEnumMemberList, TSInterfaceOrObjectBodyList}, - types::ModifierFlags, -}; +use super::list::{TSEnumMemberList, TSInterfaceOrObjectBodyList}; use crate::{ diagnostics, js::{FunctionKind, VariableDeclarationContext, VariableDeclarationParent}, lexer::Kind, list::{NormalList, SeparatedList}, + modifiers::{ModifierKind, Modifiers}, ParserImpl, }; @@ -472,106 +470,75 @@ impl<'a> ParserImpl<'a> { Ok(()) } - pub(crate) fn eat_modifiers_before_declaration(&mut self) -> (ModifierFlags, Modifiers<'a>) { - let mut flags = ModifierFlags::empty(); - let mut modifiers = self.ast.new_vec(); - while self.at_modifier() { - let span = self.start_span(); - let modifier_flag = self.cur_kind().into(); - flags.set(modifier_flag, true); - let kind = self.cur_kind(); - self.bump_any(); - modifiers.push(Self::modifier(kind, self.end_span(span))); - } - - (flags, Modifiers::new(modifiers)) - } - - fn at_modifier(&mut self) -> bool { - self.lookahead(Self::at_modifier_worker) + pub(crate) fn at_start_of_ts_declaration(&mut self) -> bool { + self.lookahead(Self::at_start_of_ts_declaration_worker) } - fn at_modifier_worker(&mut self) -> bool { - if !self.cur_kind().is_modifier_kind() { - return false; - } - - match self.cur_kind() { - Kind::Const => !self.peek_token().is_on_new_line && self.peek_kind() == Kind::Enum, - Kind::Export => { - self.bump_any(); - match self.cur_kind() { - Kind::Default => { - self.bump_any(); - self.can_follow_default() + /// Check if the parser is at a start of a declaration + fn at_start_of_ts_declaration_worker(&mut self) -> bool { + loop { + match self.cur_kind() { + Kind::Var | Kind::Let | Kind::Const | Kind::Function | Kind::Class | Kind::Enum => { + return true; + } + Kind::Interface | Kind::Type => { + self.bump_any(); + return self.cur_kind().is_binding_identifier() + && !self.cur_token().is_on_new_line; + } + Kind::Module | Kind::Namespace => { + self.bump_any(); + return !self.cur_token().is_on_new_line + && (self.cur_kind().is_binding_identifier() + || self.cur_kind() == Kind::Str); + } + Kind::Abstract + | Kind::Accessor + | Kind::Async + | Kind::Declare + | Kind::Private + | Kind::Protected + | Kind::Public + | Kind::Readonly => { + self.bump_any(); + if self.cur_token().is_on_new_line { + return false; } - Kind::Type => { - self.bump_any(); - self.can_follow_export() + } + Kind::Global => { + self.bump_any(); + return matches!(self.cur_kind(), Kind::Ident | Kind::LCurly | Kind::Export); + } + Kind::Import => { + self.bump_any(); + return matches!(self.cur_kind(), Kind::Str | Kind::Star | Kind::LCurly) + || self.cur_kind().is_identifier(); + } + Kind::Export => { + self.bump_any(); + let kind = if self.cur_kind() == Kind::Type { + self.peek_kind() + } else { + self.cur_kind() + }; + // This allows constructs like + // `export *`, `export default`, `export {}`, `export = {}` along with all + // export [declaration] + if matches!( + kind, + Kind::Eq | Kind::Star | Kind::Default | Kind::LCurly | Kind::At | Kind::As + ) { + return true; } - _ => self.can_follow_export(), + // falls through to check next token + } + Kind::Static => { + self.bump_any(); + } + _ => { + return false; } - } - Kind::Default => { - self.bump_any(); - self.can_follow_default() - } - Kind::Accessor | Kind::Static | Kind::Get | Kind::Set => { - // These modifiers can cross line. - self.bump_any(); - Self::can_follow_modifier(self.cur_kind()) - } - // Rest modifiers cannot cross line - _ => { - self.bump_any(); - Self::can_follow_modifier(self.cur_kind()) && !self.cur_token().is_on_new_line } } } - - fn can_follow_default(&mut self) -> bool { - let at_declaration = - matches!(self.cur_kind(), Kind::Class | Kind::Function | Kind::Interface); - let at_abstract_declaration = self.at(Kind::Abstract) - && self.peek_at(Kind::Class) - && !self.peek_token().is_on_new_line; - let at_async_function = self.at(Kind::Async) - && self.peek_at(Kind::Function) - && !self.peek_token().is_on_new_line; - at_declaration | at_abstract_declaration | at_async_function - } - - fn can_follow_export(&mut self) -> bool { - // Note that the `export` in export assignment is not a modifier - // and are handled explicitly in the parser. - !matches!(self.cur_kind(), Kind::Star | Kind::As | Kind::LCurly) - && Self::can_follow_modifier(self.cur_kind()) - } - - fn can_follow_modifier(kind: Kind) -> bool { - kind.is_literal_property_name() - || matches!(kind, Kind::LCurly | Kind::LBrack | Kind::Star | Kind::Dot3) - } - - fn modifier(kind: Kind, span: Span) -> Modifier { - let modifier_kind = match kind { - Kind::Abstract => ModifierKind::Abstract, - Kind::Declare => ModifierKind::Declare, - Kind::Private => ModifierKind::Private, - Kind::Protected => ModifierKind::Protected, - Kind::Public => ModifierKind::Public, - Kind::Static => ModifierKind::Static, - Kind::Readonly => ModifierKind::Readonly, - Kind::Override => ModifierKind::Override, - Kind::Async => ModifierKind::Async, - Kind::Const => ModifierKind::Const, - Kind::In => ModifierKind::In, - Kind::Out => ModifierKind::Out, - Kind::Export => ModifierKind::Export, - Kind::Default => ModifierKind::Default, - Kind::Accessor => ModifierKind::Accessor, - _ => unreachable!(), - }; - Modifier { span, kind: modifier_kind } - } } diff --git a/crates/oxc_parser/src/ts/types.rs b/crates/oxc_parser/src/ts/types.rs index 79b4907a7dcddf..80ad90879526ff 100644 --- a/crates/oxc_parser/src/ts/types.rs +++ b/crates/oxc_parser/src/ts/types.rs @@ -1,4 +1,3 @@ -use bitflags::bitflags; use oxc_allocator::{Box, Vec}; use oxc_ast::ast::*; use oxc_diagnostics::Result; @@ -13,100 +12,11 @@ use crate::{ js::list::{ArrayPatternList, ObjectPatternProperties}, lexer::Kind, list::{NormalList, SeparatedList}, + modifiers::ModifierFlags, ts::list::TSImportAttributeList, Context, ParserImpl, }; -bitflags! { - /// Bitflag of modifiers and contextual modifiers. - /// Useful to cheaply track all already seen modifiers of a member (instead of using a HashSet). - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct ModifierFlags: u16 { - const DECLARE = 1 << 0; - const PRIVATE = 1 << 1; - const PROTECTED = 1 << 2; - const PUBLIC = 1 << 3; - const STATIC = 1 << 4; - const READONLY = 1 << 5; - const ABSTRACT = 1 << 6; - const OVERRIDE = 1 << 7; - const ASYNC = 1 << 8; - const CONST = 1 << 9; - const IN = 1 << 10; - const OUT = 1 << 11; - const EXPORT = 1 << 12; - const DEFAULT = 1 << 13; - const ACCESSOR = 1 << 14; - const ACCESSIBILITY = Self::PRIVATE.bits() | Self::PROTECTED.bits() | Self::PUBLIC.bits(); - } -} - -/// It is the caller's safety to always check by `Kind::is_modifier_kind` -/// before converting [`Kind`] to [`ModifierFlags`] so that we can assume here that -/// the conversion always succeeds. -impl From for ModifierFlags { - fn from(value: Kind) -> Self { - match value { - Kind::Abstract => Self::ABSTRACT, - Kind::Declare => Self::DECLARE, - Kind::Private => Self::PRIVATE, - Kind::Protected => Self::PROTECTED, - Kind::Public => Self::PUBLIC, - Kind::Static => Self::STATIC, - Kind::Readonly => Self::READONLY, - Kind::Override => Self::OVERRIDE, - Kind::Async => Self::ASYNC, - Kind::Const => Self::CONST, - Kind::In => Self::IN, - Kind::Out => Self::OUT, - Kind::Export => Self::EXPORT, - Kind::Default => Self::DEFAULT, - Kind::Accessor => Self::ACCESSOR, - _ => unreachable!(), - } - } -} - -impl ModifierFlags { - pub(crate) fn accessibility(self) -> Option { - if self.contains(Self::PUBLIC) { - return Some(TSAccessibility::Public); - } - if self.contains(Self::PROTECTED) { - return Some(TSAccessibility::Protected); - } - - if self.contains(Self::PRIVATE) { - return Some(TSAccessibility::Private); - } - None - } - - pub(crate) fn readonly(self) -> bool { - self.contains(Self::READONLY) - } - - pub(crate) fn declare(self) -> bool { - self.contains(Self::DECLARE) - } - - pub(crate) fn r#async(self) -> bool { - self.contains(Self::ASYNC) - } - - pub(crate) fn r#override(self) -> bool { - self.contains(Self::OVERRIDE) - } - - pub(crate) fn r#abstract(self) -> bool { - self.contains(Self::ABSTRACT) - } - - pub(crate) fn r#static(self) -> bool { - self.contains(Self::STATIC) - } -} - impl<'a> ParserImpl<'a> { pub(crate) fn parse_ts_type(&mut self) -> Result> { if self.is_at_constructor_type() {