mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 20:28:58 +00:00
refactor(parser): move code around for parsing Modifiers (#3849)
This commit is contained in:
parent
2f5d50e710
commit
97d59fc2f3
12 changed files with 377 additions and 478 deletions
|
|
@ -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",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)>;
|
||||
|
|
|
|||
|
|
@ -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>> {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
283
crates/oxc_parser/src/modifiers.rs
Normal file
283
crates/oxc_parser/src/modifiers.rs
Normal 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 }
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
mod declaration;
|
||||
mod list;
|
||||
mod statement;
|
||||
mod types;
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue