use oxc_allocator::{Box, Vec};
use oxc_ast::ast::*;
use oxc_span::{GetSpan, Span};
use oxc_str::Str;
use super::{VariableDeclarationParent, grammar::CoverGrammar};
use crate::{
Context, ParserConfig as Config, ParserImpl, StatementContext, diagnostics,
lexer::Kind,
modifiers::{ModifierKind, Modifiers},
};
impl<'a, C: Config> ParserImpl<'a, C> {
pub(crate) fn parse_hashbang(&mut self) -> Option<Hashbang<'a>> {
if self.cur_kind() == Kind::HashbangComment {
let span = self.start_span();
self.bump_any();
let span = self.end_span(span);
let src = &self.source_text[span.start as usize + 2..span.end as usize];
Some(self.ast.hashbang(span, Str::from(src)))
} else {
None
}
}
pub(crate) fn parse_directives_and_statements(
&mut self,
) -> (Vec<'a, Directive<'a>>, Vec<'a, Statement<'a>>) {
let mut directives = self.ast.vec();
let mut statements = self.ast.vec();
let is_top_level = self.ctx.has_top_level();
let stmt_ctx = StatementContext::StatementList;
let mut track_await_reparse =
is_top_level && self.source_type.is_unambiguous() && !self.ctx.has_await();
let mut expecting_directives = true;
while !self.has_fatal_error() {
if !is_top_level && self.at(Kind::RCurly) {
break;
}
if track_await_reparse && self.module_record_builder.has_module_syntax() {
track_await_reparse = false;
self.ctx = self.ctx.and_await(true);
}
let checkpoint = if track_await_reparse {
self.state.encountered_await_identifier = false;
Some((statements.len(), self.checkpoint()))
} else {
None
};
let stmt = self.parse_statement_list_item(stmt_ctx);
if let Some((stmt_index, checkpoint)) = checkpoint
&& self.state.encountered_await_identifier
{
self.state.potential_await_reparse.push((stmt_index, checkpoint));
}
if expecting_directives {
if let Statement::ExpressionStatement(expr) = &stmt
&& let Expression::StringLiteral(string) = &expr.expression
{
if expr.span.start == string.span.start {
let src = &self.source_text
[string.span.start as usize + 1..string.span.end as usize - 1];
let directive =
self.ast.directive(expr.span, (*string).clone(), Str::from(src));
directives.push(directive);
continue;
}
}
expecting_directives = false;
}
statements.push(stmt);
}
(directives, statements)
}
pub(crate) fn parse_statement_list_item(
&mut self,
stmt_ctx: StatementContext,
) -> Statement<'a> {
let has_no_side_effects_comment =
self.lexer.trivia_builder.previous_token_has_no_side_effects_comment();
let pure_comment_index = self.lexer.trivia_builder.previous_token_has_pure_comment();
let mut stmt = match self.cur_kind() {
Kind::LCurly => self.parse_block_statement(),
Kind::Semicolon => self.parse_empty_statement(),
Kind::If => self.parse_if_statement(),
Kind::Do => self.parse_do_while_statement(),
Kind::While => self.parse_while_statement(),
Kind::For => self.parse_for_statement(),
Kind::Continue => self.parse_continue_statement(),
Kind::Break => self.parse_break_statement(),
Kind::With => self.parse_with_statement(),
Kind::Switch => self.parse_switch_statement(),
Kind::Throw => self.parse_throw_statement(),
Kind::Try => self.parse_try_statement(),
Kind::Debugger => self.parse_debugger_statement(),
Kind::Class => self.parse_class_statement(
self.start_span(),
stmt_ctx,
&Modifiers::empty(),
self.ast.vec(),
),
Kind::Export => self.parse_export_declaration(self.start_span(), self.ast.vec()),
Kind::Return => self.parse_return_statement(),
Kind::Var => {
let span = self.start_span();
self.bump_any();
self.parse_variable_statement(span, VariableDeclarationKind::Var, stmt_ctx)
}
Kind::Function => {
self.parse_function_declaration(self.start_span(), false, stmt_ctx)
}
Kind::At => self.parse_decorated_statement(stmt_ctx),
Kind::Let if !self.cur_token().escaped() => self.parse_let(stmt_ctx),
Kind::Async => self.parse_async_statement(self.start_span(), stmt_ctx),
Kind::Import => self.parse_import_statement(),
Kind::Const => self.parse_const_statement(stmt_ctx),
Kind::Using if self.is_using_declaration() => self.parse_using_statement(stmt_ctx),
Kind::Await if self.is_using_statement() => self.parse_using_statement(stmt_ctx),
Kind::Interface
| Kind::Type
| Kind::Module
| Kind::Namespace
| Kind::Declare
| Kind::Enum
| Kind::Private
| Kind::Protected
| Kind::Public
| Kind::Abstract
| Kind::Accessor
| Kind::Static
| Kind::Readonly
| Kind::Global
if self.is_ts && self.at_start_of_ts_declaration() =>
{
self.parse_ts_declaration_statement(self.start_span())
}
_ => self.parse_expression_or_labeled_statement(),
};
if let Some(index) = pure_comment_index
&& !matches!(stmt, Statement::ExpressionStatement(_))
{
self.lexer.trivia_builder.mark_pure_comment_not_applied(index);
}
if has_no_side_effects_comment {
Self::set_pure_on_function_stmt(&mut stmt);
}
stmt
}
fn set_pure_on_function_stmt(stmt: &mut Statement<'a>) {
match stmt {
Statement::FunctionDeclaration(func) => {
func.pure = true;
}
Statement::ExportDefaultDeclaration(decl) => match &mut decl.declaration {
ExportDefaultDeclarationKind::FunctionExpression(func)
| ExportDefaultDeclarationKind::FunctionDeclaration(func) => {
func.pure = true;
}
ExportDefaultDeclarationKind::ArrowFunctionExpression(func) => {
func.pure = true;
}
_ => {}
},
Statement::ExportNamedDeclaration(decl) => match &mut decl.declaration {
Some(Declaration::FunctionDeclaration(func)) => {
func.pure = true;
}
Some(Declaration::VariableDeclaration(var_decl)) if var_decl.kind.is_const() => {
if let Some(Some(expr)) = var_decl.declarations.first_mut().map(|d| &mut d.init)
{
Self::set_pure_on_function_expr(expr);
}
}
_ => {}
},
Statement::VariableDeclaration(var_decl) if var_decl.kind.is_const() => {
if let Some(Some(expr)) = var_decl.declarations.first_mut().map(|d| &mut d.init) {
Self::set_pure_on_function_expr(expr);
}
}
_ => {}
}
}
fn parse_expression_or_labeled_statement(&mut self) -> Statement<'a> {
let span = self.start_span();
let expr = self.parse_expr();
if let Expression::Identifier(ident) = &expr {
if self.eat(Kind::Colon) {
let label = self.ast.label_identifier(ident.span, ident.name);
let body = self.parse_statement_list_item(StatementContext::Label);
return self.ast.statement_labeled(self.end_span(span), label, body);
}
}
self.parse_expression_statement(span, expr)
}
pub(crate) fn parse_block(&mut self) -> Box<'a, BlockStatement<'a>> {
let span = self.start_span();
let body = self.parse_normal_list(Kind::LCurly, Kind::RCurly, |p| {
p.parse_statement_list_item(StatementContext::StatementList)
});
self.ast.alloc_block_statement(self.end_span(span), body)
}
pub(crate) fn parse_block_statement(&mut self) -> Statement<'a> {
let block = self.parse_block();
Statement::BlockStatement(block)
}
pub(crate) fn parse_variable_statement(
&mut self,
start_span: u32,
kind: VariableDeclarationKind,
stmt_ctx: StatementContext,
) -> Statement<'a> {
let decl = self.parse_variable_declaration(
start_span,
kind,
VariableDeclarationParent::Statement,
false,
);
if stmt_ctx.is_single_statement() && decl.kind.is_lexical() {
self.error(diagnostics::lexical_declaration_single_statement(decl.span));
}
Statement::VariableDeclaration(decl)
}
fn parse_empty_statement(&mut self) -> Statement<'a> {
let span = self.start_span();
self.bump_any(); self.ast.statement_empty(self.end_span(span))
}
pub(crate) fn parse_expression_statement(
&mut self,
span: u32,
expression: Expression<'a>,
) -> Statement<'a> {
self.asi();
self.ast.statement_expression(self.end_span(span), expression)
}
fn parse_if_statement(&mut self) -> Statement<'a> {
let span = self.start_span();
self.bump_any(); let test = self.parse_paren_expression();
let consequent = self.parse_statement_list_item(StatementContext::If);
let alternate =
self.eat(Kind::Else).then(|| self.parse_statement_list_item(StatementContext::If));
self.ast.statement_if(self.end_span(span), test, consequent, alternate)
}
fn parse_do_while_statement(&mut self) -> Statement<'a> {
let span = self.start_span();
self.bump_any(); let body = self.parse_statement_list_item(StatementContext::Do);
self.expect(Kind::While);
let test = self.parse_paren_expression();
self.bump(Kind::Semicolon);
self.ast.statement_do_while(self.end_span(span), body, test)
}
fn parse_while_statement(&mut self) -> Statement<'a> {
let span = self.start_span();
self.bump_any(); let test = self.parse_paren_expression();
let body = self.parse_statement_list_item(StatementContext::While);
self.ast.statement_while(self.end_span(span), test, body)
}
fn parse_for_statement(&mut self) -> Statement<'a> {
let span = self.start_span();
self.bump_any();
let r#await = if self.at(Kind::Await) {
if !self.ctx.has_await() {
self.error_on_script(diagnostics::await_expression(self.cur_token().span()));
}
self.bump_any();
true
} else {
false
};
let parenthesis_opening_span = self.cur_token().span();
self.expect(Kind::LParen);
if self.at(Kind::Semicolon) {
return self.parse_for_loop(span, parenthesis_opening_span, None, r#await);
}
match self.cur_kind() {
Kind::Const => {
let start_span = self.start_span();
self.bump_any();
return self.parse_variable_declaration_for_statement(
span,
start_span,
parenthesis_opening_span,
VariableDeclarationKind::Const,
r#await,
);
}
Kind::Var => {
let start_span = self.start_span();
self.bump_any();
return self.parse_variable_declaration_for_statement(
span,
start_span,
parenthesis_opening_span,
VariableDeclarationKind::Var,
r#await,
);
}
Kind::Let => {
let start_span = self.start_span();
let checkpoint = self.checkpoint();
self.bump_any(); if self.cur_kind().is_after_let() {
return self.parse_variable_declaration_for_statement(
span,
start_span,
parenthesis_opening_span,
VariableDeclarationKind::Let,
r#await,
);
}
self.rewind(checkpoint);
}
_ => {}
}
if self.at(Kind::Await)
&& self.lookahead(|p| {
p.bump_any();
if !p.at(Kind::Using) || p.cur_token().is_on_new_line() {
return false;
}
p.bump_any();
!p.cur_token().is_on_new_line()
})
{
return self.parse_using_declaration_for_statement(
span,
parenthesis_opening_span,
r#await,
);
}
if self.at(Kind::Using)
&& self.lookahead(|p| {
p.bump_any(); let token = p.cur_token();
if token.is_on_new_line() {
return false;
}
let kind = token.kind();
if kind == Kind::Of {
p.bump_any(); return matches!(p.cur_kind(), Kind::Eq | Kind::Semicolon | Kind::Colon);
}
kind.is_binding_identifier()
})
{
return self.parse_using_declaration_for_statement(
span,
parenthesis_opening_span,
r#await,
);
}
if self.at(Kind::RParen) {
return self.parse_for_loop(span, parenthesis_opening_span, None, r#await);
}
let is_let = self.at(Kind::Let);
let is_async = self.at(Kind::Async) && !self.cur_token().escaped();
let expr_span = self.start_span();
let init_expression = self.context_remove(Context::In, ParserImpl::parse_expr);
match self.cur_kind() {
Kind::In => {
let target = AssignmentTarget::cover(init_expression, self);
let for_stmt_left = ForStatementLeft::from(target);
self.parse_for_in_loop(span, parenthesis_opening_span, r#await, for_stmt_left)
}
Kind::Of => {
if !r#await && is_async && init_expression.is_identifier_reference() {
self.error(diagnostics::for_loop_async_of(self.end_span(expr_span)));
}
if is_let {
self.error(diagnostics::for_loop_let_reserved_word(self.end_span(expr_span)));
}
let target = AssignmentTarget::cover(init_expression, self);
let for_stmt_left = ForStatementLeft::from(target);
self.parse_for_of_loop(span, parenthesis_opening_span, r#await, for_stmt_left)
}
_ => self.parse_for_loop(
span,
parenthesis_opening_span,
Some(ForStatementInit::from(init_expression)),
r#await,
),
}
}
fn parse_variable_declaration_for_statement(
&mut self,
span: u32,
start_span: u32,
parenthesis_opening_span: Span,
decl_kind: VariableDeclarationKind,
r#await: bool,
) -> Statement<'a> {
let init_declaration = self.context_remove(Context::In, |p| {
p.parse_variable_declaration(
start_span,
decl_kind,
VariableDeclarationParent::For,
false,
)
});
self.parse_any_for_loop(span, parenthesis_opening_span, init_declaration, r#await)
}
pub(crate) fn is_using_declaration(&mut self) -> bool {
let token = self.lexer.peek_token();
let kind = token.kind();
(kind.is_binding_identifier() || kind == Kind::LCurly) && !token.is_on_new_line()
}
fn parse_using_declaration_for_statement(
&mut self,
span: u32,
parenthesis_opening_span: Span,
r#await: bool,
) -> Statement<'a> {
let using_decl = self.parse_using_declaration(StatementContext::For);
if matches!(self.cur_kind(), Kind::In) {
if using_decl.kind.is_await() {
self.error(diagnostics::await_using_declaration_not_allowed_in_for_in_statement(
using_decl.span,
));
} else {
self.error(diagnostics::using_declaration_not_allowed_in_for_in_statement(
using_decl.span,
));
}
}
let init_declaration = self.alloc(using_decl);
self.parse_any_for_loop(span, parenthesis_opening_span, init_declaration, r#await)
}
fn parse_any_for_loop(
&mut self,
span: u32,
parenthesis_opening_span: Span,
init_declaration: Box<'a, VariableDeclaration<'a>>,
r#await: bool,
) -> Statement<'a> {
match self.cur_kind() {
Kind::In => self.parse_for_in_loop(
span,
parenthesis_opening_span,
r#await,
ForStatementLeft::VariableDeclaration(init_declaration),
),
Kind::Of => self.parse_for_of_loop(
span,
parenthesis_opening_span,
r#await,
ForStatementLeft::VariableDeclaration(init_declaration),
),
_ => self.parse_for_loop(
span,
parenthesis_opening_span,
Some(ForStatementInit::VariableDeclaration(init_declaration)),
r#await,
),
}
}
fn parse_for_loop(
&mut self,
span: u32,
parenthesis_opening_span: Span,
init: Option<ForStatementInit<'a>>,
r#await: bool,
) -> Statement<'a> {
self.expect(Kind::Semicolon);
if let Some(ForStatementInit::VariableDeclaration(decl)) = &init {
for d in &decl.declarations {
self.check_missing_initializer(d);
}
}
let test = if matches!(self.cur_kind(), Kind::Semicolon | Kind::RParen) {
None
} else {
Some(self.context_add(Context::In, ParserImpl::parse_expr))
};
self.expect(Kind::Semicolon);
let update = if self.at(Kind::RParen) {
None
} else {
Some(self.context_add(Context::In, ParserImpl::parse_expr))
};
self.expect_closing(Kind::RParen, parenthesis_opening_span);
if r#await {
self.error(diagnostics::for_await(self.end_span(span)));
}
let body = self.parse_statement_list_item(StatementContext::For);
self.ast.statement_for(self.end_span(span), init, test, update, body)
}
fn parse_for_in_loop(
&mut self,
span: u32,
parenthesis_opening_span: Span,
r#await: bool,
left: ForStatementLeft<'a>,
) -> Statement<'a> {
self.bump_any(); let right = self.parse_expr();
self.expect_closing(Kind::RParen, parenthesis_opening_span);
if r#await {
self.error(diagnostics::for_await(self.end_span(span)));
}
let body = self.parse_statement_list_item(StatementContext::For);
let span = self.end_span(span);
self.ast.statement_for_in(span, left, right, body)
}
fn parse_for_of_loop(
&mut self,
span: u32,
parenthesis_opening_span: Span,
r#await: bool,
left: ForStatementLeft<'a>,
) -> Statement<'a> {
self.bump_any(); let right = self.parse_assignment_expression_or_higher();
self.expect_closing(Kind::RParen, parenthesis_opening_span);
let body = self.parse_statement_list_item(StatementContext::For);
let span = self.end_span(span);
self.ast.statement_for_of(span, r#await, left, right, body)
}
fn parse_continue_statement(&mut self) -> Statement<'a> {
let span = self.start_span();
self.bump_any(); let label =
if self.can_insert_semicolon() { None } else { Some(self.parse_label_identifier()) };
self.asi();
self.ast.statement_continue(self.end_span(span), label)
}
fn parse_break_statement(&mut self) -> Statement<'a> {
let span = self.start_span();
self.bump_any(); let label =
if self.can_insert_semicolon() { None } else { Some(self.parse_label_identifier()) };
self.asi();
self.ast.statement_break(self.end_span(span), label)
}
fn parse_return_statement(&mut self) -> Statement<'a> {
let span = self.start_span();
self.bump_any(); let argument = if self.eat(Kind::Semicolon) || self.can_insert_semicolon() {
None
} else {
let expr = self.context_add(Context::In, ParserImpl::parse_expr);
self.asi();
Some(expr)
};
if !self.ctx.has_return() {
self.error(diagnostics::return_statement_only_in_function_body(Span::sized(span, 6)));
}
self.ast.statement_return(self.end_span(span), argument)
}
fn parse_with_statement(&mut self) -> Statement<'a> {
let span = self.start_span();
self.bump_any(); let object = self.parse_paren_expression();
let body = self.parse_statement_list_item(StatementContext::With);
let span = self.end_span(span);
self.ast.statement_with(span, object, body)
}
fn parse_switch_statement(&mut self) -> Statement<'a> {
let span = self.start_span();
self.bump_any(); let discriminant = self.parse_paren_expression();
let cases = self.parse_normal_list(Kind::LCurly, Kind::RCurly, Self::parse_switch_case);
self.ast.statement_switch(self.end_span(span), discriminant, cases)
}
pub(crate) fn parse_switch_case(&mut self) -> SwitchCase<'a> {
let span = self.start_span();
let test = match self.cur_kind() {
Kind::Default => {
self.bump_any();
None
}
Kind::Case => {
self.bump_any();
let expression = self.parse_expr();
Some(expression)
}
_ => {
return self
.fatal_error(diagnostics::expect_switch_clause(self.cur_token().span()));
}
};
self.expect(Kind::Colon);
let mut consequent = self.ast.vec();
loop {
let kind = self.cur_kind();
if matches!(
kind,
Kind::Case | Kind::Default | Kind::RCurly | Kind::Eof | Kind::Undetermined
) || self.fatal_error.is_some()
{
break;
}
let stmt = self.parse_statement_list_item(StatementContext::StatementList);
if let Statement::VariableDeclaration(var_decl) = &stmt
&& var_decl.kind.is_using()
{
self.error(diagnostics::using_declaration_not_allowed_in_switch_bare_case(
stmt.span(),
));
}
consequent.push(stmt);
}
self.ast.switch_case(self.end_span(span), test, consequent)
}
fn parse_throw_statement(&mut self) -> Statement<'a> {
let span = self.start_span();
self.bump_any(); if self.cur_token().is_on_new_line() {
self.error(diagnostics::illegal_newline(
"throw",
self.end_span(span),
self.cur_token().span(),
));
}
let argument = self.parse_expr();
self.asi();
self.ast.statement_throw(self.end_span(span), argument)
}
fn parse_try_statement(&mut self) -> Statement<'a> {
let span = self.start_span();
self.bump_any();
let block = self.parse_block();
let handler = self.at(Kind::Catch).then(|| self.parse_catch_clause());
let finalizer = self.eat(Kind::Finally).then(|| self.parse_block());
if handler.is_none() && finalizer.is_none() {
let range = Span::empty(block.span.end);
self.error(diagnostics::expect_catch_finally(range));
}
self.ast.statement_try(self.end_span(span), block, handler, finalizer)
}
fn parse_catch_clause(&mut self) -> Box<'a, CatchClause<'a>> {
let span = self.start_span();
self.bump_any(); let pattern = if self.eat(Kind::LParen) {
let (pattern, type_annotation) = self.parse_binding_pattern_with_type_annotation();
self.expect(Kind::RParen);
Some((pattern, type_annotation))
} else {
None
};
let body = self.parse_block();
let param = pattern.map(|(pattern, type_annotation)| {
self.ast.catch_parameter(
Span::new(
pattern.span().start,
type_annotation.as_ref().map_or(pattern.span().end, |ta| ta.span.end),
),
pattern,
type_annotation,
)
});
self.ast.alloc_catch_clause(self.end_span(span), param, body)
}
fn parse_debugger_statement(&mut self) -> Statement<'a> {
let span = self.start_span();
self.bump_any();
self.asi();
self.ast.statement_debugger(self.end_span(span))
}
fn parse_const_statement(&mut self, stmt_ctx: StatementContext) -> Statement<'a> {
let span = self.start_span();
self.bump_any();
if self.is_ts && self.at(Kind::Enum) {
let modifiers = Modifiers::new_single(ModifierKind::Const, span);
Statement::from(self.parse_ts_enum_declaration(span, &modifiers))
} else {
self.parse_variable_statement(span, VariableDeclarationKind::Const, stmt_ctx)
}
}
fn parse_import_statement(&mut self) -> Statement<'a> {
let checkpoint = self.checkpoint();
let span = self.start_span();
self.bump_any();
if matches!(self.cur_kind(), Kind::Dot | Kind::LParen) {
self.rewind(checkpoint);
self.parse_expression_or_labeled_statement()
} else {
self.parse_import_declaration(span, self.ctx.has_top_level())
}
}
fn parse_async_statement(&mut self, span: u32, stmt_ctx: StatementContext) -> Statement<'a> {
let checkpoint = self.checkpoint();
self.bump_any(); let token = self.cur_token();
if token.kind() == Kind::Function && !token.is_on_new_line() {
return self.parse_function_declaration(span, true, stmt_ctx);
}
self.rewind(checkpoint);
if self.is_ts && self.at_start_of_ts_declaration() {
return self.parse_ts_declaration_statement(span);
}
self.parse_expression_or_labeled_statement()
}
fn parse_decorated_statement(&mut self, stmt_ctx: StatementContext) -> Statement<'a> {
let span = self.start_span();
let decorators = self.parse_decorators();
let kind = self.cur_kind();
if kind == Kind::Export {
return self.parse_export_declaration(self.start_span(), decorators);
}
let modifiers = self.parse_modifiers(false, false);
if self.at(Kind::Class) {
return self.parse_class_statement(span, stmt_ctx, &modifiers, decorators);
}
self.unexpected()
}
}