use crate::parser::{ParsedSyntax, RecoveryResult};
use crate::prelude::*;
use crate::state::{
EnableStrictMode, EnterClassPropertyInitializer, EnterClassStaticInitializationBlock,
EnterParameters, SignatureFlags,
};
use crate::syntax::binding::parse_binding;
use crate::syntax::expr::{
parse_assignment_expression_or_higher, parse_lhs_expr, parse_private_name, ExpressionContext,
};
use crate::syntax::function::{
parse_any_parameter, parse_formal_parameter, parse_function_body, parse_parameter_list,
parse_parameters_list, parse_ts_type_annotation_or_error, ParameterContext,
};
use crate::syntax::js_parse_error;
use crate::syntax::js_parse_error::{
decorator_must_precede_modifier, decorators_not_allowed, expected_binding, expected_expression,
invalid_decorator_error, modifier_already_seen, modifier_cannot_be_used_with_modifier,
modifier_must_precede_modifier, parameter_decorators_not_allowed,
};
use crate::syntax::object::{
is_at_literal_member_name, parse_computed_member_name, parse_literal_member_name,
};
use crate::syntax::stmt::{optional_semi, parse_statements, StatementContext};
use crate::syntax::typescript::ts_parse_error::{
ts_accessibility_modifier_already_seen, ts_accessor_type_parameters_error,
ts_constructor_type_parameters_error, ts_modifier_cannot_appear_on_a_constructor_declaration,
ts_modifier_cannot_appear_on_a_parameter, ts_only_syntax_error,
ts_set_accessor_return_type_error,
};
use crate::syntax::typescript::{
is_reserved_type_name, parse_ts_implements_clause, parse_ts_return_type_annotation,
parse_ts_type_annotation, parse_ts_type_arguments, parse_ts_type_parameters, TypeContext,
};
use crate::JsSyntaxFeature::TypeScript;
use crate::ParsedSyntax::{Absent, Present};
use crate::{JsParser, StrictMode};
use biome_js_syntax::JsSyntaxKind::*;
use biome_js_syntax::TextSize;
use biome_js_syntax::{JsSyntaxKind, T};
use biome_parser::parse_lists::ParseNodeList;
use biome_parser::parse_recovery::ParseRecoveryTokenSet;
use biome_parser::ParserProgress;
use biome_rowan::{SyntaxKind, TextRange};
use bitflags::bitflags;
use drop_bomb::DebugDropBomb;
use smallvec::SmallVec;
use std::fmt::Debug;
use std::ops::Add;
use std::slice::Iter;
use super::function::LineBreak;
use super::js_parse_error::unexpected_body_inside_ambient_context;
use super::typescript::ts_parse_error::{self, unexpected_abstract_member_with_body};
use super::typescript::{
expect_ts_index_signature_member, is_at_ts_index_signature_member, MemberParent,
};
pub(crate) fn is_at_ts_abstract_class_declaration(
p: &mut JsParser,
should_check_line_break: LineBreak,
) -> bool {
let is_abstract = p.at(T![abstract]) && p.nth_at(1, T![class]);
if should_check_line_break == LineBreak::DoCheck {
is_abstract && !p.has_nth_preceding_line_break(1)
} else {
is_abstract
}
}
pub(crate) fn is_at_export_class_declaration(p: &mut JsParser) -> bool {
p.at(T![export]) && (p.nth_at(1, T![class]) || p.nth_at(1, T![@]) || p.nth_at(1, T![abstract]))
}
pub(crate) fn is_at_export_default_class_declaration(p: &mut JsParser) -> bool {
p.at(T![export])
&& p.nth_at(1, T![default])
&& (p.nth_at(2, T![class]) || p.nth_at(2, T![@]) || p.nth_at(2, T![abstract]))
}
pub(super) fn parse_class_expression(
p: &mut JsParser,
decorator_list: ParsedSyntax,
) -> ParsedSyntax {
if !p.at(T![class]) {
return Absent;
}
Present(parse_class(p, ClassKind::Expression, decorator_list))
}
pub(super) fn parse_class_declaration(
p: &mut JsParser,
decorator_list: ParsedSyntax,
context: StatementContext,
) -> ParsedSyntax {
if !matches!(p.cur(), T![abstract] | T![class]) {
return Absent;
}
let mut class = parse_class(p, ClassKind::Declaration, decorator_list);
if !class.kind(p).is_bogus() && context.is_single_statement() {
p.error(
p.err_builder(
"Classes can only be declared at top level or inside a block",
class.range(p),
)
.with_hint("wrap the class in a block statement"),
);
class.change_to_bogus(p)
}
Present(class)
}
pub(super) fn parse_class_export_default_declaration(
p: &mut JsParser,
decorator_list: ParsedSyntax,
) -> ParsedSyntax {
if !matches!(p.cur(), T![abstract] | T![class]) {
return Absent;
}
Present(parse_class(p, ClassKind::ExportDefault, decorator_list))
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
enum ClassKind {
Declaration,
Expression,
ExportDefault,
}
impl ClassKind {
fn is_id_optional(&self) -> bool {
matches!(self, ClassKind::Expression | ClassKind::ExportDefault)
}
}
impl From<ClassKind> for JsSyntaxKind {
fn from(kind: ClassKind) -> Self {
match kind {
ClassKind::Declaration => JS_CLASS_DECLARATION,
ClassKind::Expression => JS_CLASS_EXPRESSION,
ClassKind::ExportDefault => JS_CLASS_EXPORT_DEFAULT_DECLARATION,
}
}
}
#[inline]
fn parse_class(p: &mut JsParser, kind: ClassKind, decorator_list: ParsedSyntax) -> CompletedMarker {
let decorator_list = decorator_list.or_else(|| empty_decorator_list(p));
let m = decorator_list.precede(p);
let is_abstract = p.eat(T![abstract]);
let class_token_range = p.cur_range();
p.expect(T![class]);
let p = &mut *p.with_scoped_state(EnableStrictMode(StrictMode::Class(p.cur_range())));
let id = match p.cur() {
T![implements] if TypeScript.is_supported(p) => Absent,
T![extends] => Absent,
_ => parse_binding(p),
};
match id {
Present(id) => {
let text = p.text(id.range(p));
if TypeScript.is_supported(p) && is_reserved_type_name(text) {
let err = p
.err_builder(format!(
"`{}` cannot be used as a class name because it is already reserved as a type",
text
),id.range(p), );
p.error(err);
}
}
Absent => {
if !kind.is_id_optional() {
let err = p.err_builder(
"class declarations must have a name",
class_token_range.start()..p.cur_range().start(),
);
p.error(err);
}
}
}
TypeScript
.parse_exclusive_syntax(
p,
|p| {
parse_ts_type_parameters(
p,
TypeContext::default()
.and_allow_in_out_modifier(true)
.and_allow_const_modifier(true),
)
},
|p, type_parameters| {
ts_only_syntax_error(p, "class type parameters", type_parameters.range(p))
},
)
.ok();
eat_class_heritage_clause(p);
p.expect(T!['{']);
ClassMembersList {
inside_abstract_class: is_abstract,
}
.parse_list(p);
p.expect(T!['}']);
m.complete(p, kind.into())
}
fn eat_class_heritage_clause(p: &mut JsParser) {
let mut first_extends: Option<CompletedMarker> = None;
let mut first_implements: Option<CompletedMarker> = None;
loop {
match p.cur() {
T![extends] => {
let current = parse_extends_clause(p).expect(
"Expected extends clause because parser is positioned at extends keyword",
);
match first_extends.as_ref() {
None => {
first_extends = {
if let Some(first_implements) = first_implements.as_ref() {
p.error(
p.err_builder(
"'extends' clause must precede 'implements' clause.",
current.range(p),
)
.with_detail(
first_implements.range(p),
"This is where implements was found",
),
)
}
Some(current)
}
}
Some(first_extends) => p.error(
p.err_builder("'extends' clause already seen.", current.range(p))
.with_detail(first_extends.range(p), "first 'extends' clause"),
),
}
}
T![implements] => {
let mut current = parse_ts_implements_clause(p).expect("expected 'implements' clause because parser is positioned at 'implements' keyword.");
match first_implements.as_ref() {
None => {
first_implements = {
if TypeScript.is_unsupported(p) {
p.error(p.err_builder(
"classes can only implement interfaces in TypeScript files",
current.range(p),
));
current.change_to_bogus(p);
}
Some(current)
}
}
Some(first_implements) => {
p.error(
p.err_builder("'implements' clause already seen.", current.range(p))
.with_detail(
first_implements.range(p),
"first 'implements' clause",
),
);
}
}
}
_ => break,
}
}
}
fn parse_extends_clause(p: &mut JsParser) -> ParsedSyntax {
if !p.at(T![extends]) {
return Absent;
}
let m = p.start();
let extends_end = p.cur_range().end();
p.expect(T![extends]);
if parse_extends_expression(p).is_absent() {
p.error(p.err_builder("'extends' list cannot be empty.", extends_end..extends_end))
} else {
TypeScript
.parse_exclusive_syntax(
p,
|p| parse_ts_type_arguments(p, TypeContext::default()),
|p, arguments| ts_only_syntax_error(p, "type arguments", arguments.range(p)),
)
.ok();
}
while p.at(T![,]) {
let comma_range = p.cur_range();
p.bump(T![,]);
let extra = p.start();
if parse_extends_expression(p).is_absent() {
p.error(p.err_builder("Trailing comma not allowed.", comma_range));
extra.abandon(p);
break;
}
parse_ts_type_arguments(p, TypeContext::default()).ok();
let extra_class = extra.complete(p, JS_BOGUS);
p.error(p.err_builder(
"Classes can only extend a single class.",
extra_class.range(p),
));
}
Present(m.complete(p, JS_EXTENDS_CLAUSE))
}
fn parse_extends_expression(p: &mut JsParser) -> ParsedSyntax {
if p.at(T!['{']) && p.nth_at(1, T!['}']) {
if !matches!(p.nth(2), T![extends] | T![implements] | T!['{'] | T![,]) {
return Absent;
}
}
parse_lhs_expr(p, ExpressionContext::default())
}
struct ClassMembersList {
inside_abstract_class: bool,
}
impl ParseNodeList for ClassMembersList {
type Kind = JsSyntaxKind;
type Parser<'source> = JsParser<'source>;
const LIST_KIND: JsSyntaxKind = JS_CLASS_MEMBER_LIST;
fn parse_element(&mut self, p: &mut JsParser) -> ParsedSyntax {
parse_class_member(p, self.inside_abstract_class)
}
fn is_at_list_end(&self, p: &mut JsParser) -> bool {
p.at(T!['}'])
}
fn recover(&mut self, p: &mut JsParser, parsed_element: ParsedSyntax) -> RecoveryResult {
parsed_element.or_recover_with_token_set(
p,
&ParseRecoveryTokenSet::new(
JS_BOGUS_MEMBER,
token_set![
T![;],
T![ident],
T![readonly],
T![private],
T![protected],
T![public],
T![override],
T![declare],
T![static],
T![accessor],
T![async],
T![yield],
T!['}'],
T![#],
T![@],
],
),
js_parse_error::expected_class_member,
)
}
}
fn parse_class_member(p: &mut JsParser, inside_abstract_class: bool) -> ParsedSyntax {
let member_marker = p.start();
if p.eat(T![;]) {
return Present(member_marker.complete(p, JS_EMPTY_CLASS_MEMBER));
}
let mut modifiers = parse_class_member_modifiers(p, false);
if is_at_static_initialization_block_class_member(p) {
return Present(parse_static_initialization_block_class_member(
p,
member_marker,
modifiers,
));
}
let member = parse_class_member_impl(p, member_marker, &mut modifiers);
match member {
Present(mut member) => {
let mut valid = true;
if !inside_abstract_class {
if let Some(abstract_token_range) =
modifiers.get_first_range(ModifierKind::Abstract)
{
let err = p.err_builder(
"Only abstract classes can have abstract members",
abstract_token_range,
);
p.error(err);
valid = false;
}
}
let modifiers_valid = modifiers.validate_and_complete(p, member.kind(p));
if !valid || !modifiers_valid {
member.change_to_bogus(p);
}
Present(member)
}
Absent => {
debug_assert!(!modifiers
.flags
.contains(ModifierFlags::ALL_MODIFIERS_EXCEPT_DECORATOR));
modifiers.abandon(p);
Absent
}
}
}
fn parse_index_signature_class_member(p: &mut JsParser, member_marker: Marker) -> ParsedSyntax {
TypeScript.parse_exclusive_syntax(
p,
|p| {
Present(expect_ts_index_signature_member(
p,
member_marker,
MemberParent::Class,
TypeContext::default(),
))
},
|p, member| ts_only_syntax_error(p, "Index signatures", member.range(p)),
)
}
fn parse_class_member_impl(
p: &mut JsParser,
member_marker: Marker,
modifiers: &mut ClassMemberModifiers,
) -> ParsedSyntax {
let start_token_pos = p.source().position();
let generator_range = p.cur_range();
if p.at(T![*]) {
p.bump_any(); if is_at_constructor(p, modifiers) {
let err = p.err_builder("constructors can't be generators", generator_range);
p.error(err);
}
return Present(parse_method_class_member(
p,
member_marker,
modifiers,
SignatureFlags::GENERATOR,
));
};
if p.at(T![async])
&& !p.nth_at(1, T![?])
&& !p.nth_at(1, T![;])
&& !p.nth_at(1, T![=])
&& !is_at_method_class_member(p, 1)
&& !p.has_nth_preceding_line_break(1)
{
let async_range = p.cur_range();
p.expect(T![async]);
let mut flags = SignatureFlags::ASYNC;
if p.eat(T![*]) {
flags |= SignatureFlags::GENERATOR;
}
return Present(if is_at_constructor(p, modifiers) {
let err = p.err_builder("constructors cannot be async", async_range);
p.error(err);
parse_class_member_name(p, modifiers).unwrap();
parse_constructor_class_member_body(p, member_marker, modifiers)
} else {
parse_method_class_member(p, member_marker, modifiers, flags)
});
}
if is_at_ts_index_signature_member(p) {
return parse_index_signature_class_member(p, member_marker);
}
if matches!(p.cur(), T![get] | T![set]) && is_at_class_member_name(p, 1) {
let is_getter = p.at(T![get]);
if is_getter {
p.expect(T![get]);
} else {
p.expect(T![set]);
}
parse_class_member_name(p, modifiers)
.or_add_diagnostic(p, js_parse_error::expected_class_member_name);
if let Present(type_parameters) = parse_ts_type_parameters(p, TypeContext::default()) {
p.error(ts_accessor_type_parameters_error(p, &type_parameters))
}
let completed = if is_getter {
p.expect(T!['(']);
p.expect(T![')']);
parse_ts_type_annotation_or_error(p).ok();
let member_kind = expect_accessor_body(p, &member_marker, modifiers);
member_marker.complete(p, member_kind.as_getter_syntax_kind())
} else {
let has_l_paren = p.expect(T!['(']);
p.with_state(EnterParameters(SignatureFlags::empty()), |p| {
let decorator_list = parse_parameter_decorators(p);
parse_formal_parameter(
p,
decorator_list,
ParameterContext::ClassSetter,
ExpressionContext::default().and_object_expression_allowed(has_l_paren),
TypeContext::default(),
)
})
.or_add_diagnostic(p, js_parse_error::expected_parameter);
p.expect(T![')']);
if let Present(return_type_annotation) =
parse_ts_return_type_annotation(p, TypeContext::default())
{
p.error(ts_set_accessor_return_type_error(
p,
&return_type_annotation,
));
}
let member_kind = expect_accessor_body(p, &member_marker, modifiers);
member_marker.complete(p, member_kind.as_setter_syntax_kind())
};
return Present(completed);
}
let is_constructor = is_at_constructor(p, modifiers);
let member_name = parse_class_member_name(p, modifiers)
.or_add_diagnostic(p, js_parse_error::expected_class_member_name);
if is_at_method_class_member(p, 0) {
return if is_constructor {
Present(parse_constructor_class_member_body(
p,
member_marker,
modifiers,
))
} else {
Present(parse_method_class_member_rest(
p,
member_marker,
modifiers,
SignatureFlags::empty(),
))
};
}
match member_name {
Some(_) => {
let mut property = parse_property_class_member_body(p, member_marker, modifiers);
if !property.kind(p).is_bogus() && is_constructor {
let err = p.err_builder(
"class properties may not be called `constructor`",
property.range(p),
);
p.error(err);
property.change_to_bogus(p);
}
Present(property)
}
None => {
debug_assert_eq!(
p.source().position(),
start_token_pos,
"Parser shouldn't be progressing when returning Absent"
);
member_marker.abandon(p);
Absent
}
}
}
fn is_at_static_initialization_block_class_member(p: &mut JsParser) -> bool {
p.at(T![static]) && p.nth_at(1, T!['{'])
}
fn parse_static_initialization_block_class_member(
p: &mut JsParser,
member_marker: Marker,
modifiers: ClassMemberModifiers,
) -> CompletedMarker {
if modifiers.is_empty() {
modifiers.abandon(p);
} else {
p.error(p.err_builder(
"Static class blocks cannot have any modifier.",
modifiers.list_marker.range(p),
));
modifiers.validate_and_complete(p, JS_STATIC_INITIALIZATION_BLOCK_CLASS_MEMBER);
}
p.expect(T![static]);
p.expect(T!['{']);
p.with_state(EnterClassStaticInitializationBlock, |p| {
let statement_list = p.start();
parse_statements(p, true, statement_list)
});
p.expect(T!['}']);
member_marker.complete(p, JS_STATIC_INITIALIZATION_BLOCK_CLASS_MEMBER)
}
fn parse_property_class_member_body(
p: &mut JsParser,
member_marker: Marker,
modifiers: &ClassMemberModifiers,
) -> CompletedMarker {
let annotation = parse_ts_property_annotation(p, modifiers).ok();
let initializer_syntax = p.with_state(EnterClassPropertyInitializer, |p| {
parse_initializer_clause(p, ExpressionContext::default())
});
expect_member_semi(p, &member_marker, "class property");
let is_signature = modifiers.is_signature() || p.state().in_ambient_context();
let kind = if !is_signature {
JS_PROPERTY_CLASS_MEMBER
} else if initializer_syntax.is_present() {
TS_INITIALIZED_PROPERTY_SIGNATURE_CLASS_MEMBER
} else {
TS_PROPERTY_SIGNATURE_CLASS_MEMBER
};
let member = member_marker.complete(p, kind);
if let Present(initializer) = &initializer_syntax {
if modifiers.has(ModifierKind::Abstract) {
p.error(p.err_builder(
"Property cannot have an initializer because it is marked abstract.",
initializer.range(p),
));
} else if modifiers.has(ModifierKind::Declare) || p.state().in_ambient_context() {
if !modifiers.has(ModifierKind::Readonly) {
p.error(p.err_builder(
"In ambient contexts, properties with initializers need to be readonly.",
initializer.range(p),
));
} else if let Some(annotation) = annotation {
p.error(p.err_builder(
"In ambient contexts, properties cannot have both a type annotation and an initializer.",
initializer.range(p),
).with_detail(annotation.range(p), "The type annotation is here:"));
}
}
}
member
}
fn expect_member_semi(p: &mut JsParser, member_marker: &Marker, name: &str) {
if !optional_semi(p) {
let end = p.last_end().unwrap_or_else(|| p.cur_range().start());
let err = p.err_builder(
format!("expected a semicolon to end the {name}, but found none"),
member_marker.start()..end,
);
p.error(err);
}
}
fn parse_ts_property_annotation(
p: &mut JsParser,
modifiers: &ClassMemberModifiers,
) -> ParsedSyntax {
if !p.at(T![?]) && !p.at(T![!]) {
return parse_ts_type_annotation_or_error(p);
}
let m = p.start();
let mut valid = true;
let optional_range = match optional_member_token(p) {
Ok(optional_range) => optional_range,
Err(optional_range) => {
valid = false;
Some(optional_range)
}
};
let definite_range = if p.at(T![!]) {
let range = p.cur_range();
p.bump(T![!]);
if TypeScript.is_unsupported(p) {
let error = p.err_builder("`!` modifiers can only be used in TypeScript files", range);
p.error(error);
valid = false;
}
else if modifiers.has(ModifierKind::Abstract) {
p.error(p.err_builder(
"A definite assignment operator '!' cannot appear on an 'abstract' property.",
range,
));
valid = false;
} else if modifiers.has(ModifierKind::Declare) || p.state().in_ambient_context() {
p.error(p.err_builder(
"Definite assignment operators '!' aren't allowed in ambient contexts.",
range,
));
}
Some(range)
} else {
None
};
let mut annotation = match (optional_range, definite_range) {
(Some(_), None) => {
parse_ts_type_annotation(p, TypeContext::default()).ok();
m.complete(p, TS_OPTIONAL_PROPERTY_ANNOTATION)
}
(None, Some(_)) => {
parse_ts_type_annotation(p, TypeContext::default()).or_add_diagnostic(p, |p, range| {
p.err_builder("Properties with definite assignment assertions must also have type annotations.",range, )
});
m.complete(p, TS_DEFINITE_PROPERTY_ANNOTATION)
}
(Some(optional_range), Some(definite_range)) => {
parse_ts_type_annotation(p, TypeContext::default()).ok();
let error = p
.err_builder(
"class properties cannot be both optional and definite",
definite_range,
)
.with_detail(definite_range, "The definite")
.with_detail(optional_range, "The optional");
p.error(error);
m.complete(p, JS_BOGUS)
}
(None, None) => unreachable!(),
};
if !valid {
annotation.change_to_bogus(p);
}
Present(annotation)
}
fn optional_member_token(p: &mut JsParser) -> Result<Option<TextRange>, TextRange> {
if p.at(T![?]) {
let range = p.cur_range();
p.bump(T![?]);
if TypeScript.is_supported(p) {
Ok(Some(range))
} else {
let err = p.err_builder("`?` modifiers can only be used in TypeScript files", range);
p.error(err);
Err(range)
}
} else {
Ok(None)
}
}
pub(crate) fn parse_initializer_clause(
p: &mut JsParser,
context: ExpressionContext,
) -> ParsedSyntax {
if p.at(T![=]) {
let m = p.start();
p.bump(T![=]);
parse_assignment_expression_or_higher(p, context)
.or_add_diagnostic(p, js_parse_error::expected_expression_assignment);
Present(m.complete(p, JS_INITIALIZER_CLAUSE))
} else {
Absent
}
}
fn parse_method_class_member(
p: &mut JsParser,
m: Marker,
modifiers: &mut ClassMemberModifiers,
flags: SignatureFlags,
) -> CompletedMarker {
parse_class_member_name(p, modifiers)
.or_add_diagnostic(p, js_parse_error::expected_class_member_name);
parse_method_class_member_rest(p, m, modifiers, flags)
}
fn parse_method_class_member_rest(
p: &mut JsParser,
m: Marker,
modifiers: &ClassMemberModifiers,
flags: SignatureFlags,
) -> CompletedMarker {
let optional = optional_member_token(p);
TypeScript
.parse_exclusive_syntax(
p,
|p| parse_ts_type_parameters(p, TypeContext::default().and_allow_const_modifier(true)),
|p, marker| ts_only_syntax_error(p, "type parameters", marker.range(p)),
)
.ok();
let parameter_context = if modifiers.is_signature() {
ParameterContext::Declaration
} else {
ParameterContext::ClassImplementation
};
parse_parameter_list(p, parameter_context, TypeContext::default(), flags)
.or_add_diagnostic(p, js_parse_error::expected_class_parameters);
TypeScript
.parse_exclusive_syntax(
p,
|p| parse_ts_return_type_annotation(p, TypeContext::default()),
|p, annotation| ts_only_syntax_error(p, "return type annotation", annotation.range(p)),
)
.ok();
let member_kind = expect_method_body(p, &m, modifiers, ClassMethodMemberKind::Method(flags));
let mut member = m.complete(p, member_kind.as_method_syntax_kind());
let is_async = flags.contains(SignatureFlags::ASYNC);
if modifiers.has(ModifierKind::Abstract) && is_async {
let err = ts_parse_error::abstract_member_cannot_be_async(
p,
&modifiers.get_first_range_unchecked(ModifierKind::Abstract),
);
p.error(err);
member.change_to_bogus(p);
} else if flags.contains(SignatureFlags::GENERATOR) && member_kind.is_signature() {
p.error(p.err_builder(
"A method signature cannot be declared as a generator.",
member.range(p),
));
} else if p.state().in_ambient_context() && is_async {
p.error(p.err_builder(
"'async' modifier cannot be used in an ambient context.",
member.range(p),
));
member.change_to_bogus(p);
} else if optional.is_err() {
member.change_to_bogus(p);
}
member
}
#[derive(Debug)]
enum MemberKind {
Signature,
Declaration,
}
impl MemberKind {
const fn is_signature(&self) -> bool {
matches!(self, MemberKind::Signature)
}
const fn as_method_syntax_kind(&self) -> JsSyntaxKind {
match self {
MemberKind::Signature => TS_METHOD_SIGNATURE_CLASS_MEMBER,
MemberKind::Declaration => JS_METHOD_CLASS_MEMBER,
}
}
const fn as_constructor_syntax_kind(&self) -> JsSyntaxKind {
match self {
MemberKind::Signature => TS_CONSTRUCTOR_SIGNATURE_CLASS_MEMBER,
MemberKind::Declaration => JS_CONSTRUCTOR_CLASS_MEMBER,
}
}
const fn as_setter_syntax_kind(&self) -> JsSyntaxKind {
match self {
MemberKind::Signature => TS_SETTER_SIGNATURE_CLASS_MEMBER,
MemberKind::Declaration => JS_SETTER_CLASS_MEMBER,
}
}
const fn as_getter_syntax_kind(&self) -> JsSyntaxKind {
match self {
MemberKind::Signature => TS_GETTER_SIGNATURE_CLASS_MEMBER,
MemberKind::Declaration => JS_GETTER_CLASS_MEMBER,
}
}
}
#[derive(Debug)]
enum ClassMethodMemberKind {
Accessor,
Constructor,
Method(SignatureFlags),
}
impl ClassMethodMemberKind {
const fn is_body_optional(&self) -> bool {
matches!(
self,
ClassMethodMemberKind::Method(_) | ClassMethodMemberKind::Constructor
)
}
const fn is_constructor(&self) -> bool {
matches!(self, ClassMethodMemberKind::Constructor)
}
const fn signature_flags(&self) -> SignatureFlags {
match self {
ClassMethodMemberKind::Method(flags) => *flags,
ClassMethodMemberKind::Constructor => SignatureFlags::CONSTRUCTOR,
ClassMethodMemberKind::Accessor => SignatureFlags::empty(),
}
}
}
fn expect_method_body(
p: &mut JsParser,
member_marker: &Marker,
modifiers: &ClassMemberModifiers,
method_kind: ClassMethodMemberKind,
) -> MemberKind {
let body = parse_function_body(p, method_kind.signature_flags());
if p.state().in_ambient_context() {
match body {
Present(body) => p.error(unexpected_body_inside_ambient_context(p, body.range(p))),
Absent => {
expect_member_semi(p, member_marker, "method declaration")
}
}
MemberKind::Signature
}
else if modifiers.has(ModifierKind::Abstract) && !method_kind.is_constructor() {
match body {
Present(body) => p.error(unexpected_abstract_member_with_body(p, body.range(p))),
Absent => {
expect_member_semi(p, member_marker, "method declaration")
}
}
MemberKind::Signature
}
else if method_kind.is_body_optional()
&& TypeScript.is_supported(p)
&& body.is_absent()
&& optional_semi(p)
{
MemberKind::Signature
} else {
body.or_add_diagnostic(p, js_parse_error::expected_class_method_body);
MemberKind::Declaration
}
}
fn expect_accessor_body(
p: &mut JsParser,
member_marker: &Marker,
modifiers: &ClassMemberModifiers,
) -> MemberKind {
expect_method_body(p, member_marker, modifiers, ClassMethodMemberKind::Accessor)
}
fn parse_constructor_class_member_body(
p: &mut JsParser,
member_marker: Marker,
modifiers: &ClassMemberModifiers,
) -> CompletedMarker {
if let Ok(Some(range)) = optional_member_token(p) {
let err = p.err_builder("constructors cannot be optional", range);
p.error(err);
}
if let Present(type_parameters) = parse_ts_type_parameters(p, TypeContext::default()) {
p.error(ts_constructor_type_parameters_error(p, &type_parameters));
}
parse_constructor_parameter_list(p)
.or_add_diagnostic(p, js_parse_error::expected_constructor_parameters);
if let Present(marker) = parse_ts_type_annotation(p, TypeContext::default()) {
let err = p.err_builder("constructors cannot have type annotations", marker.range(p));
p.error(err);
}
let constructor_kind = expect_method_body(
p,
&member_marker,
modifiers,
ClassMethodMemberKind::Constructor,
);
member_marker.complete(p, constructor_kind.as_constructor_syntax_kind())
}
fn parse_constructor_parameter_list(p: &mut JsParser) -> ParsedSyntax {
let m = p.start();
let flags = SignatureFlags::CONSTRUCTOR;
parse_parameters_list(
p,
flags,
parse_constructor_parameter,
JS_CONSTRUCTOR_PARAMETER_LIST,
);
Present(m.complete(p, JS_CONSTRUCTOR_PARAMETERS))
}
fn parse_constructor_parameter(p: &mut JsParser, context: ExpressionContext) -> ParsedSyntax {
let decorator_list = parse_parameter_decorators(p);
if is_nth_at_modifier(p, 0, true) {
let property_parameter = decorator_list
.or_else(|| empty_decorator_list(p))
.precede(p);
let modifiers = parse_class_member_modifiers(p, true);
parse_formal_parameter(
p,
Absent,
ParameterContext::ParameterProperty,
context,
TypeContext::default(),
)
.or_add_diagnostic(p, expected_binding);
let kind = if modifiers.validate_and_complete(p, TS_PROPERTY_PARAMETER) {
TS_PROPERTY_PARAMETER
} else {
JS_BOGUS_PARAMETER
};
Present(property_parameter.complete(p, kind))
} else {
parse_any_parameter(
p,
decorator_list,
ParameterContext::ClassImplementation,
context,
TypeContext::default(),
)
.map(|mut parameter| {
if parameter.kind(p) == TS_THIS_PARAMETER {
p.error(p.err_builder(
"A constructor cannot have a 'this' parameter.",
parameter.range(p),
));
parameter.change_to_bogus(p);
}
parameter
})
}
}
fn is_at_class_member_name(p: &mut JsParser, offset: usize) -> bool {
matches!(p.nth(offset), T![#] | T!['[']) || is_at_literal_member_name(p, offset)
}
fn parse_class_member_name(p: &mut JsParser, modifiers: &mut ClassMemberModifiers) -> ParsedSyntax {
modifiers.set_private_member_name(p.at(T![#]));
match p.cur() {
T![#] => parse_private_class_member_name(p),
T!['['] => parse_computed_member_name(p),
_ => parse_literal_member_name(p),
}
}
pub(crate) fn parse_private_class_member_name(p: &mut JsParser) -> ParsedSyntax {
parse_private_name(p).map(|mut name| {
name.change_kind(p, JS_PRIVATE_CLASS_MEMBER_NAME);
name
})
}
fn is_at_method_class_member(p: &mut JsParser, mut offset: usize) -> bool {
if p.nth_at(offset, T![?]) {
offset += 1;
}
p.nth_at(offset, T!['(']) || p.nth_at(offset, T![<])
}
pub(crate) fn is_nth_at_modifier(p: &mut JsParser, n: usize, constructor_parameter: bool) -> bool {
if !matches!(
p.nth(n),
T![declare]
| T![public]
| T![protected]
| T![private]
| T![override]
| T![static]
| T![accessor]
| T![readonly]
| T![abstract]
) {
return false;
}
if !matches!(p.nth(n), T![static]) && p.has_nth_preceding_line_break(n + 1) {
return false;
}
let followed_by_any_member = is_at_class_member_name(p, n + 1);
let followed_by_class_member = !constructor_parameter && p.nth_at(n + 1, T![*]);
let followed_by_parameter = constructor_parameter && matches!(p.nth(n + 1), T!['{'] | T!['[']);
followed_by_any_member || followed_by_class_member || followed_by_parameter
}
fn is_at_constructor(p: &JsParser, modifiers: &ClassMemberModifiers) -> bool {
!modifiers.has(ModifierKind::Static)
&& (p.at(T![constructor]) || matches!(p.cur_text(), "\"constructor\"" | "'constructor'"))
}
fn parse_class_member_modifiers(
p: &mut JsParser,
constructor_parameter: bool,
) -> ClassMemberModifiers {
let mut modifiers = ClassMemberModifierList::default();
let list = p.start();
let mut progress = ParserProgress::default();
let mut flags = ModifierFlags::empty();
while let Some(modifier) = parse_modifier(p, constructor_parameter) {
progress.assert_progressing(p);
flags |= modifier.kind.as_flags();
modifiers.add_modifier(modifier);
}
let list = list.complete(p, JS_BOGUS);
ClassMemberModifiers::new(modifiers, list, flags)
}
fn parse_modifier(p: &mut JsParser, constructor_parameter: bool) -> Option<ClassMemberModifier> {
let is_at_decorator_modifier = p.cur() == T![@] || p.nth_at(1, T![@]);
if !is_nth_at_modifier(p, 0, constructor_parameter) && !is_at_decorator_modifier {
return None;
}
let modifier_kind = match p.cur() {
T![declare] => ModifierKind::Declare,
T![public] => ModifierKind::Public,
T![protected] => ModifierKind::Protected,
T![private] => ModifierKind::Private,
T![override] => ModifierKind::Override,
T![static] => ModifierKind::Static,
T![accessor] => ModifierKind::Accessor,
T![readonly] => ModifierKind::Readonly,
T![abstract] => ModifierKind::Abstract,
T![@] => ModifierKind::Decorator,
_ => {
return None;
}
};
match modifier_kind {
ModifierKind::Decorator => {
let decorator = parse_decorator(p).unwrap();
let range = decorator.range(p);
Some(ClassMemberModifier {
start: range.start(),
length: range.len(),
kind: modifier_kind,
})
}
_ => {
let m = p.start();
let range = p.cur_range();
p.bump_any();
m.complete(p, modifier_kind.as_syntax_kind());
Some(ClassMemberModifier {
start: range.start(),
length: range.len(),
kind: modifier_kind,
})
}
}
}
bitflags! {
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
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 PRIVATE_NAME = 1 << 8;
const ACCESSOR = 1 << 9;
const DECORATOR = 1 << 10;
const ACCESSIBILITY = ModifierFlags::PRIVATE.bits() | ModifierFlags::PROTECTED.bits() | ModifierFlags::PUBLIC.bits();
const ALL_MODIFIERS_EXCEPT_DECORATOR = ModifierFlags::DECLARE.bits()
| ModifierFlags::PRIVATE.bits()
| ModifierFlags::PROTECTED.bits()
| ModifierFlags::PUBLIC.bits()
| ModifierFlags::STATIC.bits()
| ModifierFlags::READONLY.bits()
| ModifierFlags::ABSTRACT.bits()
| ModifierFlags::OVERRIDE.bits()
| ModifierFlags::PRIVATE_NAME.bits()
| ModifierFlags::ACCESSOR.bits();
}
}
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
enum ModifierKind {
Declare,
Abstract,
Private,
Protected,
Public,
Static,
Accessor,
Readonly,
Override,
Decorator,
}
impl ModifierKind {
const fn is_ts_modifier(&self) -> bool {
!matches!(
self,
ModifierKind::Static | ModifierKind::Accessor | ModifierKind::Decorator
)
}
const fn as_syntax_kind(&self) -> JsSyntaxKind {
match self {
ModifierKind::Declare => TS_DECLARE_MODIFIER,
ModifierKind::Abstract => TS_ABSTRACT_MODIFIER,
ModifierKind::Private | ModifierKind::Protected | ModifierKind::Public => {
TS_ACCESSIBILITY_MODIFIER
}
ModifierKind::Static => JS_STATIC_MODIFIER,
ModifierKind::Accessor => JS_ACCESSOR_MODIFIER,
ModifierKind::Readonly => TS_READONLY_MODIFIER,
ModifierKind::Override => TS_OVERRIDE_MODIFIER,
ModifierKind::Decorator => JS_DECORATOR,
}
}
const fn as_flags(&self) -> ModifierFlags {
match self {
ModifierKind::Declare => ModifierFlags::DECLARE,
ModifierKind::Abstract => ModifierFlags::ABSTRACT,
ModifierKind::Private => ModifierFlags::PRIVATE,
ModifierKind::Protected => ModifierFlags::PROTECTED,
ModifierKind::Public => ModifierFlags::PUBLIC,
ModifierKind::Static => ModifierFlags::STATIC,
ModifierKind::Accessor => ModifierFlags::ACCESSOR,
ModifierKind::Readonly => ModifierFlags::READONLY,
ModifierKind::Override => ModifierFlags::OVERRIDE,
ModifierKind::Decorator => ModifierFlags::DECORATOR,
}
}
}
#[derive(Debug, Clone)]
struct ClassMemberModifier {
kind: ModifierKind,
start: TextSize,
length: TextSize,
}
impl ClassMemberModifier {
fn as_text_range(&self) -> TextRange {
TextRange::new(self.start, self.start.add(self.length))
}
}
#[derive(Debug, Default)]
struct ClassMemberModifierList(SmallVec<[ClassMemberModifier; 4]>);
impl ClassMemberModifierList {
fn add_modifier(&mut self, modifier: ClassMemberModifier) {
self.0.push(modifier);
}
fn iter(&self) -> Iter<'_, ClassMemberModifier> {
self.0.iter()
}
fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
#[derive(Debug)]
#[must_use]
struct ClassMemberModifiers {
modifiers: ClassMemberModifierList,
flags: ModifierFlags,
list_marker: CompletedMarker,
bomb: DebugDropBomb,
}
impl ClassMemberModifiers {
fn new(
modifiers: ClassMemberModifierList,
list_marker: CompletedMarker,
flags: ModifierFlags,
) -> Self {
Self {
modifiers,
list_marker,
flags,
bomb: DebugDropBomb::new("list must either be 'completed' or 'abandoned' by calling 'complete' or 'abandon'.")
}
}
fn set_private_member_name(&mut self, private_name: bool) {
self.flags.set(ModifierFlags::PRIVATE_NAME, private_name);
}
fn has(&self, kind: ModifierKind) -> bool {
self.flags.contains(kind.as_flags())
}
fn is_signature(&self) -> bool {
self.has(ModifierKind::Abstract) || self.has(ModifierKind::Declare)
}
fn get_first_range(&self, kind: ModifierKind) -> Option<TextRange> {
if self.flags.contains(kind.as_flags()) {
self.modifiers
.iter()
.find(|m| m.kind == kind)
.map(|m| m.as_text_range())
} else {
None
}
}
fn get_first_range_unchecked(&self, kind: ModifierKind) -> TextRange {
self.get_first_range(kind)
.unwrap_or_else(|| panic!("Expected modifier of kind {:?} to be present", kind))
}
fn is_empty(&self) -> bool {
self.modifiers.is_empty()
}
fn abandon(mut self, p: &mut JsParser) {
self.list_marker.undo_completion(p).abandon(p);
self.bomb.defuse();
}
fn validate_and_complete(mut self, p: &mut JsParser, member_kind: JsSyntaxKind) -> bool {
self.bomb.defuse();
let list_kind = match member_kind {
JS_PROPERTY_CLASS_MEMBER => JS_PROPERTY_MODIFIER_LIST,
TS_PROPERTY_SIGNATURE_CLASS_MEMBER | TS_INITIALIZED_PROPERTY_SIGNATURE_CLASS_MEMBER => {
TS_PROPERTY_SIGNATURE_MODIFIER_LIST
}
JS_GETTER_CLASS_MEMBER | JS_SETTER_CLASS_MEMBER | JS_METHOD_CLASS_MEMBER => {
JS_METHOD_MODIFIER_LIST
}
TS_GETTER_SIGNATURE_CLASS_MEMBER
| TS_SETTER_SIGNATURE_CLASS_MEMBER
| TS_METHOD_SIGNATURE_CLASS_MEMBER => TS_METHOD_SIGNATURE_MODIFIER_LIST,
JS_CONSTRUCTOR_CLASS_MEMBER | TS_CONSTRUCTOR_SIGNATURE_CLASS_MEMBER => {
JS_CONSTRUCTOR_MODIFIER_LIST
}
TS_INDEX_SIGNATURE_CLASS_MEMBER => TS_INDEX_SIGNATURE_MODIFIER_LIST,
TS_PROPERTY_PARAMETER => TS_PROPERTY_PARAMETER_MODIFIER_LIST,
JS_BOGUS_MEMBER | JS_STATIC_INITIALIZATION_BLOCK_CLASS_MEMBER => {
self.list_marker.undo_completion(p).abandon(p);
return false;
}
t => panic!("Unknown member kind {:?}", t),
};
self.list_marker.change_kind(p, list_kind);
let mut preceding_modifiers = ModifierFlags::empty();
let mut valid = true;
for modifier in self.modifiers.iter() {
if let Some(diagnostic) =
self.check_class_member_modifier(p, modifier, preceding_modifiers, member_kind)
{
p.error(diagnostic);
valid = false;
}
preceding_modifiers |= modifier.kind.as_flags(); }
valid
}
fn check_class_member_modifier(
&self,
p: &JsParser,
modifier: &ClassMemberModifier,
preceding_modifiers: ModifierFlags,
member_kind: JsSyntaxKind,
) -> Option<ParseDiagnostic> {
if TypeScript.is_unsupported(p) && modifier.kind.is_ts_modifier() {
return Some(p.err_builder(
format!(
"'{}' modifier can only be used in TypeScript files",
p.text(modifier.as_text_range())
),
modifier.as_text_range(),
));
}
if modifier.kind == ModifierKind::Decorator
&& !matches!(
member_kind,
JS_PROPERTY_CLASS_MEMBER
| JS_METHOD_CLASS_MEMBER
| JS_GETTER_CLASS_MEMBER
| JS_SETTER_CLASS_MEMBER
| TS_PROPERTY_SIGNATURE_CLASS_MEMBER
| TS_INITIALIZED_PROPERTY_SIGNATURE_CLASS_MEMBER
)
{
return Some(decorators_not_allowed(p, modifier.as_text_range()));
} else if member_kind == TS_INDEX_SIGNATURE_CLASS_MEMBER
&& !matches!(modifier.kind, ModifierKind::Static | ModifierKind::Readonly)
{
return Some(p.err_builder(
format!(
"'{}' modifier cannot appear on an index signature.",
p.text(modifier.as_text_range())
),
modifier.as_text_range(),
));
} else if matches!(
member_kind,
JS_CONSTRUCTOR_CLASS_MEMBER | TS_CONSTRUCTOR_SIGNATURE_CLASS_MEMBER
) && !matches!(
modifier.kind,
ModifierKind::Private | ModifierKind::Protected | ModifierKind::Public
) {
return Some(ts_modifier_cannot_appear_on_a_constructor_declaration(
p,
modifier.as_text_range(),
));
} else if
member_kind == TS_PROPERTY_PARAMETER
&& !matches!(
modifier.kind,
ModifierKind::Private
| ModifierKind::Protected
| ModifierKind::Public
| ModifierKind::Override
| ModifierKind::Readonly
)
{
return Some(ts_modifier_cannot_appear_on_a_parameter(
p,
modifier.as_text_range(),
));
}
match modifier.kind {
ModifierKind::Decorator => {
if preceding_modifiers.intersects(ModifierFlags::ALL_MODIFIERS_EXCEPT_DECORATOR) {
return Some(decorator_must_precede_modifier(p, modifier.as_text_range()));
}
}
ModifierKind::Readonly => {
if preceding_modifiers.contains(ModifierFlags::READONLY) {
return Some(modifier_already_seen(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Readonly),
));
} else if !matches!(
member_kind,
JS_PROPERTY_CLASS_MEMBER
| TS_PROPERTY_SIGNATURE_CLASS_MEMBER
| TS_INITIALIZED_PROPERTY_SIGNATURE_CLASS_MEMBER
| TS_INDEX_SIGNATURE_CLASS_MEMBER
| JS_BOGUS_MEMBER
| TS_PROPERTY_PARAMETER
) {
return Some(p.err_builder(
"Readonly can only appear on a property declaration or index signature.",
modifier.as_text_range(),
));
}
}
ModifierKind::Declare => {
if preceding_modifiers.contains(ModifierFlags::DECLARE) {
return Some(modifier_already_seen(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Declare),
));
} else if self.flags.contains(ModifierFlags::ACCESSOR) {
return Some(modifier_cannot_be_used_with_modifier(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Accessor),
));
} else if self.flags.contains(ModifierFlags::OVERRIDE) {
return Some(modifier_cannot_be_used_with_modifier(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Override),
));
} else if !matches!(
member_kind,
JS_PROPERTY_CLASS_MEMBER
| TS_PROPERTY_SIGNATURE_CLASS_MEMBER
| TS_INITIALIZED_PROPERTY_SIGNATURE_CLASS_MEMBER
) {
return Some(p.err_builder(
"'declare' modifier is only allowed on properties.",
modifier.as_text_range(),
));
} else if self.flags.contains(ModifierFlags::PRIVATE_NAME) {
return Some(p.err_builder(
"'declare' modifier cannot be used with a private identifier'.",
modifier.as_text_range(),
));
}
}
ModifierKind::Abstract => {
if preceding_modifiers.contains(ModifierFlags::ABSTRACT) {
return Some(modifier_already_seen(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Abstract),
));
} else if !matches!(
member_kind,
JS_METHOD_CLASS_MEMBER
| TS_METHOD_SIGNATURE_CLASS_MEMBER
| JS_PROPERTY_CLASS_MEMBER
| TS_PROPERTY_SIGNATURE_CLASS_MEMBER
| TS_INITIALIZED_PROPERTY_SIGNATURE_CLASS_MEMBER
| JS_SETTER_CLASS_MEMBER
| TS_SETTER_SIGNATURE_CLASS_MEMBER
| JS_GETTER_CLASS_MEMBER
| TS_GETTER_SIGNATURE_CLASS_MEMBER
| JS_BOGUS_MEMBER
) {
return Some(
p.err_builder("'abstract' modifier can only appear on a class, method or property declaration.",modifier.as_text_range(), )
);
} else if self.flags.contains(ModifierFlags::STATIC) {
return Some(modifier_cannot_be_used_with_modifier(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Static),
));
} else if preceding_modifiers.contains(ModifierFlags::ACCESSOR) {
return Some(modifier_must_precede_modifier(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Accessor),
));
} else if preceding_modifiers.contains(ModifierFlags::OVERRIDE) {
return Some(modifier_must_precede_modifier(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Override),
));
} else if self.flags.contains(ModifierFlags::PRIVATE_NAME) {
return Some(p.err_builder(
"'abstract' modifier cannot be used with a private identifier'.",
modifier.as_text_range(),
));
}
}
ModifierKind::Private | ModifierKind::Protected | ModifierKind::Public => {
if preceding_modifiers.intersects(ModifierFlags::ACCESSIBILITY) {
let range = if preceding_modifiers.contains(ModifierFlags::PRIVATE) {
self.get_first_range_unchecked(ModifierKind::Private)
} else if preceding_modifiers.contains(ModifierFlags::PROTECTED) {
self.get_first_range_unchecked(ModifierKind::Protected)
} else {
self.get_first_range_unchecked(ModifierKind::Public)
};
return Some(ts_accessibility_modifier_already_seen(
p,
modifier.as_text_range(),
range,
));
} else if preceding_modifiers.contains(ModifierFlags::OVERRIDE) {
return Some(modifier_must_precede_modifier(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Override),
));
} else if preceding_modifiers.contains(ModifierFlags::STATIC) {
return Some(modifier_must_precede_modifier(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Static),
));
} else if preceding_modifiers.contains(ModifierFlags::ACCESSOR) {
return Some(modifier_must_precede_modifier(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Accessor),
));
} else if preceding_modifiers.contains(ModifierFlags::READONLY) {
return Some(modifier_must_precede_modifier(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Readonly),
));
} else if self.flags.contains(ModifierFlags::ABSTRACT)
&& modifier.kind == ModifierKind::Private
{
return Some(modifier_cannot_be_used_with_modifier(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Abstract),
));
} else if preceding_modifiers.contains(ModifierFlags::ABSTRACT) {
return Some(modifier_must_precede_modifier(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Abstract),
));
} else if self.flags.contains(ModifierFlags::PRIVATE_NAME) {
return Some(p.err_builder(
"An accessibility modifier cannot be used with a private identifier.",
modifier.as_text_range(),
));
}
}
ModifierKind::Static => {
if preceding_modifiers.contains(ModifierFlags::STATIC) {
return Some(modifier_already_seen(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Static),
));
} else if preceding_modifiers.contains(ModifierFlags::ACCESSOR) {
return Some(modifier_must_precede_modifier(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Accessor),
));
} else if preceding_modifiers.contains(ModifierFlags::READONLY) {
return Some(modifier_must_precede_modifier(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Readonly),
));
} else if preceding_modifiers.contains(ModifierFlags::OVERRIDE) {
return Some(modifier_must_precede_modifier(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Override),
));
}
}
ModifierKind::Accessor => {
if preceding_modifiers.contains(ModifierFlags::ACCESSOR) {
return Some(modifier_already_seen(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Accessor),
));
}
else if preceding_modifiers.contains(ModifierFlags::READONLY) {
return Some(modifier_must_precede_modifier(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Readonly),
));
} else if preceding_modifiers.contains(ModifierFlags::OVERRIDE) {
return Some(modifier_cannot_be_used_with_modifier(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Override),
));
} else if !matches!(
member_kind,
JS_PROPERTY_CLASS_MEMBER | TS_PROPERTY_SIGNATURE_CLASS_MEMBER
) {
return Some(p.err_builder(
"'accessor' modifier is only allowed on properties.",
modifier.as_text_range(),
));
}
}
ModifierKind::Override => {
if preceding_modifiers.contains(ModifierFlags::OVERRIDE) {
return Some(modifier_already_seen(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Override),
));
} else if preceding_modifiers.contains(ModifierFlags::READONLY) {
return Some(modifier_must_precede_modifier(
p,
modifier.as_text_range(),
self.get_first_range_unchecked(ModifierKind::Readonly),
));
}
}
}
None
}
}
pub(crate) fn parse_decorators(p: &mut JsParser) -> ParsedSyntax {
if !p.at(T![@]) {
return Absent;
}
let decorators = p.start();
let mut progress = ParserProgress::default();
while p.at(T![@]) {
progress.assert_progressing(p);
parse_decorator(p).ok();
}
Present(decorators.complete(p, JS_DECORATOR_LIST))
}
pub(crate) fn parse_parameter_decorators(p: &mut JsParser) -> ParsedSyntax {
let decorator_list = parse_decorators(p);
if p.options().should_parse_parameter_decorators() {
decorator_list
} else {
decorator_list
.add_diagnostic_if_present(p, parameter_decorators_not_allowed)
.map(|mut decorator_list| {
decorator_list.change_to_bogus(p);
decorator_list
})
.into()
}
}
pub(crate) fn empty_decorator_list(p: &mut JsParser) -> ParsedSyntax {
let m = p.start();
Present(m.complete(p, JS_DECORATOR_LIST))
}
fn parse_decorator(p: &mut JsParser) -> ParsedSyntax {
if !p.at(T![@]) {
return Absent;
}
let m = p.start();
p.bump(T![@]);
if let Some(mut complete_marker) =
parse_lhs_expr(p, ExpressionContext::default().and_in_decorator(true))
.or_add_diagnostic(p, expected_expression)
{
if !matches!(
complete_marker.kind(p),
JS_PARENTHESIZED_EXPRESSION
| JS_CALL_EXPRESSION
| JS_STATIC_MEMBER_EXPRESSION
| JS_IDENTIFIER_EXPRESSION
) {
p.error(invalid_decorator_error(p, complete_marker.range(p)));
complete_marker.change_to_bogus(p);
}
}
Present(m.complete(p, JS_DECORATOR))
}