use crate::parser::ParsedSyntax;
use crate::prelude::*;
use crate::state::{EnterFunction, EnterParameters, SignatureFlags};
use crate::syntax::binding::{
is_at_identifier_binding, is_nth_at_identifier_binding, parse_binding, parse_binding_pattern,
};
use crate::syntax::class::{
empty_decorator_list, parse_initializer_clause, parse_parameter_decorators,
};
use crate::syntax::expr::{
is_nth_at_identifier, parse_assignment_expression_or_higher, ExpressionContext,
};
use crate::syntax::js_parse_error;
use crate::syntax::js_parse_error::{
decorators_not_allowed, expected_binding, expected_parameter, expected_parameters,
};
use crate::syntax::stmt::{is_semi, parse_block_impl, semi, StatementContext};
use crate::syntax::typescript::ts_parse_error::ts_only_syntax_error;
use crate::syntax::typescript::{
is_nth_at_type_parameter_modifier, parse_ts_return_type_annotation, parse_ts_type_annotation,
parse_ts_type_parameters, try_parse, TypeContext,
};
use crate::JsSyntaxFeature::TypeScript;
use crate::ParsedSyntax::{Absent, Present};
use crate::{JsParser, JsSyntaxFeature, ParseRecoveryTokenSet};
use biome_js_syntax::JsSyntaxKind::*;
use biome_js_syntax::{JsSyntaxKind, TextRange, T};
use biome_parser::ParserProgress;
use biome_rowan::SyntaxKind;
pub(super) fn parse_function_declaration(
p: &mut JsParser,
context: StatementContext,
) -> ParsedSyntax {
if !is_at_function(p) {
return Absent;
}
let m = p.start();
let mut function = if p.state().in_ambient_context() {
parse_ambient_function(p, m, AmbientFunctionKind::Declaration)
} else {
parse_function(
p,
m,
FunctionKind::Declaration {
single_statement_context: context.is_single_statement(),
},
)
};
if context != StatementContext::StatementList && !function.kind(p).is_bogus() {
if JsSyntaxFeature::StrictMode.is_supported(p) {
p.error(p.err_builder("In strict mode code, functions can only be declared at top level or inside a block", function.range(p)).with_hint( "wrap the function in a block statement"));
function.change_to_bogus(p);
} else if !matches!(context, StatementContext::If | StatementContext::Label) {
p.error(p.err_builder("In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if or labelled statement", function.range(p)).with_hint( "wrap the function in a block statement"));
function.change_to_bogus(p);
}
}
Present(function)
}
pub(super) fn parse_function_expression(p: &mut JsParser) -> ParsedSyntax {
if !is_at_function(p) {
return Absent;
}
let m = p.start();
Present(parse_function(p, m, FunctionKind::Expression))
}
pub(super) fn parse_function_export_default_declaration(p: &mut JsParser) -> ParsedSyntax {
if !is_at_function(p) {
return Absent;
}
let m = p.start();
Present(if p.state().in_ambient_context() {
parse_ambient_function(p, m, AmbientFunctionKind::ExportDefault)
} else {
parse_function(p, m, FunctionKind::ExportDefault)
})
}
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
enum AmbientFunctionKind {
Declaration,
ExportDefault,
}
impl AmbientFunctionKind {
const fn is_export_default(&self) -> bool {
matches!(self, AmbientFunctionKind::ExportDefault)
}
}
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
enum FunctionKind {
Declaration {
single_statement_context: bool,
},
Expression,
ExportDefault,
}
impl FunctionKind {
const fn is_export_default(&self) -> bool {
matches!(self, FunctionKind::ExportDefault)
}
fn is_id_optional(&self) -> bool {
matches!(self, FunctionKind::Expression | FunctionKind::ExportDefault)
}
fn is_expression(&self) -> bool {
matches!(self, FunctionKind::Expression)
}
fn is_in_single_statement_context(&self) -> bool {
matches!(
self,
FunctionKind::Declaration {
single_statement_context: true
}
)
}
}
impl From<FunctionKind> for JsSyntaxKind {
fn from(kind: FunctionKind) -> Self {
match kind {
FunctionKind::Declaration { .. } => JS_FUNCTION_DECLARATION,
FunctionKind::Expression => JS_FUNCTION_EXPRESSION,
FunctionKind::ExportDefault => JS_FUNCTION_EXPORT_DEFAULT_DECLARATION,
}
}
}
fn is_at_function(p: &mut JsParser) -> bool {
p.at_ts(token_set![T![async], T![function]]) || is_at_async_function(p, LineBreak::DoNotCheck)
}
#[inline]
fn parse_function(p: &mut JsParser, m: Marker, kind: FunctionKind) -> CompletedMarker {
let mut flags = SignatureFlags::empty();
let in_async = is_at_async_function(p, LineBreak::DoNotCheck);
if in_async {
p.eat(T![async]);
flags |= SignatureFlags::ASYNC;
}
p.expect(T![function]);
let generator_range = if p.at(T![*]) {
let range = p.cur_range();
p.bump(T![*]);
flags |= SignatureFlags::GENERATOR;
Some(range)
} else {
None
};
let id = parse_function_id(p, kind, flags);
if !kind.is_id_optional() {
id.or_add_diagnostic(p, |p, range| {
p.err_builder(
"expected a name for the function in a function declaration, but found none",
range,
)
});
}
TypeScript
.parse_exclusive_syntax(
p,
|p| parse_ts_type_parameters(p, TypeContext::default().and_allow_const_modifier(true)),
|p, marker| {
p.err_builder(
"type parameters can only be used in TypeScript files",
marker.range(p),
)
},
)
.ok();
let parameter_context = if !kind.is_expression() && TypeScript.is_supported(p) {
ParameterContext::Declaration
} else {
ParameterContext::Implementation
};
parse_parameter_list(p, parameter_context, 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, marker| {
p.err_builder(
"return types can only be used in TypeScript files",
marker.range(p),
)
},
)
.ok();
let body = parse_function_body(p, flags);
if body.is_absent()
&& TypeScript.is_supported(p)
&& is_semi(p, 0)
&& !kind.is_in_single_statement_context()
&& !kind.is_expression()
{
p.eat(T![;]);
if let Some(generator_range) = generator_range {
p.error(p.err_builder(
"An overload signature cannot be declared as a generator.",
generator_range,
));
}
if kind.is_export_default() {
m.complete(p, TS_DECLARE_FUNCTION_EXPORT_DEFAULT_DECLARATION)
} else {
m.complete(p, TS_DECLARE_FUNCTION_DECLARATION)
}
} else {
body.or_add_diagnostic(p, js_parse_error::expected_function_body);
let mut function = m.complete(p, kind.into());
if kind.is_in_single_statement_context() && (in_async || generator_range.is_some()) {
p.error(p.err_builder("`async` and generator functions can only be declared at top level or inside a block", function.range(p) ));
function.change_to_bogus(p);
}
function
}
}
pub(super) fn parse_function_body(p: &mut JsParser, flags: SignatureFlags) -> ParsedSyntax {
p.with_state(EnterFunction(flags), |p| {
parse_block_impl(p, JS_FUNCTION_BODY)
})
}
fn parse_function_id(p: &mut JsParser, kind: FunctionKind, flags: SignatureFlags) -> ParsedSyntax {
match kind {
FunctionKind::Expression => {
p.with_state(EnterFunction(flags), parse_binding)
}
_ => {
parse_binding(p)
}
}
}
fn parse_ambient_function(
p: &mut JsParser,
m: Marker,
kind: AmbientFunctionKind,
) -> CompletedMarker {
let stmt_start = p.cur_range().start();
let is_async = p.at(T![async]);
if is_async {
p.error(p.err_builder(
"'async' modifier cannot be used in an ambient context.",
p.cur_range(),
));
p.bump(T![async]);
}
p.expect(T![function]);
let is_generator = p.at(T![*]);
if is_generator {
p.error(p.err_builder(
"Generators are not allowed in an ambient context.",
p.cur_range(),
));
p.bump(T![*]);
}
let binding = parse_binding(p);
let binding_range = p.cur_range();
parse_ts_type_parameters(p, TypeContext::default().and_allow_const_modifier(true)).ok();
parse_parameter_list(
p,
ParameterContext::Declaration,
TypeContext::default(),
SignatureFlags::empty(),
)
.or_add_diagnostic(p, expected_parameters);
parse_ts_return_type_annotation(p, TypeContext::default()).ok();
if let Present(body) = parse_function_body(p, SignatureFlags::empty()) {
p.error(
p.err_builder(
"A 'declare' function cannot have a function body",
body.range(p),
)
.with_hint("remove this body"),
);
}
semi(p, TextRange::new(stmt_start, p.cur_range().start()));
if is_async {
m.complete(p, JS_BOGUS_STATEMENT)
} else if kind.is_export_default() {
m.complete(p, TS_DECLARE_FUNCTION_EXPORT_DEFAULT_DECLARATION)
} else {
if binding.is_absent() {
p.error(expected_binding(p, binding_range));
}
m.complete(p, TS_DECLARE_FUNCTION_DECLARATION)
}
}
pub(crate) fn parse_ts_type_annotation_or_error(p: &mut JsParser) -> ParsedSyntax {
TypeScript.parse_exclusive_syntax(
p,
|p| parse_ts_type_annotation(p, TypeContext::default()),
|p, annotation| {
p.err_builder(
"return types can only be used in TypeScript files",
annotation.range(p),
)
.with_hint("remove this type annotation")
},
)
}
#[derive(PartialEq, Eq)]
pub(crate) enum LineBreak {
DoCheck,
DoNotCheck,
}
#[inline]
pub(super) fn is_at_async_function(p: &mut JsParser, should_check_line_break: LineBreak) -> bool {
let async_function_tokens = p.at(T![async]) && p.nth_at(1, T![function]);
if should_check_line_break == LineBreak::DoCheck {
async_function_tokens && !p.has_nth_preceding_line_break(1)
} else {
async_function_tokens
}
}
#[derive(Debug, Copy, Clone)]
pub(crate) enum Ambiguity {
Allowed,
Disallowed,
}
impl Ambiguity {
fn is_disallowed(&self) -> bool {
matches!(self, Ambiguity::Disallowed)
}
}
pub(crate) fn parse_arrow_function_expression(p: &mut JsParser) -> ParsedSyntax {
parse_parenthesized_arrow_function_expression(p)
.or_else(|| parse_arrow_function_with_single_parameter(p))
}
fn try_parse_parenthesized_arrow_function_head(
p: &mut JsParser,
ambiguity: Ambiguity,
) -> Result<(Marker, SignatureFlags), Marker> {
let m = p.start();
let flags = if p.eat(T![async]) {
SignatureFlags::ASYNC
} else {
SignatureFlags::empty()
};
if p.at(T![<]) {
parse_ts_type_parameters(p, TypeContext::default().and_allow_const_modifier(true)).ok();
if ambiguity.is_disallowed() && p.last() != Some(T![>]) {
return Err(m);
}
}
if !p.at(T!['(']) && ambiguity.is_disallowed() {
return Err(m);
}
parse_parameter_list(
p,
ParameterContext::Arrow,
TypeContext::default(),
arrow_function_parameter_flags(p, flags),
)
.or_add_diagnostic(p, expected_parameters);
if p.last() != Some(T![')']) && ambiguity.is_disallowed() {
return Err(m);
}
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();
if p.has_preceding_line_break() {
p.error(p.err_builder("Line terminator not permitted before arrow.", p.cur_range()));
}
if !p.expect(T![=>]) && ambiguity.is_disallowed() {
return Err(m);
}
Ok((m, flags))
}
fn parse_possible_parenthesized_arrow_function_expression(p: &mut JsParser) -> ParsedSyntax {
let start_pos = p.cur_range().start();
if p.state().not_parenthesized_arrow.contains(&start_pos) {
return Absent;
}
match try_parse(p, |p| {
try_parse_parenthesized_arrow_function_head(p, Ambiguity::Disallowed)
}) {
Ok((m, flags)) => {
parse_arrow_body(p, flags).or_add_diagnostic(p, js_parse_error::expected_arrow_body);
Present(m.complete(p, JS_ARROW_FUNCTION_EXPRESSION))
}
Err(m) => {
m.abandon(p);
p.state_mut().not_parenthesized_arrow.insert(start_pos);
Absent
}
}
}
fn parse_parenthesized_arrow_function_expression(p: &mut JsParser) -> ParsedSyntax {
let is_parenthesized = is_parenthesized_arrow_function_expression(p);
match is_parenthesized {
IsParenthesizedArrowFunctionExpression::True => {
let (m, flags) = try_parse_parenthesized_arrow_function_head(p, Ambiguity::Allowed).expect("'CompletedMarker' because function should never return 'Err' if called with 'Ambiguity::Allowed'.");
parse_arrow_body(p, flags).or_add_diagnostic(p, js_parse_error::expected_arrow_body);
Present(m.complete(p, JS_ARROW_FUNCTION_EXPRESSION))
}
IsParenthesizedArrowFunctionExpression::Unknown => {
parse_possible_parenthesized_arrow_function_expression(p)
}
IsParenthesizedArrowFunctionExpression::False => Absent,
}
}
#[derive(Debug, Copy, Clone)]
enum IsParenthesizedArrowFunctionExpression {
True,
False,
Unknown,
}
fn is_parenthesized_arrow_function_expression(
p: &mut JsParser,
) -> IsParenthesizedArrowFunctionExpression {
match p.cur() {
T!['('] | T![<] => {
is_parenthesized_arrow_function_expression_impl(p, SignatureFlags::empty())
}
T![async] => {
if p.has_nth_preceding_line_break(1) {
IsParenthesizedArrowFunctionExpression::False
} else if matches!(p.nth(1), T!['('] | T![<]) {
is_parenthesized_arrow_function_expression_impl(p, SignatureFlags::ASYNC)
} else {
IsParenthesizedArrowFunctionExpression::False
}
}
T![=>] => IsParenthesizedArrowFunctionExpression::True,
_ => IsParenthesizedArrowFunctionExpression::False,
}
}
fn is_parenthesized_arrow_function_expression_impl(
p: &mut JsParser,
flags: SignatureFlags,
) -> IsParenthesizedArrowFunctionExpression {
let n = usize::from(flags.contains(SignatureFlags::ASYNC));
match p.nth(n) {
T!['('] => {
match p.nth(n + 1) {
T![')'] => {
match p.nth(n + 2) {
T![=>] | T![:] | T!['{'] => IsParenthesizedArrowFunctionExpression::True,
_ => IsParenthesizedArrowFunctionExpression::False,
}
}
T![...] => IsParenthesizedArrowFunctionExpression::True,
T!['['] | T!['{'] => IsParenthesizedArrowFunctionExpression::Unknown,
T![@] => IsParenthesizedArrowFunctionExpression::Unknown,
_ if is_nth_at_identifier_binding(p, n + 1) || p.nth_at(n + 1, T![this]) => {
match p.nth(n + 2) {
T![:] => IsParenthesizedArrowFunctionExpression::True,
T![=] | T![,] | T![')'] => IsParenthesizedArrowFunctionExpression::Unknown,
T![?] => {
match p.nth(n + 3) {
T![:] | T![,] | T![=] | T![')'] => {
IsParenthesizedArrowFunctionExpression::True
}
_ => IsParenthesizedArrowFunctionExpression::False,
}
}
_ => IsParenthesizedArrowFunctionExpression::False,
}
}
_ => IsParenthesizedArrowFunctionExpression::False,
}
}
T![<] => {
if JsSyntaxFeature::Jsx.is_supported(p) {
let n = if p.nth_at(n + 1, T![const]) { n + 1 } else { n };
if !is_nth_at_identifier(p, n + 1) {
return IsParenthesizedArrowFunctionExpression::False;
};
match p.nth(n + 2) {
T![extends] => {
if matches!(p.nth(n + 3), T![=] | T![/] | T![>]) {
IsParenthesizedArrowFunctionExpression::False
}
else if is_nth_at_identifier(p, n + 3) {
IsParenthesizedArrowFunctionExpression::Unknown
} else {
IsParenthesizedArrowFunctionExpression::True
}
}
T![=] | T![,] => IsParenthesizedArrowFunctionExpression::True,
_ => IsParenthesizedArrowFunctionExpression::False,
}
} else if is_nth_at_type_parameter_modifier(p, n + 1) {
IsParenthesizedArrowFunctionExpression::True
} else if !is_nth_at_identifier(p, n + 1) {
IsParenthesizedArrowFunctionExpression::False
} else {
IsParenthesizedArrowFunctionExpression::Unknown
}
}
_ => IsParenthesizedArrowFunctionExpression::False,
}
}
fn arrow_function_parameter_flags(p: &JsParser, mut flags: SignatureFlags) -> SignatureFlags {
if p.state().in_generator() {
flags |= SignatureFlags::GENERATOR;
}
if p.state().in_async() {
flags |= SignatureFlags::ASYNC;
}
flags
}
fn parse_arrow_function_with_single_parameter(p: &mut JsParser) -> ParsedSyntax {
if !is_arrow_function_with_single_parameter(p) {
return Absent;
}
let m = p.start();
let is_async = p.at(T![async]) && is_nth_at_identifier_binding(p, 1);
let flags = if is_async {
p.eat(T![async]);
SignatureFlags::ASYNC
} else {
SignatureFlags::empty()
};
p.with_state(EnterParameters(arrow_function_parameter_flags(p, flags)), parse_binding)
.expect("Expected function parameter to be present as guaranteed by is_arrow_function_with_simple_parameter");
p.bump(T![=>]);
parse_arrow_body(p, flags).or_add_diagnostic(p, js_parse_error::expected_arrow_body);
Present(m.complete(p, JS_ARROW_FUNCTION_EXPRESSION))
}
fn is_arrow_function_with_single_parameter(p: &mut JsParser) -> bool {
if p.nth_at(1, T![=>]) {
is_at_identifier_binding(p) && !p.has_nth_preceding_line_break(1)
}
else {
p.at(T![async])
&& !p.has_nth_preceding_line_break(1)
&& is_nth_at_identifier_binding(p, 1)
&& !p.has_nth_preceding_line_break(2)
&& p.nth_at(2, T![=>])
}
}
fn parse_arrow_body(p: &mut JsParser, mut flags: SignatureFlags) -> ParsedSyntax {
if p.state().in_constructor() {
flags |= SignatureFlags::CONSTRUCTOR
}
if p.at(T!['{']) {
parse_function_body(p, flags)
} else {
p.with_state(EnterFunction(flags), |p| {
parse_assignment_expression_or_higher(p, ExpressionContext::default())
})
}
}
pub(crate) fn parse_any_parameter(
p: &mut JsParser,
decorator_list: ParsedSyntax,
parameter_context: ParameterContext,
expression_context: ExpressionContext,
type_context: TypeContext,
) -> ParsedSyntax {
let parameter = match p.cur() {
T![...] => parse_rest_parameter(p, decorator_list, expression_context, type_context),
T![this] => {
decorator_list
.add_diagnostic_if_present(p, decorators_not_allowed)
.map(|mut decorator_list| {
decorator_list.change_to_bogus(p);
decorator_list
});
parse_ts_this_parameter(p, type_context)
}
_ => parse_formal_parameter(
p,
decorator_list,
parameter_context,
expression_context,
type_context,
),
};
parameter.map(|mut parameter| {
if parameter.kind(p) == TS_THIS_PARAMETER {
if TypeScript.is_unsupported(p) {
parameter.change_to_bogus(p);
p.error(ts_only_syntax_error(
p,
"this parameter",
parameter.range(p),
));
} else if parameter_context.is_arrow_function() {
parameter.change_to_bogus(p);
p.error(p.err_builder(
"An arrow function cannot have a 'this' parameter.",
parameter.range(p),
));
}
}
parameter
})
}
pub(crate) fn parse_rest_parameter(
p: &mut JsParser,
decorator_list: ParsedSyntax,
expression_context: ExpressionContext,
type_context: TypeContext,
) -> ParsedSyntax {
if !p.at(T![...]) {
return Absent;
}
let m = decorator_list
.or_else(|| empty_decorator_list(p))
.precede(p);
p.bump(T![...]);
parse_binding_pattern(p, expression_context).or_add_diagnostic(p, expected_binding);
let mut valid = true;
if p.eat(T![?]) {
let err = p.err_builder("rest patterns cannot be optional", p.cur_range());
p.error(err);
valid = false;
}
TypeScript
.parse_exclusive_syntax(
p,
|p| parse_ts_type_annotation(p, type_context),
|p, annotation| ts_only_syntax_error(p, "type annotation", annotation.range(p)),
)
.ok();
if let Present(initializer) = parse_initializer_clause(p, ExpressionContext::default()) {
let err = p.err_builder(
"rest elements may not have default initializers",
initializer.range(p),
);
p.error(err);
valid = false;
}
let mut rest_parameter = m.complete(p, JS_REST_PARAMETER);
if p.at(T![,]) {
let err = p.err_builder(
"rest elements may not have trailing commas",
rest_parameter.range(p),
);
p.error(err);
valid = false;
}
if !valid {
rest_parameter.change_to_bogus(p);
}
Present(rest_parameter)
}
pub(crate) fn parse_ts_this_parameter(p: &mut JsParser, context: TypeContext) -> ParsedSyntax {
if !p.at(T![this]) {
return Absent;
}
let parameter = p.start();
p.expect(T![this]);
parse_ts_type_annotation(p, context).ok();
Present(parameter.complete(p, TS_THIS_PARAMETER))
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) enum ParameterContext {
ClassImplementation,
Implementation,
Declaration,
Setter,
ClassSetter,
Arrow,
ParameterProperty,
}
impl ParameterContext {
pub fn is_any_setter(&self) -> bool {
self.is_setter() || self.is_class_setter()
}
pub fn is_setter(&self) -> bool {
self == &ParameterContext::Setter
}
pub fn is_class_setter(&self) -> bool {
self == &ParameterContext::ClassSetter
}
pub fn is_class_method_implementation(&self) -> bool {
self == &ParameterContext::ClassImplementation
}
pub fn is_any_class_method(&self) -> bool {
self.is_class_method_implementation() || self.is_class_setter()
}
pub fn is_parameter_property(&self) -> bool {
self == &ParameterContext::ParameterProperty
}
pub fn is_arrow_function(&self) -> bool {
self == &ParameterContext::Arrow
}
}
pub(crate) fn parse_formal_parameter(
p: &mut JsParser,
decorator_list: ParsedSyntax,
parameter_context: ParameterContext,
expression_context: ExpressionContext,
type_context: TypeContext,
) -> ParsedSyntax {
let checkpoint = p.checkpoint();
let m = decorator_list
.or_else(|| empty_decorator_list(p))
.precede(p);
if let Present(binding) = parse_binding_pattern(p, expression_context) {
let binding_kind = binding.kind(p);
let binding_range = binding.range(p);
let mut valid = true;
let is_optional = if p.at(T![?]) {
if TypeScript.is_unsupported(p) {
p.error(ts_only_syntax_error(
p,
"optional parameters",
p.cur_range(),
));
valid = false;
} else if parameter_context.is_any_setter() {
p.error(p.err_builder(
"A 'set' accessor cannot have an optional parameter.",
p.cur_range(),
));
valid = false;
}
p.bump(T![?]);
true
} else {
false
};
if valid
&& matches!(
binding_kind,
JS_OBJECT_BINDING_PATTERN | JS_ARRAY_BINDING_PATTERN
)
&& parameter_context.is_parameter_property()
{
valid = false;
p.error(p.err_builder(
"A parameter property may not be declared using a binding pattern.",
binding_range,
));
}
TypeScript
.parse_exclusive_syntax(
p,
|p| parse_ts_type_annotation(p, type_context),
|p, annotation| ts_only_syntax_error(p, "Type annotations", annotation.range(p)),
)
.ok();
if let Present(initializer) = parse_initializer_clause(p, expression_context) {
if valid && parameter_context.is_any_setter() && TypeScript.is_supported(p) {
p.error(p.err_builder(
"A 'set' accessor parameter cannot have an initializer.",
initializer.range(p),
));
} else if is_optional && valid {
p.error(p.err_builder(
"Parameter cannot have question mark and initializer",
initializer.range(p),
));
}
}
let mut parameter = m.complete(p, JS_FORMAL_PARAMETER);
if !valid {
parameter.change_to_bogus(p);
}
Present(parameter)
} else {
m.abandon(p);
p.rewind(checkpoint);
Absent
}
}
pub(super) fn skip_parameter_start(p: &mut JsParser) -> bool {
if is_at_identifier_binding(p) || p.at(T![this]) {
p.bump_any();
return true;
}
if p.at(T!['[']) || p.at(T!['{']) {
let previous_error_count = p.context().diagnostics().len();
let pattern = parse_binding_pattern(p, ExpressionContext::default());
pattern.is_present() && p.context().diagnostics().len() == previous_error_count
} else {
false
}
}
pub(super) fn parse_parameter_list(
p: &mut JsParser,
parameter_context: ParameterContext,
type_context: TypeContext,
flags: SignatureFlags,
) -> ParsedSyntax {
if !p.at(T!['(']) {
return Absent;
}
let m = p.start();
parse_parameters_list(
p,
flags,
|p, expression_context| {
let decorator_list = parse_parameter_decorators(p);
let decorator_list = if parameter_context.is_any_class_method() {
decorator_list
} else {
decorator_list
.add_diagnostic_if_present(p, decorators_not_allowed)
.map(|mut decorator_list| {
decorator_list.change_to_bogus(p);
decorator_list
})
.into()
};
parse_any_parameter(
p,
decorator_list,
parameter_context,
expression_context,
type_context,
)
},
JS_PARAMETER_LIST,
);
Present(m.complete(p, JS_PARAMETERS))
}
pub(super) fn parse_parameters_list(
p: &mut JsParser,
flags: SignatureFlags,
parse_parameter: impl Fn(&mut JsParser, ExpressionContext) -> ParsedSyntax,
list_kind: JsSyntaxKind,
) {
let mut first = true;
let has_l_paren = p.expect(T!['(']);
p.with_state(EnterParameters(flags), |p| {
let parameters_list = p.start();
let mut progress = ParserProgress::default();
while !p.at(EOF) && !p.at(T![')']) {
if first {
first = false;
} else {
p.expect(T![,]);
}
if p.at(T![')']) {
break;
}
progress.assert_progressing(p);
let parameter = parse_parameter(
p,
ExpressionContext::default().and_object_expression_allowed(!first || has_l_paren),
);
if parameter.is_absent() && p.at(T![,]) {
parameter.or_add_diagnostic(p, expected_parameter);
continue;
}
let recovered_result = parameter.or_recover_with_token_set(
p,
&ParseRecoveryTokenSet::new(
JS_BOGUS_PARAMETER,
token_set![
T![ident],
T![await],
T![yield],
T![this],
T![,],
T!['['],
T![...],
T!['{'],
T![')'],
T![;],
],
)
.enable_recovery_on_line_break(),
js_parse_error::expected_parameter,
);
if recovered_result.is_err() {
break;
}
}
parameters_list.complete(p, list_kind);
});
p.expect(T![')']);
}