#[allow(deprecated)]
use crate::parser::single_token_parse_recovery::SingleTokenParseRecovery;
use crate::parser::ParsedSyntax::{Absent, Present};
use crate::parser::{ParsedSyntax, RecoveryResult};
use crate::prelude::*;
use crate::state::{EnterParameters, SignatureFlags};
use crate::syntax::class::parse_decorators;
use crate::syntax::expr::{
is_nth_at_reference_identifier, parse_assignment_expression_or_higher, parse_expression,
parse_reference_identifier, ExpressionContext,
};
use crate::syntax::function::{
parse_formal_parameter, parse_function_body, parse_parameter_list, ParameterContext,
};
use crate::syntax::js_parse_error;
use crate::syntax::js_parse_error::decorators_not_allowed;
use crate::syntax::typescript::ts_parse_error::{
ts_accessor_type_parameters_error, ts_only_syntax_error, ts_set_accessor_return_type_error,
};
use crate::syntax::typescript::{
parse_ts_return_type_annotation, parse_ts_type_annotation, parse_ts_type_parameters,
TypeContext,
};
use crate::JsSyntaxFeature::TypeScript;
use crate::{JsParser, ParseRecoveryTokenSet};
use biome_js_syntax::JsSyntaxKind::*;
use biome_js_syntax::{JsSyntaxKind, T};
use biome_parser::parse_lists::ParseSeparatedList;
struct ObjectMembersList;
impl ParseSeparatedList for ObjectMembersList {
type Kind = JsSyntaxKind;
type Parser<'source> = JsParser<'source>;
const LIST_KIND: Self::Kind = JS_OBJECT_MEMBER_LIST;
fn parse_element(&mut self, p: &mut JsParser) -> ParsedSyntax {
parse_object_member(p)
}
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!['}'], T![;], T![:]])
.enable_recovery_on_line_break(),
js_parse_error::expected_object_member,
)
}
fn separating_element_kind(&mut self) -> JsSyntaxKind {
T![,]
}
fn allow_trailing_separating_element(&self) -> bool {
true
}
}
pub(super) fn parse_object_expression(p: &mut JsParser) -> ParsedSyntax {
if !p.at(T!['{']) {
return Absent;
}
let m = p.start();
p.bump(T!['{']);
ObjectMembersList.parse_list(p);
p.expect(T!['}']);
Present(m.complete(p, JS_OBJECT_EXPRESSION))
}
fn parse_object_member(p: &mut JsParser) -> ParsedSyntax {
match p.cur() {
T![get] if !p.has_nth_preceding_line_break(1) && is_nth_at_type_member_name(p, 1) => {
parse_getter_object_member(p)
}
T![set] if !p.has_nth_preceding_line_break(1) && is_nth_at_type_member_name(p, 1) => {
parse_setter_object_member(p)
}
T![async] if is_parser_at_async_method_member(p) => parse_method_object_member(p),
T![...] => {
let m = p.start();
p.bump_any();
parse_assignment_expression_or_higher(p, ExpressionContext::default())
.or_add_diagnostic(p, js_parse_error::expected_expression_assignment);
Present(m.complete(p, JS_SPREAD))
}
T![*] => {
parse_method_object_member(p)
}
_ => {
let m = p.start();
if is_nth_at_reference_identifier(p, 0)
&& !token_set![T!['('], T![<], T![:]].contains(p.nth(1))
{
parse_reference_identifier(p).unwrap();
if p.at(T![=]) {
p.error(p.err_builder("Did you mean to use a `:`? An `=` can only follow a property name when the containing object literal is part of a destructuring pattern.",
p.cur_range()));
p.bump(T![=]);
parse_assignment_expression_or_higher(p, ExpressionContext::default()).ok();
return Present(m.complete(p, JS_BOGUS_MEMBER));
}
return Present(m.complete(p, JS_SHORTHAND_PROPERTY_OBJECT_MEMBER));
}
let checkpoint = p.checkpoint();
let member_name = parse_object_member_name(p)
.or_add_diagnostic(p, js_parse_error::expected_object_member);
if p.at(T!['(']) || p.at(T![<]) {
parse_method_object_member_body(p, SignatureFlags::empty());
Present(m.complete(p, JS_METHOD_OBJECT_MEMBER))
} else if member_name.is_some() {
p.expect(T![:]);
parse_assignment_expression_or_higher(p, ExpressionContext::default())
.or_add_diagnostic(p, js_parse_error::expected_expression_assignment);
Present(m.complete(p, JS_PROPERTY_OBJECT_MEMBER))
} else {
#[allow(deprecated)]
SingleTokenParseRecovery::new(token_set![T![:], T![,]], JS_BOGUS).recover(p);
if p.eat(T![:]) {
parse_assignment_expression_or_higher(p, ExpressionContext::default())
.or_add_diagnostic(p, js_parse_error::expected_object_member);
Present(m.complete(p, JS_PROPERTY_OBJECT_MEMBER))
} else {
p.rewind(checkpoint);
m.abandon(p);
Absent
}
}
}
}
}
fn parse_getter_object_member(p: &mut JsParser) -> ParsedSyntax {
if !p.at(T![get]) {
return Absent;
}
let m = p.start();
p.expect(T![get]);
parse_object_member_name(p).or_add_diagnostic(p, js_parse_error::expected_object_member_name);
if let Present(type_parameters) = parse_ts_type_parameters(p, TypeContext::default()) {
p.error(ts_accessor_type_parameters_error(p, &type_parameters))
}
p.expect(T!['(']);
p.expect(T![')']);
TypeScript
.parse_exclusive_syntax(
p,
|p| parse_ts_type_annotation(p, TypeContext::default()),
|p, annotation| ts_only_syntax_error(p, "type annotation", annotation.range(p)),
)
.ok();
parse_function_body(p, SignatureFlags::empty())
.or_add_diagnostic(p, js_parse_error::expected_function_body);
Present(m.complete(p, JS_GETTER_OBJECT_MEMBER))
}
fn parse_setter_object_member(p: &mut JsParser) -> ParsedSyntax {
if !p.at(T![set]) {
return Absent;
}
let m = p.start();
p.expect(T![set]);
parse_object_member_name(p).or_add_diagnostic(p, js_parse_error::expected_object_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 has_l_paren = p.expect(T!['(']);
p.with_state(EnterParameters(SignatureFlags::empty()), |p| {
let decorator_list = parse_decorators(p)
.add_diagnostic_if_present(p, decorators_not_allowed)
.map(|mut decorator_list| {
decorator_list.change_to_bogus(p);
decorator_list
})
.into();
parse_formal_parameter(
p,
decorator_list,
ParameterContext::Setter,
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,
));
}
parse_function_body(p, SignatureFlags::empty())
.or_add_diagnostic(p, js_parse_error::expected_function_body);
Present(m.complete(p, JS_SETTER_OBJECT_MEMBER))
}
pub(crate) fn parse_object_member_name(p: &mut JsParser) -> ParsedSyntax {
match p.cur() {
T!['['] => parse_computed_member_name(p),
_ => parse_literal_member_name(p),
}
}
pub(crate) fn is_nth_at_type_member_name(p: &mut JsParser, offset: usize) -> bool {
let nth = p.nth(offset);
let start_names = token_set![
JS_STRING_LITERAL,
JS_NUMBER_LITERAL,
T![ident],
T![await],
T![yield],
T!['[']
];
nth.is_keyword() || start_names.contains(nth)
}
pub(crate) fn is_at_object_member_name(p: &mut JsParser) -> bool {
is_nth_at_type_member_name(p, 0)
}
pub(crate) fn parse_computed_member_name(p: &mut JsParser) -> ParsedSyntax {
if !p.at(T!['[']) {
return Absent;
}
let m = p.start();
p.expect(T!['[']);
parse_expression(p, ExpressionContext::default())
.or_add_diagnostic(p, js_parse_error::expected_expression);
p.expect(T![']']);
Present(m.complete(p, JS_COMPUTED_MEMBER_NAME))
}
pub(super) fn is_at_literal_member_name(p: &mut JsParser, offset: usize) -> bool {
matches!(
p.nth(offset),
JS_STRING_LITERAL | JS_NUMBER_LITERAL | T![ident]
) || p.nth(offset).is_keyword()
}
pub(super) fn parse_literal_member_name(p: &mut JsParser) -> ParsedSyntax {
let m = p.start();
match p.cur() {
JS_STRING_LITERAL | JS_NUMBER_LITERAL | T![ident] => {
p.bump_any();
}
t if t.is_keyword() => {
p.bump_remap(T![ident]);
}
_ => {
m.abandon(p);
return Absent;
}
}
Present(m.complete(p, JS_LITERAL_MEMBER_NAME))
}
fn parse_method_object_member(p: &mut JsParser) -> ParsedSyntax {
let is_async = is_parser_at_async_method_member(p);
if !is_async && !p.at(T![*]) && !is_at_object_member_name(p) {
return Absent;
}
let m = p.start();
let mut flags = SignatureFlags::empty();
if is_async {
p.eat(T![async]);
flags |= SignatureFlags::ASYNC;
}
if p.eat(T![*]) {
flags |= SignatureFlags::GENERATOR;
}
parse_object_member_name(p).or_add_diagnostic(p, js_parse_error::expected_object_member_name);
parse_method_object_member_body(p, flags);
Present(m.complete(p, JS_METHOD_OBJECT_MEMBER))
}
fn parse_method_object_member_body(p: &mut JsParser, flags: SignatureFlags) {
TypeScript
.parse_exclusive_syntax(
p,
|p| parse_ts_type_parameters(p, TypeContext::default().and_allow_const_modifier(true)),
|p, type_parameters| {
ts_only_syntax_error(p, "type parameters", type_parameters.range(p))
},
)
.ok();
parse_parameter_list(
p,
ParameterContext::Implementation,
TypeContext::default(),
flags,
)
.or_add_diagnostic(p, js_parse_error::expected_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();
parse_function_body(p, flags).or_add_diagnostic(p, js_parse_error::expected_function_body);
}
fn is_parser_at_async_method_member(p: &mut JsParser) -> bool {
p.at(T![async])
&& !p.has_nth_preceding_line_break(1)
&& (is_nth_at_type_member_name(p, 1) || p.nth_at(1, T![*]))
}