use oxc_allocator::{Box, Vec};
use oxc_ast::ast::*;
use oxc_ecmascript::PropName;
use oxc_span::{GetSpan, Span};
use crate::{
Context, ParserConfig as Config, ParserImpl, StatementContext, diagnostics,
lexer::Kind,
modifiers::{ModifierKind, ModifierKinds, Modifiers},
};
use super::FunctionKind;
type ImplementsWithKeywordSpan<'a> = (Span, Vec<'a, TSClassImplements<'a>>);
impl<'a, C: Config> ParserImpl<'a, C> {
pub(crate) fn parse_class_statement(
&mut self,
start_span: u32,
stmt_ctx: StatementContext,
modifiers: &Modifiers,
decorators: Vec<'a, Decorator<'a>>,
) -> Statement<'a> {
let decl = self.parse_class_declaration(start_span, modifiers, decorators);
if stmt_ctx.is_single_statement() {
self.error(diagnostics::class_declaration(Span::new(
decl.span.start,
decl.body.span.start,
)));
}
Statement::ClassDeclaration(decl)
}
pub(crate) fn parse_class_declaration(
&mut self,
start_span: u32,
modifiers: &Modifiers,
decorators: Vec<'a, Decorator<'a>>,
) -> Box<'a, Class<'a>> {
self.parse_class(start_span, ClassType::ClassDeclaration, modifiers, decorators)
}
pub(crate) fn parse_class_expression(
&mut self,
span: u32,
modifiers: &Modifiers,
decorators: Vec<'a, Decorator<'a>>,
) -> Expression<'a> {
let class = self.parse_class(span, ClassType::ClassExpression, modifiers, decorators);
Expression::ClassExpression(class)
}
fn parse_class(
&mut self,
start_span: u32,
r#type: ClassType,
modifiers: &Modifiers,
decorators: Vec<'a, Decorator<'a>>,
) -> Box<'a, Class<'a>> {
self.bump_any();
let mut start_span = start_span;
if r#type == ClassType::ClassExpression
&& let Some(d) = decorators.first()
{
start_span = d.span.start;
}
let id = if self.cur_kind().is_binding_identifier() && !self.at(Kind::Implements) {
Some(self.parse_binding_identifier())
} else {
None
};
let type_parameters = if self.is_ts { self.parse_ts_type_parameters() } else { None };
let (extends, implements) = self.parse_heritage_clause();
let mut super_class = None;
let mut super_type_parameters = None;
if let Some(mut extends) = extends
&& !extends.is_empty()
{
let first_extends = extends.remove(0);
super_class = Some(first_extends.expression);
super_type_parameters = first_extends.type_arguments;
for extend in extends {
self.error(diagnostics::classes_can_only_extend_single_class(extend.span));
}
}
let body = self.parse_class_body();
self.verify_modifiers(
modifiers,
ModifierKinds::new([ModifierKind::Declare, ModifierKind::Abstract]),
true,
diagnostics::modifier_cannot_be_used_here,
);
self.ast.alloc_class(
self.end_span(start_span),
r#type,
decorators,
id,
type_parameters,
super_class,
super_type_parameters,
implements.map_or_else(|| self.ast.vec(), |(_, implements)| implements),
body,
modifiers.contains_abstract(),
modifiers.contains_declare(),
)
}
pub(crate) fn parse_heritage_clause(
&mut self,
) -> (Option<Vec<'a, TSInterfaceHeritage<'a>>>, Option<ImplementsWithKeywordSpan<'a>>) {
let mut extends = None;
let mut implements: Option<(Span, Vec<'a, TSClassImplements<'a>>)> = None;
loop {
match self.cur_kind() {
Kind::Extends => {
if extends.is_some() {
self.error(diagnostics::extends_clause_already_seen(
self.cur_token().span(),
));
} else if let Some((implements_span, _)) = implements {
self.error(diagnostics::extends_clause_must_precede_implements(
self.cur_token().span(),
implements_span,
));
}
extends = Some(self.parse_extends_clause());
}
Kind::Implements => {
if let Some((implements_span, _)) = implements {
self.error(diagnostics::implements_clause_already_seen(
self.cur_token().span(),
implements_span,
));
}
let implements_kw_span = self.cur_token().span();
if !self.is_ts {
self.error(diagnostics::implements_clause_in_ts(implements_kw_span));
}
if let Some((_, implements)) = implements.as_mut() {
implements.extend(self.parse_ts_implements_clause());
} else {
implements = Some((implements_kw_span, self.parse_ts_implements_clause()));
}
}
_ => break,
}
}
(extends, implements)
}
fn parse_extends_clause(&mut self) -> Vec<'a, TSInterfaceHeritage<'a>> {
self.bump_any();
let mut extends = self.ast.vec_with_capacity(1);
loop {
let span = self.start_span();
let mut extend = self.parse_lhs_expression_or_higher();
let type_argument;
if let Expression::TSInstantiationExpression(expr) = extend {
let expr = expr.unbox();
extend = expr.expression;
type_argument = Some(expr.type_arguments);
} else {
type_argument = self.try_parse_type_arguments();
}
extends.push(self.ast.ts_interface_heritage(
self.end_span(span),
extend,
type_argument,
));
if !self.eat(Kind::Comma) {
break;
}
}
extends
}
fn parse_class_body(&mut self) -> Box<'a, ClassBody<'a>> {
let span = self.start_span();
let class_elements = self.parse_normal_list_breakable(Kind::LCurly, Kind::RCurly, |p| {
if p.eat(Kind::Semicolon) {
while p.eat(Kind::Semicolon) {}
if p.at(Kind::RCurly) {
return None;
}
}
Some(Self::parse_class_element(p))
});
self.ast.alloc_class_body(self.end_span(span), class_elements)
}
fn parse_class_element(&mut self) -> ClassElement<'a> {
let elem = self.parse_class_element_impl();
if let ClassElement::MethodDefinition(def) = &elem
&& def.value.body.is_none()
&& !def.decorators.is_empty()
{
for decorator in &def.decorators {
self.error(diagnostics::decorator_on_overload(decorator.span));
}
}
elem
}
fn parse_class_element_impl(&mut self) -> ClassElement<'a> {
let span = self.start_span();
let decorators = self.parse_decorators();
let modifiers = self.parse_modifiers(
true,
true,
);
if self.at(Kind::Static) && self.lexer.peek_token().kind() == Kind::LCurly {
for decorator in decorators {
self.error(diagnostics::decorators_are_not_valid_here(decorator.span));
}
self.verify_modifiers(
&modifiers,
ModifierKinds::none(),
false,
diagnostics::modifiers_cannot_appear_here,
);
return self.parse_class_static_block(span);
}
self.verify_modifiers(
&modifiers,
ModifierKinds::all_except([ModifierKind::Export]),
false,
diagnostics::cannot_appear_on_class_elements,
);
let r#abstract = modifiers.contains(ModifierKind::Abstract);
let r#type = if r#abstract {
MethodDefinitionType::TSAbstractMethodDefinition
} else {
MethodDefinitionType::MethodDefinition
};
if self.parse_contextual_modifier(Kind::Get) {
return self.parse_accessor_declaration(
span,
r#type,
MethodDefinitionKind::Get,
&modifiers,
decorators,
);
}
if self.parse_contextual_modifier(Kind::Set) {
return self.parse_accessor_declaration(
span,
r#type,
MethodDefinitionKind::Set,
&modifiers,
decorators,
);
}
if matches!(self.cur_kind(), Kind::Constructor | Kind::Str)
&& !modifiers.contains(ModifierKind::Static)
&& let Some(name) = self.parse_constructor_name()
{
return self.parse_constructor_declaration(span, r#type, name, &modifiers, decorators);
}
if self.is_index_signature() {
for decorator in decorators {
self.error(diagnostics::decorators_are_not_valid_here(decorator.span));
}
self.verify_modifiers(
&modifiers,
ModifierKinds::new([ModifierKind::Readonly, ModifierKind::Static]),
true,
diagnostics::cannot_appear_on_an_index_signature,
);
return ClassElement::TSIndexSignature(
self.parse_index_signature_declaration(span, &modifiers),
);
}
let kind = self.cur_kind();
if kind.is_identifier_or_keyword() || kind == Kind::Star || kind == Kind::LBrack {
let is_ambient = modifiers.contains(ModifierKind::Declare);
return if is_ambient {
self.context_add(Context::Ambient, |p| {
p.parse_property_or_method_declaration(span, r#type, &modifiers, decorators)
})
} else {
self.parse_property_or_method_declaration(span, r#type, &modifiers, decorators)
};
}
self.unexpected()
}
fn parse_class_element_name(&mut self, modifiers: &Modifiers) -> (PropertyKey<'a>, bool) {
self.verify_modifiers(
modifiers,
ModifierKinds::all_except([ModifierKind::Const, ModifierKind::In, ModifierKind::Out]),
false,
|modifier, _| {
match modifier.kind {
ModifierKind::Const => diagnostics::const_class_member(modifier.span()),
ModifierKind::In | ModifierKind::Out => {
diagnostics::can_only_appear_on_a_type_parameter_of_a_class_interface_or_type_alias(modifier.kind, modifier.span())
}
_ => unreachable!(),
}
},
);
match self.cur_kind() {
Kind::PrivateIdentifier => {
let private_ident = self.parse_private_identifier();
if self.is_ts {
self.verify_modifiers(
modifiers,
ModifierKinds::all_except([
ModifierKind::Public,
ModifierKind::Private,
ModifierKind::Protected,
]),
false,
diagnostics::accessibility_modifier_on_private_property,
);
}
if private_ident.name == "constructor" {
self.error(diagnostics::private_name_constructor(private_ident.span));
}
(PropertyKey::PrivateIdentifier(self.alloc(private_ident)), false)
}
_ => self.parse_property_name(),
}
}
fn parse_class_static_block(&mut self, span: u32) -> ClassElement<'a> {
self.bump_any(); let block =
self.context(Context::Await, Context::Yield | Context::Return, Self::parse_block);
self.ast.class_element_static_block(self.end_span(span), block.unbox().body)
}
fn parse_class_accessor_property(
&mut self,
span: u32,
key: PropertyKey<'a>,
computed: bool,
definite: bool,
modifiers: &Modifiers,
decorators: Vec<'a, Decorator<'a>>,
) -> ClassElement<'a> {
let type_annotation = if self.is_ts { self.parse_ts_type_annotation() } else { None };
let value = self.eat(Kind::Eq).then(|| self.parse_assignment_expression_or_higher());
self.asi();
let r#type = if modifiers.contains(ModifierKind::Abstract) {
AccessorPropertyType::TSAbstractAccessorProperty
} else {
AccessorPropertyType::AccessorProperty
};
self.verify_modifiers(
modifiers,
ModifierKinds::new([
ModifierKind::Public,
ModifierKind::Private,
ModifierKind::Protected,
ModifierKind::Accessor,
ModifierKind::Static,
ModifierKind::Abstract,
ModifierKind::Override,
]),
true,
diagnostics::accessor_modifier,
);
self.ast.class_element_accessor_property(
self.end_span(span),
r#type,
decorators,
key,
type_annotation,
value,
computed,
modifiers.contains(ModifierKind::Static),
modifiers.contains(ModifierKind::Override),
definite,
modifiers.accessibility(),
)
}
fn parse_accessor_declaration(
&mut self,
span: u32,
r#type: MethodDefinitionType,
kind: MethodDefinitionKind,
modifiers: &Modifiers,
decorators: Vec<'a, Decorator<'a>>,
) -> ClassElement<'a> {
let (name, computed) = self.parse_class_element_name(modifiers);
let value = self.parse_method(
modifiers.contains(ModifierKind::Async),
false,
FunctionKind::ClassMethod,
);
let method_definition = self.ast.alloc_method_definition(
self.end_span(span),
r#type,
decorators,
name,
value,
kind,
computed,
modifiers.contains(ModifierKind::Static),
modifiers.contains(ModifierKind::Override),
false,
modifiers.accessibility(),
);
self.check_method_definition_accessor(&method_definition);
self.verify_modifiers(
modifiers,
ModifierKinds::all_except([ModifierKind::Async, ModifierKind::Declare]),
false,
diagnostics::modifier_cannot_be_used_here,
);
ClassElement::MethodDefinition(method_definition)
}
fn parse_constructor_declaration(
&mut self,
span: u32,
r#type: MethodDefinitionType,
name: PropertyKey<'a>,
modifiers: &Modifiers,
decorators: Vec<'a, Decorator<'a>>,
) -> ClassElement<'a> {
if let Some(modifier) = modifiers.get(ModifierKind::Declare) {
self.error(diagnostics::declare_constructor(modifier.span()));
}
let value = self.parse_method(
modifiers.contains(ModifierKind::Async),
false,
FunctionKind::Constructor,
);
let method_definition = self.ast.alloc_method_definition(
self.end_span(span),
r#type,
decorators,
name,
value,
MethodDefinitionKind::Constructor,
false,
modifiers.contains(ModifierKind::Static),
modifiers.contains(ModifierKind::Override),
false,
modifiers.accessibility(),
);
self.check_method_definition_constructor(&method_definition);
ClassElement::MethodDefinition(method_definition)
}
fn parse_constructor_name(&mut self) -> Option<PropertyKey<'a>> {
if self.at(Kind::Constructor) {
let ident = self.parse_identifier_name();
return Some(PropertyKey::StaticIdentifier(self.alloc(ident)));
}
if self.at(Kind::Str) && self.lexer.peek_token().kind() == Kind::LParen {
return self.try_parse(|p| {
let string_literal = p.parse_literal_string();
if string_literal.value != "constructor" {
return p.unexpected();
}
PropertyKey::StringLiteral(p.alloc(string_literal))
});
}
None
}
fn parse_property_or_method_declaration(
&mut self,
span: u32,
r#type: MethodDefinitionType,
modifiers: &Modifiers,
decorators: Vec<'a, Decorator<'a>>,
) -> ClassElement<'a> {
let generator = self.eat(Kind::Star);
let (name, computed) = self.parse_class_element_name(modifiers);
let cur_token = self.cur_token();
let optional_span = (cur_token.kind() == Kind::Question).then(|| {
let span = cur_token.span();
self.bump_any();
span
});
let optional = optional_span.is_some();
if generator || matches!(self.cur_kind(), Kind::LParen | Kind::LAngle) {
self.verify_modifiers(
modifiers,
ModifierKinds::all_except([ModifierKind::Declare, ModifierKind::Readonly]),
false,
|modifier, _| {
const ALLOWED: ModifierKinds = ModifierKinds::new([
ModifierKind::Public,
ModifierKind::Private,
ModifierKind::Protected,
ModifierKind::Static,
ModifierKind::Abstract,
ModifierKind::Override,
ModifierKind::Async,
]);
match modifier.kind {
ModifierKind::Declare => {
diagnostics::cannot_appear_on_class_elements(modifier, Some(ALLOWED))
}
ModifierKind::Readonly => {
diagnostics::modifier_only_on_property_declaration_or_index_signature(
modifier,
Some(ALLOWED),
)
}
_ => unreachable!(),
}
},
);
return self.parse_method_declaration(
span, r#type, generator, name, computed, optional, modifiers, decorators,
);
}
let definite = self.eat(Kind::Bang);
if definite && let Some(optional_span) = optional_span {
self.error(diagnostics::optional_definite_property(optional_span.expand_right(1)));
}
if modifiers.contains(ModifierKind::Accessor) {
if let Some(optional_span) = optional_span {
self.error(diagnostics::optional_accessor_property(optional_span));
}
if name.is_specific_string_literal("constructor") && !computed {
self.error(diagnostics::constructor_accessor(name.span()));
}
return self.parse_class_accessor_property(
span, name, computed, definite, modifiers, decorators,
);
}
self.parse_property_declaration(
span,
name,
computed,
optional_span,
definite,
modifiers,
decorators,
)
}
fn parse_method_declaration(
&mut self,
span: u32,
r#type: MethodDefinitionType,
generator: bool,
name: PropertyKey<'a>,
computed: bool,
optional: bool,
modifiers: &Modifiers,
decorators: Vec<'a, Decorator<'a>>,
) -> ClassElement<'a> {
let value = self.parse_method(
modifiers.contains(ModifierKind::Async),
generator,
FunctionKind::ClassMethod,
);
let method_definition = self.ast.alloc_method_definition(
self.end_span(span),
r#type,
decorators,
name,
value,
MethodDefinitionKind::Method,
computed,
modifiers.contains(ModifierKind::Static),
modifiers.contains(ModifierKind::Override),
optional,
modifiers.accessibility(),
);
self.check_method_definition_method(&method_definition);
ClassElement::MethodDefinition(method_definition)
}
fn parse_property_declaration(
&mut self,
span: u32,
name: PropertyKey<'a>,
computed: bool,
optional_span: Option<Span>,
definite: bool,
modifiers: &Modifiers,
decorators: Vec<'a, Decorator<'a>>,
) -> ClassElement<'a> {
let type_annotation = if self.is_ts { self.parse_ts_type_annotation() } else { None };
let initializer = self
.eat(Kind::Eq)
.then(|| self.context(Context::In, Context::Yield | Context::Await, Self::parse_expr));
let cur_token = self.cur_token();
if cur_token.kind() == Kind::Semicolon {
self.bump_any();
} else if !self.can_insert_semicolon() {
let error = diagnostics::expect_token(";", cur_token.kind().to_str(), cur_token.span());
return self.fatal_error(error);
}
let r#abstract = modifiers.contains(ModifierKind::Abstract);
let r#type = if r#abstract {
PropertyDefinitionType::TSAbstractPropertyDefinition
} else {
PropertyDefinitionType::PropertyDefinition
};
let r#static = modifiers.contains(ModifierKind::Static);
if !computed && let Some((name, span)) = name.prop_name() {
if name == "constructor" {
self.error(diagnostics::field_constructor(span));
}
if r#static && name == "prototype" && !self.ctx.has_ambient() {
self.error(diagnostics::static_prototype(span));
}
}
if r#abstract && initializer.is_some() {
let (name, span) = name.prop_name().unwrap_or_else(|| {
let span = name.span();
(&self.source_text[span], span)
});
self.error(diagnostics::abstract_property_cannot_have_initializer(name, span));
}
if self.ctx.has_ambient()
&& let Some(initializer) = &initializer
&& !(modifiers.contains(ModifierKind::Readonly) && type_annotation.is_none())
{
self.error(diagnostics::initializers_not_allowed_in_ambient_contexts(
initializer.span(),
));
}
self.ast.class_element_property_definition(
self.end_span(span),
r#type,
decorators,
name,
type_annotation,
initializer,
computed,
r#static,
modifiers.contains(ModifierKind::Declare),
modifiers.contains(ModifierKind::Override),
optional_span.is_some(),
definite,
modifiers.contains(ModifierKind::Readonly),
modifiers.accessibility(),
)
}
#[cold]
pub(crate) fn check_getter(&mut self, function: &Function<'a>) {
if !function.params.items.is_empty() {
self.error(diagnostics::getter_parameters(function.params.span));
}
}
#[cold]
pub(crate) fn check_setter(&mut self, function: &Function<'a>) {
if let Some(rest) = &function.params.rest {
self.error(diagnostics::setter_with_rest_parameter(rest.span));
} else if function.params.parameters_count() != 1 {
self.error(diagnostics::setter_with_parameters(
function.params.span,
function.params.parameters_count(),
));
} else if self.is_ts && function.params.items.first().unwrap().initializer.is_some() {
self.error(diagnostics::setter_with_initializer(function.params.span));
}
}
fn check_method_definition(&mut self, method: &MethodDefinition<'a>) {
if !method.computed
&& let Some((name, span)) = method.key.prop_name()
{
if method.r#static {
if name == "prototype" && !self.ctx.has_ambient() {
self.error(diagnostics::static_prototype(span));
}
} else if name == "constructor" {
if matches!(method.kind, MethodDefinitionKind::Get | MethodDefinitionKind::Set) {
self.error(diagnostics::constructor_getter_setter(span));
}
if method.value.r#async {
self.error(diagnostics::constructor_async(span));
}
if method.value.generator {
self.error(diagnostics::constructor_generator(span));
}
}
}
if self.ctx.has_ambient()
&& let Some(body) = &method.value.body
{
self.error(diagnostics::implementation_in_ambient(Span::empty(body.span.start)));
}
}
fn check_method_definition_accessor(&mut self, method: &MethodDefinition<'a>) {
self.check_method_definition(method);
match method.kind {
MethodDefinitionKind::Get => self.check_getter(&method.value),
MethodDefinitionKind::Set => self.check_setter(&method.value),
_ => {}
}
if method.r#type.is_abstract() && method.value.body.is_some() {
let (name, span) = method.key.prop_name().unwrap_or_else(|| {
let span = method.key.span();
(&self.source_text[span], span)
});
self.error(diagnostics::abstract_accessor_cannot_have_implementation(name, span));
}
}
fn check_method_definition_method(&mut self, method: &MethodDefinition<'a>) {
self.check_method_definition(method);
if method.r#type.is_abstract() && method.value.body.is_some() {
let (name, span) = method.key.prop_name().unwrap_or_else(|| {
let span = method.key.span();
(&self.source_text[span], span)
});
self.error(diagnostics::abstract_method_cannot_have_implementation(name, span));
}
}
fn check_method_definition_constructor(&mut self, method: &MethodDefinition<'a>) {
self.check_method_definition(method);
if let Some(this_param) = &method.value.this_param {
self.error(diagnostics::ts_constructor_this_parameter(this_param.span));
}
if let Some(type_sig) = &method.value.type_parameters {
self.error(diagnostics::ts_constructor_type_parameter(type_sig.span));
}
if method.value.body.is_some()
&& let Some(return_type) = &method.value.return_type
{
self.error(diagnostics::constructor_return_type(return_type.span));
}
}
}