mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
562 lines
20 KiB
Rust
562 lines
20 KiB
Rust
use oxc_allocator::Box;
|
|
use oxc_ast::ast::*;
|
|
use oxc_diagnostics::Result;
|
|
use oxc_span::Span;
|
|
|
|
use super::{
|
|
list::{TSEnumMemberList, TSInterfaceOrObjectBodyList},
|
|
types::ModifierFlags,
|
|
};
|
|
use crate::{
|
|
js::{
|
|
declaration::{VariableDeclarationContext, VariableDeclarationParent},
|
|
function::FunctionKind,
|
|
},
|
|
lexer::Kind,
|
|
list::{NormalList, SeparatedList},
|
|
Parser, StatementContext,
|
|
};
|
|
|
|
impl<'a> Parser<'a> {
|
|
/** ------------------- Enum ------------------ */
|
|
|
|
pub(crate) fn is_at_enum_declaration(&mut self) -> bool {
|
|
self.at(Kind::Enum) || (self.at(Kind::Const) && self.peek_at(Kind::Enum))
|
|
}
|
|
|
|
/// `https://www.typescriptlang.org/docs/handbook/enums.html`
|
|
pub(crate) fn parse_ts_enum_declaration(
|
|
&mut self,
|
|
span: Span,
|
|
modifiers: Modifiers<'a>,
|
|
) -> Result<Declaration<'a>> {
|
|
self.expect(Kind::Enum)?;
|
|
|
|
let id = self.parse_binding_identifier()?;
|
|
let members = TSEnumMemberList::parse(self)?.members;
|
|
Ok(self.ast.ts_enum_declaration(span, id, members, modifiers))
|
|
}
|
|
|
|
pub(crate) fn parse_ts_enum_member(&mut self) -> Result<TSEnumMember<'a>> {
|
|
let span = self.start_span();
|
|
let id = self.parse_ts_enum_member_name()?;
|
|
|
|
let initializer =
|
|
if self.eat(Kind::Eq) { Some(self.parse_assignment_expression_base()?) } else { None };
|
|
|
|
Ok(TSEnumMember { span: self.end_span(span), id, initializer })
|
|
}
|
|
|
|
fn parse_ts_enum_member_name(&mut self) -> Result<TSEnumMemberName<'a>> {
|
|
match self.cur_kind() {
|
|
Kind::LBrack => {
|
|
Ok(TSEnumMemberName::ComputedPropertyName(self.parse_computed_property_name()?))
|
|
}
|
|
Kind::Str => Ok(TSEnumMemberName::StringLiteral(self.parse_literal_string()?)),
|
|
kind if kind.is_number() => {
|
|
Ok(TSEnumMemberName::NumberLiteral(self.parse_literal_number()?))
|
|
}
|
|
_ => Ok(TSEnumMemberName::Identifier(self.parse_identifier_name()?)),
|
|
}
|
|
}
|
|
|
|
/** ------------------- Annotation ----------------- */
|
|
|
|
pub(crate) fn parse_ts_type_annotation(
|
|
&mut self,
|
|
) -> Result<Option<Box<'a, TSTypeAnnotation<'a>>>> {
|
|
if self.at(Kind::Colon) {
|
|
let span = self.start_span();
|
|
self.bump_any(); // bump ':'
|
|
let type_annotation = self.parse_ts_type()?;
|
|
Ok(Some(self.ast.ts_type_annotation(self.end_span(span), type_annotation)))
|
|
} else {
|
|
Ok(None)
|
|
}
|
|
}
|
|
|
|
pub(crate) fn parse_ts_variable_annotation(
|
|
&mut self,
|
|
) -> Result<(Option<Box<'a, TSTypeAnnotation<'a>>>, bool)> {
|
|
if !self.at(Kind::Bang) {
|
|
return Ok((self.parse_ts_type_annotation()?, false));
|
|
}
|
|
|
|
if self.cur_token().is_on_new_line {
|
|
return Ok((None, false));
|
|
}
|
|
|
|
let span = self.start_span();
|
|
self.bump(Kind::Bang);
|
|
|
|
if self.eat(Kind::Colon) {
|
|
let type_annotation = self.parse_ts_type()?;
|
|
Ok((Some(self.ast.ts_type_annotation(self.end_span(span), type_annotation)), true))
|
|
} else {
|
|
Err(self.unexpected())
|
|
}
|
|
}
|
|
|
|
pub(crate) fn parse_ts_type_alias_declaration(
|
|
&mut self,
|
|
span: Span,
|
|
modifiers: Modifiers<'a>,
|
|
) -> Result<Declaration<'a>> {
|
|
self.expect(Kind::Type)?;
|
|
|
|
let id = self.parse_binding_identifier()?;
|
|
let params = self.parse_ts_type_parameters()?;
|
|
self.expect(Kind::Eq)?;
|
|
|
|
let annotation = self.parse_ts_type()?;
|
|
|
|
self.asi()?;
|
|
Ok(self.ast.ts_type_alias_declaration(span, id, annotation, params, modifiers))
|
|
}
|
|
|
|
/** --------------------- Interface ------------------------ */
|
|
|
|
pub(crate) fn parse_ts_interface_declaration(
|
|
&mut self,
|
|
span: Span,
|
|
modifiers: Modifiers<'a>,
|
|
) -> Result<Declaration<'a>> {
|
|
self.expect(Kind::Interface)?; // bump interface
|
|
let id = self.parse_binding_identifier()?;
|
|
let type_parameters = self.parse_ts_type_parameters()?;
|
|
let (extends, _) = self.parse_heritage_clause()?;
|
|
let body = self.parse_ts_interface_body()?;
|
|
let extends = extends.map(|e| self.ast.ts_interface_heritages(e));
|
|
Ok(self.ast.ts_interface_declaration(
|
|
self.end_span(span),
|
|
id,
|
|
body,
|
|
type_parameters,
|
|
extends,
|
|
modifiers,
|
|
))
|
|
}
|
|
|
|
fn parse_ts_interface_body(&mut self) -> Result<Box<'a, TSInterfaceBody<'a>>> {
|
|
let span = self.start_span();
|
|
let mut body_list = TSInterfaceOrObjectBodyList::new(self);
|
|
body_list.parse(self)?;
|
|
Ok(self.ast.ts_interface_body(self.end_span(span), body_list.body))
|
|
}
|
|
|
|
pub(crate) fn is_at_interface_declaration(&mut self) -> bool {
|
|
if !self.at(Kind::Interface) || self.peek_token().is_on_new_line {
|
|
false
|
|
} else {
|
|
self.peek_token().kind.is_binding_identifier() || self.peek_at(Kind::LCurly)
|
|
}
|
|
}
|
|
|
|
pub(crate) fn parse_ts_type_signature(&mut self) -> Result<TSSignature<'a>> {
|
|
if self.is_at_ts_index_signature_member() {
|
|
return self.parse_ts_index_signature_member();
|
|
}
|
|
|
|
match self.cur_kind() {
|
|
Kind::LParen | Kind::LAngle => self.parse_ts_call_signature_member(),
|
|
Kind::New if self.peek_at(Kind::LParen) || self.peek_at(Kind::LAngle) => {
|
|
self.parse_ts_constructor_signature_member()
|
|
}
|
|
Kind::Get if self.is_next_at_type_member_name() => {
|
|
self.parse_ts_getter_signature_member()
|
|
}
|
|
Kind::Set if self.is_next_at_type_member_name() => {
|
|
self.parse_ts_setter_signature_member()
|
|
}
|
|
_ => self.parse_ts_property_or_method_signature_member(),
|
|
}
|
|
}
|
|
|
|
/// Must be at `[ident:` or `<modifiers> [ident:`
|
|
pub(crate) fn is_at_ts_index_signature_member(&mut self) -> bool {
|
|
let mut offset = 0;
|
|
while self.is_nth_at_modifier(offset, false) {
|
|
offset += 1;
|
|
}
|
|
|
|
if !self.nth_at(offset, Kind::LBrack) {
|
|
return false;
|
|
}
|
|
|
|
if !self.nth_kind(offset + 1).is_identifier() {
|
|
return false;
|
|
}
|
|
|
|
self.nth_at(offset + 2, Kind::Colon)
|
|
}
|
|
|
|
pub(crate) fn is_nth_at_modifier(&mut self, n: u8, is_constructor_parameter: bool) -> bool {
|
|
let nth = self.nth(n);
|
|
if !(matches!(
|
|
nth.kind,
|
|
Kind::Public
|
|
| Kind::Protected
|
|
| Kind::Private
|
|
| Kind::Static
|
|
| Kind::Abstract
|
|
| Kind::Readonly
|
|
| Kind::Declare
|
|
| Kind::Override
|
|
)) {
|
|
return false;
|
|
}
|
|
|
|
let next = self.nth(n + 1);
|
|
|
|
if next.is_on_new_line {
|
|
false
|
|
} else {
|
|
let followed_by_any_member =
|
|
matches!(next.kind, Kind::PrivateIdentifier | Kind::LBrack)
|
|
|| next.kind.is_literal_property_name();
|
|
let followed_by_class_member = !is_constructor_parameter && next.kind == Kind::Star;
|
|
// allow `...` for error recovery
|
|
let followed_by_parameter = is_constructor_parameter
|
|
&& matches!(next.kind, Kind::LCurly | Kind::LBrack | Kind::Dot3);
|
|
|
|
followed_by_any_member || followed_by_class_member || followed_by_parameter
|
|
}
|
|
}
|
|
|
|
/** ----------------------- Namespace & Module ----------------------- */
|
|
|
|
fn parse_ts_module_block(&mut self) -> Result<Box<'a, TSModuleBlock<'a>>> {
|
|
let span = self.start_span();
|
|
|
|
let mut statements = self.ast.new_vec();
|
|
|
|
if self.at(Kind::LCurly) {
|
|
self.expect(Kind::LCurly)?;
|
|
|
|
while !self.eat(Kind::RCurly) && !self.at(Kind::Eof) {
|
|
let stmt = self.parse_ts_module_item()?;
|
|
statements.push(stmt);
|
|
}
|
|
}
|
|
|
|
Ok(self.ast.ts_module_block(self.end_span(span), statements))
|
|
}
|
|
|
|
fn parse_ts_module_item(&mut self) -> Result<Statement<'a>> {
|
|
self.parse_statement_list_item(StatementContext::StatementList)
|
|
}
|
|
|
|
pub(crate) fn parse_ts_namespace_or_module_declaration_body(
|
|
&mut self,
|
|
span: Span,
|
|
modifiers: Modifiers<'a>,
|
|
) -> Result<Box<'a, TSModuleDeclaration<'a>>> {
|
|
let id = match self.cur_kind() {
|
|
Kind::Str => self.parse_literal_string().map(TSModuleDeclarationName::StringLiteral),
|
|
_ => self.parse_identifier_name().map(TSModuleDeclarationName::Identifier),
|
|
}?;
|
|
|
|
let body = if self.eat(Kind::Dot) {
|
|
let span = self.start_span();
|
|
let decl =
|
|
self.parse_ts_namespace_or_module_declaration_body(span, Modifiers::empty())?;
|
|
TSModuleDeclarationBody::TSModuleDeclaration(decl)
|
|
} else {
|
|
let block = self.parse_ts_module_block()?;
|
|
self.asi()?;
|
|
TSModuleDeclarationBody::TSModuleBlock(block)
|
|
};
|
|
|
|
Ok(self.ast.ts_module_declaration(self.end_span(span), id, body, modifiers))
|
|
}
|
|
|
|
pub(crate) fn parse_ts_namespace_or_module_declaration(
|
|
&mut self,
|
|
modifiers: Modifiers<'a>,
|
|
) -> Result<Box<'a, TSModuleDeclaration<'a>>> {
|
|
let span = self.start_span();
|
|
self.expect(Kind::Namespace).or_else(|_| self.expect(Kind::Module))?;
|
|
self.parse_ts_namespace_or_module_declaration_body(span, modifiers)
|
|
}
|
|
|
|
pub(crate) fn parse_ts_global_declaration(
|
|
&mut self,
|
|
start_span: Span,
|
|
modifiers: Modifiers<'a>,
|
|
) -> Result<Box<'a, TSModuleDeclaration<'a>>> {
|
|
self.parse_ts_namespace_or_module_declaration_body(start_span, modifiers)
|
|
}
|
|
|
|
/** ----------------------- declare --------------------- */
|
|
|
|
pub(crate) fn parse_ts_declaration_statement(
|
|
&mut self,
|
|
start_span: Span,
|
|
) -> Result<Statement<'a>> {
|
|
let reserved_ctx = self.ctx;
|
|
|
|
let (flags, modifiers) = self.eat_modifiers_before_declaration();
|
|
let declare = flags.declare();
|
|
let r#async = flags.r#async();
|
|
self.ctx = self.ctx.and_ambient(declare).and_await(r#async);
|
|
|
|
let result = self.parse_declaration(start_span, modifiers);
|
|
|
|
self.ctx = reserved_ctx;
|
|
result.map(Statement::Declaration)
|
|
}
|
|
|
|
pub(crate) fn parse_declaration(
|
|
&mut self,
|
|
start_span: Span,
|
|
modifiers: Modifiers<'a>,
|
|
) -> Result<Declaration<'a>> {
|
|
match self.cur_kind() {
|
|
Kind::Namespace | Kind::Module => self
|
|
.parse_ts_namespace_or_module_declaration(modifiers)
|
|
.map(Declaration::TSModuleDeclaration),
|
|
Kind::Global => {
|
|
let decl = if self.peek_at(Kind::LCurly) {
|
|
// valid syntax for
|
|
// declare global { }
|
|
self.parse_ts_namespace_or_module_declaration_body(start_span, modifiers)
|
|
} else {
|
|
self.parse_ts_global_declaration(start_span, modifiers)
|
|
}?;
|
|
Ok(Declaration::TSModuleDeclaration(decl))
|
|
}
|
|
Kind::Type => self.parse_ts_type_alias_declaration(start_span, modifiers),
|
|
Kind::Enum => self.parse_ts_enum_declaration(start_span, modifiers),
|
|
Kind::Interface if self.is_at_interface_declaration() => {
|
|
self.parse_ts_interface_declaration(start_span, modifiers)
|
|
}
|
|
Kind::Class => self
|
|
.parse_class_declaration(start_span, modifiers)
|
|
.map(Declaration::ClassDeclaration),
|
|
Kind::Import => {
|
|
self.bump_any();
|
|
self.parse_ts_import_equals_declaration(start_span, true)
|
|
}
|
|
kind if kind.is_variable_declaration() => self
|
|
.parse_variable_declaration(
|
|
start_span,
|
|
VariableDeclarationContext::new(VariableDeclarationParent::Clause),
|
|
modifiers,
|
|
)
|
|
.map(Declaration::VariableDeclaration),
|
|
_ if self.at_function_with_async() => {
|
|
let declare = modifiers.contains(ModifierKind::Declare);
|
|
if declare {
|
|
self.parse_ts_declare_function(start_span, modifiers)
|
|
.map(Declaration::FunctionDeclaration)
|
|
} else if self.ts_enabled() {
|
|
self.parse_ts_function_impl(
|
|
start_span,
|
|
FunctionKind::Declaration { single_statement: true },
|
|
modifiers,
|
|
)
|
|
.map(Declaration::FunctionDeclaration)
|
|
} else {
|
|
self.parse_function_impl(FunctionKind::Declaration { single_statement: true })
|
|
.map(Declaration::FunctionDeclaration)
|
|
}
|
|
}
|
|
_ => Err(self.unexpected()),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn parse_ts_declare_function(
|
|
&mut self,
|
|
start_span: Span,
|
|
modifiers: Modifiers<'a>,
|
|
) -> Result<Box<'a, Function<'a>>> {
|
|
let r#async = modifiers.contains(ModifierKind::Async);
|
|
self.expect(Kind::Function)?;
|
|
let func_kind = FunctionKind::TSDeclaration;
|
|
let id = self.parse_function_id(func_kind, r#async, false);
|
|
self.parse_function(start_span, id, r#async, false, func_kind, modifiers)
|
|
}
|
|
|
|
pub(crate) fn parse_ts_type_assertion(&mut self) -> Result<Expression<'a>> {
|
|
let span = self.start_span();
|
|
self.re_lex_ts_l_angle();
|
|
self.expect(Kind::LAngle)?;
|
|
let type_annotation = self.parse_ts_type()?;
|
|
self.expect(Kind::RAngle)?;
|
|
let lhs_span = self.start_span();
|
|
let expression = self.parse_unary_expression_base(lhs_span)?;
|
|
Ok(self.ast.ts_type_assertion(self.end_span(span), type_annotation, expression))
|
|
}
|
|
|
|
pub(crate) fn parse_ts_import_equals_declaration(
|
|
&mut self,
|
|
span: Span,
|
|
is_export: bool,
|
|
) -> Result<Declaration<'a>> {
|
|
let import_kind = if !self.peek_at(Kind::Eq) && self.eat(Kind::Type) {
|
|
ImportOrExportKind::Value
|
|
} else {
|
|
ImportOrExportKind::Type
|
|
};
|
|
|
|
let id = self.parse_binding_identifier()?;
|
|
|
|
self.expect(Kind::Eq)?;
|
|
|
|
let reference_span = self.start_span();
|
|
let module_reference = if self.eat(Kind::Require) {
|
|
self.expect(Kind::LParen)?;
|
|
let expression = self.parse_literal_string()?;
|
|
self.expect(Kind::RParen)?;
|
|
TSModuleReference::ExternalModuleReference(TSExternalModuleReference {
|
|
span: self.end_span(reference_span),
|
|
expression,
|
|
})
|
|
} else {
|
|
TSModuleReference::TypeName(self.parse_ts_qualified_name()?)
|
|
};
|
|
|
|
self.asi()?;
|
|
|
|
Ok(self.ast.ts_import_equals_declaration(
|
|
self.end_span(span),
|
|
id,
|
|
module_reference,
|
|
is_export,
|
|
import_kind,
|
|
))
|
|
}
|
|
|
|
pub(crate) fn parse_ts_this_parameter(&mut self) -> Result<FormalParameter<'a>> {
|
|
let span = self.start_span();
|
|
let (ident_span, name) = self.parse_identifier_kind(Kind::This);
|
|
let type_annotation = self.parse_ts_type_annotation()?;
|
|
let kind = self.ast.binding_identifier(BindingIdentifier { span: ident_span, name });
|
|
let binding = self.ast.binding_pattern(kind, type_annotation, /* optional */ false);
|
|
Ok(self.ast.formal_parameter(
|
|
self.end_span(span),
|
|
binding,
|
|
/* accessibility */ None,
|
|
/* readonly */ false,
|
|
/* decorators */ self.ast.new_vec(),
|
|
))
|
|
}
|
|
|
|
pub(crate) fn eat_decorators(&mut self) -> Result<()> {
|
|
if !self.at(Kind::At) {
|
|
return Ok(());
|
|
}
|
|
|
|
let mut decorators = self.ast.new_vec();
|
|
while self.at(Kind::At) {
|
|
let decorator = self.parse_decorator()?;
|
|
decorators.push(decorator);
|
|
}
|
|
|
|
self.state.decorators = decorators;
|
|
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)
|
|
}
|
|
|
|
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 }
|
|
}
|
|
}
|