refactor(parser): move code around for parsing Modifiers (#3849)

This commit is contained in:
Boshen 2024-06-23 12:46:42 +00:00
parent 2f5d50e710
commit 97d59fc2f3
12 changed files with 377 additions and 478 deletions

View file

@ -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<Vec<'a, Modifier>>);
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<Item = &Modifier> + '_ {
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<F>(&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",
}
}
}

View file

@ -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<Box<'a, TSTypeParameterInstantiation<'a>>>, Span)>;

View file

@ -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<Statement<'a>> {

View file

@ -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 {

View file

@ -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)

View file

@ -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

View file

@ -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,

View file

@ -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<ModifierKind>).
#[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<Kind> 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<TSAccessibility> {
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<Vec<'a, Modifier>>);
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<Item = &Modifier> + '_ {
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 }
}
}

View file

@ -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);
}
}

View file

@ -1,4 +1,3 @@
mod declaration;
mod list;
mod statement;
mod types;

View file

@ -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))
pub(crate) fn at_start_of_ts_declaration(&mut self) -> bool {
self.lookahead(Self::at_start_of_ts_declaration_worker)
}
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()
/// 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 }
}
}

View file

@ -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<ModifierKind>).
#[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<Kind> 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<TSAccessibility> {
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<TSType<'a>> {
if self.is_at_constructor_type() {