mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
refactor(parser): start porting arrow function parsing from tsc (#3340)
relates #3320
This commit is contained in:
parent
17f4b199b9
commit
9ced605487
16 changed files with 372 additions and 331 deletions
|
|
@ -28,7 +28,7 @@ fn main() -> std::io::Result<()> {
|
|||
println!("Original:");
|
||||
println!("{source_text}");
|
||||
|
||||
let options = CodegenOptions::default();
|
||||
let options = CodegenOptions { enable_source_map: false, enable_typescript: true };
|
||||
let printed =
|
||||
Codegen::<false>::new("", &source_text, options.clone()).build(&ret.program).source_text;
|
||||
println!("Printed:");
|
||||
|
|
|
|||
|
|
@ -15,7 +15,10 @@ fn main() -> Result<(), String> {
|
|||
let source_text = std::fs::read_to_string(path).map_err(|_| format!("Missing '{name}'"))?;
|
||||
let allocator = Allocator::default();
|
||||
let source_type = SourceType::from_path(path).unwrap();
|
||||
let now = std::time::Instant::now();
|
||||
let ret = Parser::new(&allocator, &source_text, source_type).parse();
|
||||
let elapsed_time = now.elapsed();
|
||||
println!("{} seconds.", elapsed_time.as_millis());
|
||||
|
||||
println!("AST:");
|
||||
println!("{}", serde_json::to_string_pretty(&ret.program).unwrap());
|
||||
|
|
|
|||
309
crates/oxc_parser/src/js/arrow.rs
Normal file
309
crates/oxc_parser/src/js/arrow.rs
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
use oxc_allocator::Box;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_diagnostics::Result;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
|
||||
use crate::{diagnostics, lexer::Kind, AstBuilder, ParserImpl};
|
||||
|
||||
use super::Tristate;
|
||||
|
||||
type ArrowFunctionHead<'a> = (
|
||||
Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
|
||||
Box<'a, FormalParameters<'a>>,
|
||||
Option<Box<'a, TSTypeAnnotation<'a>>>,
|
||||
bool,
|
||||
Span,
|
||||
);
|
||||
|
||||
impl<'a> ParserImpl<'a> {
|
||||
pub(crate) fn is_parenthesized_arrow_function_expression(&mut self, r#async: bool) -> Tristate {
|
||||
let offset = u8::from(r#async);
|
||||
|
||||
match self.nth_kind(offset) {
|
||||
Kind::LParen => match self.nth_kind(offset + 1) {
|
||||
// '()' is an arrow expression if followed by an '=>', a type annotation or body.
|
||||
// Otherwise, a parenthesized expression with a missing inner expression
|
||||
Kind::RParen => {
|
||||
let kind = self.nth_kind(offset + 2);
|
||||
if self.ts_enabled() && kind == Kind::Colon {
|
||||
Tristate::Maybe
|
||||
} else if matches!(kind, Kind::Arrow | Kind::LCurly) {
|
||||
Tristate::True
|
||||
} else {
|
||||
Tristate::False
|
||||
}
|
||||
}
|
||||
// Rest parameter
|
||||
// '(...ident' is not a parenthesized expression
|
||||
// '(...null' is a parenthesized expression
|
||||
Kind::Dot3 => match self.nth_kind(offset + 1) {
|
||||
Kind::Ident => Tristate::True,
|
||||
kind if kind.is_literal() => Tristate::False,
|
||||
_ => Tristate::Maybe,
|
||||
},
|
||||
// '([ ...', '({ ... } can either be a parenthesized object or array expression or a destructing parameter
|
||||
Kind::LBrack | Kind::LCurly => Tristate::Maybe,
|
||||
_ if self.nth_kind(offset + 1).is_binding_identifier()
|
||||
|| self.nth_at(offset + 1, Kind::This) =>
|
||||
{
|
||||
match self.nth_kind(offset + 2) {
|
||||
// '(a: ' must be a type annotation
|
||||
Kind::Colon => Tristate::True,
|
||||
// * '(a = ': an initializer or a parenthesized assignment expression
|
||||
// * '(a, ': separator to next parameter or a parenthesized sequence expression
|
||||
// * '(a)': a single parameter OR a parenthesized expression
|
||||
Kind::Eq | Kind::Comma | Kind::RParen => Tristate::Maybe,
|
||||
// '(a?:' | '(a?,' | '(a?=' | '(a?)'
|
||||
Kind::Question
|
||||
if matches!(
|
||||
self.nth_kind(offset + 3),
|
||||
Kind::Colon | Kind::Comma | Kind::Eq | Kind::RParen
|
||||
) =>
|
||||
{
|
||||
Tristate::True
|
||||
}
|
||||
_ => Tristate::False,
|
||||
}
|
||||
}
|
||||
_ => Tristate::False,
|
||||
},
|
||||
Kind::LAngle => {
|
||||
let kind = self.nth_kind(offset + 1);
|
||||
|
||||
// `<const` for const type parameter from TypeScript 5.0
|
||||
if kind == Kind::Const {
|
||||
return Tristate::Maybe;
|
||||
}
|
||||
|
||||
if !kind.is_identifier() {
|
||||
return Tristate::False;
|
||||
}
|
||||
|
||||
if self.source_type.is_jsx() {
|
||||
return match self.nth_kind(offset + 2) {
|
||||
Kind::Extends => {
|
||||
let third_kind = self.nth_kind(offset + 3);
|
||||
if matches!(third_kind, Kind::Eq | Kind::RAngle) {
|
||||
Tristate::False
|
||||
} else if third_kind.is_identifier() {
|
||||
Tristate::Maybe
|
||||
} else {
|
||||
Tristate::True
|
||||
}
|
||||
}
|
||||
Kind::Eq | Kind::Comma => Tristate::True,
|
||||
_ => Tristate::False,
|
||||
};
|
||||
}
|
||||
|
||||
Tristate::Maybe
|
||||
}
|
||||
_ => Tristate::False,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_parenthesized_arrow_function(&mut self) -> Tristate {
|
||||
match self.cur_kind() {
|
||||
Kind::LAngle | Kind::LParen => self.is_parenthesized_arrow_function_expression(false),
|
||||
Kind::Async => {
|
||||
let peeked = self.peek_token();
|
||||
if !peeked.is_on_new_line && matches!(peeked.kind, Kind::LAngle | Kind::LParen) {
|
||||
self.is_parenthesized_arrow_function_expression(true)
|
||||
} else {
|
||||
Tristate::False
|
||||
}
|
||||
}
|
||||
_ => Tristate::False,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_parenthesized_arrow_function_head(
|
||||
&mut self,
|
||||
) -> Result<ArrowFunctionHead<'a>> {
|
||||
let span = self.start_span();
|
||||
let r#async = self.eat(Kind::Async);
|
||||
|
||||
let has_await = self.ctx.has_await();
|
||||
self.ctx = self.ctx.union_await_if(r#async);
|
||||
|
||||
let type_parameters = self.parse_ts_type_parameters()?;
|
||||
|
||||
let (this_param, params) =
|
||||
self.parse_formal_parameters(FormalParameterKind::ArrowFormalParameters)?;
|
||||
|
||||
if let Some(this_param) = this_param {
|
||||
// const x = (this: number) => {};
|
||||
self.error(diagnostics::ts_arrow_function_this_parameter(this_param.span));
|
||||
}
|
||||
|
||||
let return_type = self.parse_ts_return_type_annotation()?;
|
||||
|
||||
self.ctx = self.ctx.and_await(has_await);
|
||||
|
||||
if self.cur_token().is_on_new_line {
|
||||
self.error(diagnostics::lineterminator_before_arrow(self.cur_token().span()));
|
||||
}
|
||||
|
||||
self.expect(Kind::Arrow)?;
|
||||
|
||||
Ok((type_parameters, params, return_type, r#async, span))
|
||||
}
|
||||
|
||||
/// [ConciseBody](https://tc39.es/ecma262/#prod-ConciseBody)
|
||||
/// [lookahead ≠ {] `ExpressionBody`[?In, ~Await]
|
||||
/// { `FunctionBody`[~Yield, ~Await] }
|
||||
/// `ExpressionBody`[In, Await] :
|
||||
/// `AssignmentExpression`[?In, ~Yield, ?Await]
|
||||
pub(crate) fn parse_arrow_function_body(
|
||||
&mut self,
|
||||
span: Span,
|
||||
type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
|
||||
params: Box<'a, FormalParameters<'a>>,
|
||||
return_type: Option<Box<'a, TSTypeAnnotation<'a>>>,
|
||||
r#async: bool,
|
||||
) -> Result<Expression<'a>> {
|
||||
let has_await = self.ctx.has_await();
|
||||
let has_yield = self.ctx.has_yield();
|
||||
self.ctx = self.ctx.and_await(r#async).and_yield(false);
|
||||
|
||||
let expression = !self.at(Kind::LCurly);
|
||||
let body = if expression {
|
||||
let expr = self.parse_assignment_expression_or_higher()?;
|
||||
let span = expr.span();
|
||||
let expr_stmt = self.ast.expression_statement(span, expr);
|
||||
self.ast.function_body(span, self.ast.new_vec(), self.ast.new_vec_single(expr_stmt))
|
||||
} else {
|
||||
self.parse_function_body()?
|
||||
};
|
||||
|
||||
self.ctx = self.ctx.and_await(has_await).and_yield(has_yield);
|
||||
|
||||
Ok(self.ast.arrow_function_expression(
|
||||
self.end_span(span),
|
||||
expression,
|
||||
r#async,
|
||||
params,
|
||||
body,
|
||||
type_parameters,
|
||||
return_type,
|
||||
))
|
||||
}
|
||||
|
||||
/// Section [Arrow Function](https://tc39.es/ecma262/#sec-arrow-function-definitions)
|
||||
/// `ArrowFunction`[In, Yield, Await] :
|
||||
/// `ArrowParameters`[?Yield, ?Await] [no `LineTerminator` here] => `ConciseBody`[?In]
|
||||
pub(crate) fn parse_parenthesized_arrow_function(&mut self) -> Result<Option<Expression<'a>>> {
|
||||
let (type_parameters, params, return_type, r#async, span) =
|
||||
self.parse_parenthesized_arrow_function_head()?;
|
||||
self.parse_arrow_function_body(span, type_parameters, params, return_type, r#async)
|
||||
.map(Some)
|
||||
}
|
||||
|
||||
pub(crate) fn parse_single_param_function_expression(
|
||||
&mut self,
|
||||
span: Span,
|
||||
r#async: bool,
|
||||
generator: bool,
|
||||
) -> Result<Expression<'a>> {
|
||||
let has_await = self.ctx.has_await();
|
||||
let has_yield = self.ctx.has_yield();
|
||||
|
||||
self.ctx = self.ctx.union_await_if(r#async).union_yield_if(generator);
|
||||
let params_span = self.start_span();
|
||||
let param = self.parse_binding_identifier()?;
|
||||
let ident = self.ast.binding_pattern_identifier(param);
|
||||
let pattern = self.ast.binding_pattern(ident, None, false);
|
||||
let params_span = self.end_span(params_span);
|
||||
let formal_parameter = self.ast.formal_parameter(
|
||||
params_span,
|
||||
pattern,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
AstBuilder::new_vec(&self.ast),
|
||||
);
|
||||
let params = self.ast.formal_parameters(
|
||||
params_span,
|
||||
FormalParameterKind::ArrowFormalParameters,
|
||||
self.ast.new_vec_single(formal_parameter),
|
||||
None,
|
||||
);
|
||||
|
||||
self.expect(Kind::Arrow)?;
|
||||
|
||||
self.ctx = self.ctx.and_await(r#async).and_yield(generator);
|
||||
let expression = !self.at(Kind::LCurly);
|
||||
let body = if expression {
|
||||
let expr = self.parse_assignment_expression_or_higher()?;
|
||||
let span = expr.span();
|
||||
let expr_stmt = self.ast.expression_statement(span, expr);
|
||||
self.ast.function_body(span, self.ast.new_vec(), self.ast.new_vec_single(expr_stmt))
|
||||
} else {
|
||||
self.parse_function_body()?
|
||||
};
|
||||
self.ctx = self.ctx.and_await(has_await).and_yield(has_yield);
|
||||
|
||||
Ok(self.ast.arrow_function_expression(
|
||||
self.end_span(span),
|
||||
expression,
|
||||
r#async,
|
||||
params,
|
||||
body,
|
||||
None,
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn parse_possible_parenthesized_arrow_function_expression(
|
||||
&mut self,
|
||||
) -> Result<Option<Expression<'a>>> {
|
||||
let pos = self.cur_token().start;
|
||||
if self.state.not_parenthesized_arrow.contains(&pos) {
|
||||
return Ok(None);
|
||||
}
|
||||
if let Ok((type_parameters, params, return_type, r#async, span)) =
|
||||
self.try_parse(ParserImpl::parse_parenthesized_arrow_function_head)
|
||||
{
|
||||
return self
|
||||
.parse_arrow_function_body(span, type_parameters, params, return_type, r#async)
|
||||
.map(Some);
|
||||
}
|
||||
self.state.not_parenthesized_arrow.insert(pos);
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub(crate) fn try_parse_parenthesized_arrow_function_expression(
|
||||
&mut self,
|
||||
) -> Result<Option<Expression<'a>>> {
|
||||
match self.is_parenthesized_arrow_function() {
|
||||
Tristate::False => Ok(None),
|
||||
Tristate::True => self.parse_parenthesized_arrow_function(),
|
||||
Tristate::Maybe => self.parse_possible_parenthesized_arrow_function_expression(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_parse_async_simple_arrow_function_expression(
|
||||
&mut self,
|
||||
) -> Result<Option<Expression<'a>>> {
|
||||
let span = self.start_span();
|
||||
if self.cur_kind().is_binding_identifier()
|
||||
&& self.peek_at(Kind::Arrow)
|
||||
&& !self.peek_token().is_on_new_line
|
||||
{
|
||||
self.parse_single_param_function_expression(span, false, false).map(Some)
|
||||
} else if self.at_async_no_new_line()
|
||||
&& self.peek_kind().is_binding_identifier()
|
||||
&& !self.peek_token().is_on_new_line
|
||||
&& self.nth_at(2, Kind::Arrow)
|
||||
{
|
||||
self.bump_any(); // bump async
|
||||
let arrow_token = self.peek_token();
|
||||
if arrow_token.is_on_new_line {
|
||||
self.error(diagnostics::no_line_break_is_allowed_before_arrow(arrow_token.span()));
|
||||
}
|
||||
self.parse_single_param_function_expression(span, true, false).map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -132,7 +132,7 @@ impl<'a> ParserImpl<'a> {
|
|||
left: BindingPattern<'a>,
|
||||
) -> Result<BindingPattern<'a>> {
|
||||
if self.eat(Kind::Eq) {
|
||||
let expr = self.parse_assignment_expression_base()?;
|
||||
let expr = self.parse_assignment_expression_or_higher()?;
|
||||
Ok(self.ast.assignment_pattern(self.end_span(span), left, expr))
|
||||
} else {
|
||||
Ok(left)
|
||||
|
|
|
|||
|
|
@ -467,7 +467,7 @@ impl<'a> ParserImpl<'a> {
|
|||
r#abstract: bool,
|
||||
) -> Result<ClassElement<'a>> {
|
||||
let value =
|
||||
self.eat(Kind::Eq).then(|| self.parse_assignment_expression_base()).transpose()?;
|
||||
self.eat(Kind::Eq).then(|| self.parse_assignment_expression_or_higher()).transpose()?;
|
||||
let r#type = if r#abstract {
|
||||
AccessorPropertyType::TSAbstractAccessorProperty
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ impl<'a> ParserImpl<'a> {
|
|||
let peeked = self.peek_kind();
|
||||
// let = foo, let instanceof x, let + 1
|
||||
if peeked.is_assignment_operator() || peeked.is_binary_operator() {
|
||||
let expr = self.parse_assignment_expression_base()?;
|
||||
let expr = self.parse_assignment_expression_or_higher()?;
|
||||
self.parse_expression_statement(span, expr)
|
||||
// let.a = 1, let()[a] = 1
|
||||
} else if matches!(peeked, Kind::Dot | Kind::LParen) {
|
||||
|
|
@ -118,7 +118,7 @@ impl<'a> ParserImpl<'a> {
|
|||
};
|
||||
|
||||
let init =
|
||||
self.eat(Kind::Eq).then(|| self.parse_assignment_expression_base()).transpose()?;
|
||||
self.eat(Kind::Eq).then(|| self.parse_assignment_expression_or_higher()).transpose()?;
|
||||
|
||||
if init.is_none() && decl_ctx.parent == VariableDeclarationParent::Statement {
|
||||
// LexicalBinding[In, Yield, Await] :
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ use oxc_syntax::{
|
|||
};
|
||||
|
||||
use super::{
|
||||
function::IsParenthesizedArrowFunction,
|
||||
grammar::CoverGrammar,
|
||||
list::{ArrayExpressionList, CallArguments, SequenceExpressionList},
|
||||
operator::{
|
||||
|
|
@ -43,7 +42,7 @@ impl<'a> ParserImpl<'a> {
|
|||
self.ctx = self.ctx.and_decorator(false);
|
||||
}
|
||||
|
||||
let lhs = self.parse_assignment_expression_base()?;
|
||||
let lhs = self.parse_assignment_expression_or_higher()?;
|
||||
if !self.at(Kind::Comma) {
|
||||
return Ok(lhs);
|
||||
}
|
||||
|
|
@ -923,58 +922,24 @@ impl<'a> ParserImpl<'a> {
|
|||
if !self.eat(Kind::Question) {
|
||||
return Ok(lhs);
|
||||
}
|
||||
let consequent =
|
||||
self.context(Context::In, Context::empty(), Self::parse_assignment_expression_base)?;
|
||||
let consequent = self.context(
|
||||
Context::In,
|
||||
Context::empty(),
|
||||
Self::parse_assignment_expression_or_higher,
|
||||
)?;
|
||||
self.expect(Kind::Colon)?;
|
||||
let alternate = self.parse_assignment_expression_base()?;
|
||||
let alternate = self.parse_assignment_expression_or_higher()?;
|
||||
Ok(self.ast.conditional_expression(self.end_span(span), lhs, consequent, alternate))
|
||||
}
|
||||
|
||||
pub(crate) fn parse_assignment_expression_base(&mut self) -> Result<Expression<'a>> {
|
||||
match self.is_parenthesized_arrow_function() {
|
||||
IsParenthesizedArrowFunction::True => {
|
||||
return self.parse_parenthesized_arrow_function();
|
||||
}
|
||||
IsParenthesizedArrowFunction::Maybe => {
|
||||
let pos = self.cur_token().start;
|
||||
if !self.state.not_parenthesized_arrow.contains(&pos) {
|
||||
if let Ok((type_parameters, params, return_type, r#async, span)) =
|
||||
self.try_parse(ParserImpl::parse_parenthesized_arrow_function_head)
|
||||
{
|
||||
return self.parse_arrow_function_body(
|
||||
span,
|
||||
type_parameters,
|
||||
params,
|
||||
return_type,
|
||||
r#async,
|
||||
);
|
||||
}
|
||||
self.state.not_parenthesized_arrow.insert(pos);
|
||||
}
|
||||
}
|
||||
IsParenthesizedArrowFunction::False => {}
|
||||
pub(crate) fn parse_assignment_expression_or_higher(&mut self) -> Result<Expression<'a>> {
|
||||
if let Some(arrow_expr) = self.try_parse_parenthesized_arrow_function_expression()? {
|
||||
return Ok(arrow_expr);
|
||||
}
|
||||
|
||||
let span = self.start_span();
|
||||
if self.cur_kind().is_binding_identifier()
|
||||
&& self.peek_at(Kind::Arrow)
|
||||
&& !self.peek_token().is_on_new_line
|
||||
{
|
||||
self.parse_single_param_function_expression(span, false, false)
|
||||
} else if self.at_async_no_new_line()
|
||||
&& self.peek_kind().is_binding_identifier()
|
||||
&& !self.peek_token().is_on_new_line
|
||||
&& self.nth_at(2, Kind::Arrow)
|
||||
{
|
||||
self.bump_any(); // bump async
|
||||
let arrow_token = self.peek_token();
|
||||
if arrow_token.is_on_new_line {
|
||||
self.error(diagnostics::no_line_break_is_allowed_before_arrow(arrow_token.span()));
|
||||
}
|
||||
self.parse_single_param_function_expression(span, true, false)
|
||||
} else {
|
||||
self.parse_assignment_expression()
|
||||
if let Some(arrow_expr) = self.try_parse_async_simple_arrow_function_expression()? {
|
||||
return Ok(arrow_expr);
|
||||
}
|
||||
self.parse_assignment_expression()
|
||||
}
|
||||
|
||||
/// `AssignmentExpression`[In, Yield, Await] :
|
||||
|
|
@ -1011,7 +976,7 @@ impl<'a> ParserImpl<'a> {
|
|||
|
||||
self.bump_any();
|
||||
|
||||
let right = self.parse_assignment_expression_base()?;
|
||||
let right = self.parse_assignment_expression_or_higher()?;
|
||||
Ok(self.ast.assignment_expression(self.end_span(span), operator, left, right))
|
||||
}
|
||||
|
||||
|
|
@ -1023,7 +988,7 @@ impl<'a> ParserImpl<'a> {
|
|||
) -> Result<Expression<'a>> {
|
||||
let mut expressions = self.ast.new_vec_single(first_expression);
|
||||
while self.eat(Kind::Comma) {
|
||||
let expression = self.parse_assignment_expression_base()?;
|
||||
let expression = self.parse_assignment_expression_or_higher()?;
|
||||
expressions.push(expression);
|
||||
}
|
||||
Ok(self.ast.sequence_expression(self.end_span(span), expressions))
|
||||
|
|
|
|||
|
|
@ -1,28 +1,13 @@
|
|||
use std::cell::Cell;
|
||||
|
||||
use oxc_allocator::Box;
|
||||
use oxc_ast::{ast::*, AstBuilder};
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_diagnostics::Result;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
use oxc_span::Span;
|
||||
|
||||
use super::list::FormalParameterList;
|
||||
use crate::{diagnostics, lexer::Kind, list::SeparatedList, Context, ParserImpl, StatementContext};
|
||||
|
||||
type ArrowFunctionHead<'a> = (
|
||||
Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
|
||||
Box<'a, FormalParameters<'a>>,
|
||||
Option<Box<'a, TSTypeAnnotation<'a>>>,
|
||||
bool,
|
||||
Span,
|
||||
);
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum IsParenthesizedArrowFunction {
|
||||
True,
|
||||
False,
|
||||
Maybe,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum FunctionKind {
|
||||
Declaration { single_statement: bool },
|
||||
|
|
@ -215,61 +200,6 @@ impl<'a> ParserImpl<'a> {
|
|||
Ok(self.ast.function_expression(function))
|
||||
}
|
||||
|
||||
pub(crate) fn parse_single_param_function_expression(
|
||||
&mut self,
|
||||
span: Span,
|
||||
r#async: bool,
|
||||
generator: bool,
|
||||
) -> Result<Expression<'a>> {
|
||||
let has_await = self.ctx.has_await();
|
||||
let has_yield = self.ctx.has_yield();
|
||||
|
||||
self.ctx = self.ctx.union_await_if(r#async).union_yield_if(generator);
|
||||
let params_span = self.start_span();
|
||||
let param = self.parse_binding_identifier()?;
|
||||
let ident = self.ast.binding_pattern_identifier(param);
|
||||
let pattern = self.ast.binding_pattern(ident, None, false);
|
||||
let params_span = self.end_span(params_span);
|
||||
let formal_parameter = self.ast.formal_parameter(
|
||||
params_span,
|
||||
pattern,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
AstBuilder::new_vec(&self.ast),
|
||||
);
|
||||
let params = self.ast.formal_parameters(
|
||||
params_span,
|
||||
FormalParameterKind::ArrowFormalParameters,
|
||||
self.ast.new_vec_single(formal_parameter),
|
||||
None,
|
||||
);
|
||||
|
||||
self.expect(Kind::Arrow)?;
|
||||
|
||||
self.ctx = self.ctx.and_await(r#async).and_yield(generator);
|
||||
let expression = !self.at(Kind::LCurly);
|
||||
let body = if expression {
|
||||
let expr = self.parse_assignment_expression_base()?;
|
||||
let span = expr.span();
|
||||
let expr_stmt = self.ast.expression_statement(span, expr);
|
||||
self.ast.function_body(span, self.ast.new_vec(), self.ast.new_vec_single(expr_stmt))
|
||||
} else {
|
||||
self.parse_function_body()?
|
||||
};
|
||||
self.ctx = self.ctx.and_await(has_await).and_yield(has_yield);
|
||||
|
||||
Ok(self.ast.arrow_function_expression(
|
||||
self.end_span(span),
|
||||
expression,
|
||||
r#async,
|
||||
params,
|
||||
body,
|
||||
None,
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
/// Section 15.4 Method Definitions
|
||||
/// `ClassElementName` ( `UniqueFormalParameters` ) { `FunctionBody` }
|
||||
/// `GeneratorMethod`
|
||||
|
|
@ -324,7 +254,7 @@ impl<'a> ParserImpl<'a> {
|
|||
);
|
||||
if !not_assignment_expr || delegate {
|
||||
self.ctx = self.ctx.union_yield_if(true);
|
||||
argument = Some(self.parse_assignment_expression_base()?);
|
||||
argument = Some(self.parse_assignment_expression_or_higher()?);
|
||||
self.ctx = self.ctx.and_yield(has_yield);
|
||||
}
|
||||
}
|
||||
|
|
@ -356,191 +286,4 @@ impl<'a> ParserImpl<'a> {
|
|||
|
||||
id
|
||||
}
|
||||
|
||||
pub(crate) fn is_parenthesized_arrow_function_expression(
|
||||
&mut self,
|
||||
r#async: bool,
|
||||
) -> IsParenthesizedArrowFunction {
|
||||
let offset = u8::from(r#async);
|
||||
|
||||
match self.nth_kind(offset) {
|
||||
Kind::LParen => match self.nth_kind(offset + 1) {
|
||||
// '()' is an arrow expression if followed by an '=>', a type annotation or body.
|
||||
// Otherwise, a parenthesized expression with a missing inner expression
|
||||
Kind::RParen => {
|
||||
let kind = self.nth_kind(offset + 2);
|
||||
if self.ts_enabled() && kind == Kind::Colon {
|
||||
IsParenthesizedArrowFunction::Maybe
|
||||
} else if matches!(kind, Kind::Arrow | Kind::LCurly) {
|
||||
IsParenthesizedArrowFunction::True
|
||||
} else {
|
||||
IsParenthesizedArrowFunction::False
|
||||
}
|
||||
}
|
||||
// Rest parameter
|
||||
// '(...ident' is not a parenthesized expression
|
||||
// '(...null' is a parenthesized expression
|
||||
Kind::Dot3 => match self.nth_kind(offset + 1) {
|
||||
Kind::Ident => IsParenthesizedArrowFunction::True,
|
||||
kind if kind.is_literal() => IsParenthesizedArrowFunction::False,
|
||||
_ => IsParenthesizedArrowFunction::Maybe,
|
||||
},
|
||||
// '([ ...', '({ ... } can either be a parenthesized object or array expression or a destructing parameter
|
||||
Kind::LBrack | Kind::LCurly => IsParenthesizedArrowFunction::Maybe,
|
||||
_ if self.nth_kind(offset + 1).is_binding_identifier()
|
||||
|| self.nth_at(offset + 1, Kind::This) =>
|
||||
{
|
||||
match self.nth_kind(offset + 2) {
|
||||
// '(a: ' must be a type annotation
|
||||
Kind::Colon => IsParenthesizedArrowFunction::True,
|
||||
// * '(a = ': an initializer or a parenthesized assignment expression
|
||||
// * '(a, ': separator to next parameter or a parenthesized sequence expression
|
||||
// * '(a)': a single parameter OR a parenthesized expression
|
||||
Kind::Eq | Kind::Comma | Kind::RParen => {
|
||||
IsParenthesizedArrowFunction::Maybe
|
||||
}
|
||||
// '(a?:' | '(a?,' | '(a?=' | '(a?)'
|
||||
Kind::Question
|
||||
if matches!(
|
||||
self.nth_kind(offset + 3),
|
||||
Kind::Colon | Kind::Comma | Kind::Eq | Kind::RParen
|
||||
) =>
|
||||
{
|
||||
IsParenthesizedArrowFunction::True
|
||||
}
|
||||
_ => IsParenthesizedArrowFunction::False,
|
||||
}
|
||||
}
|
||||
_ => IsParenthesizedArrowFunction::False,
|
||||
},
|
||||
Kind::LAngle => {
|
||||
let kind = self.nth_kind(offset + 1);
|
||||
|
||||
// `<const` for const type parameter from TypeScript 5.0
|
||||
if kind == Kind::Const {
|
||||
return IsParenthesizedArrowFunction::Maybe;
|
||||
}
|
||||
|
||||
if !kind.is_identifier() {
|
||||
return IsParenthesizedArrowFunction::False;
|
||||
}
|
||||
|
||||
if self.source_type.is_jsx() {
|
||||
return match self.nth_kind(offset + 2) {
|
||||
Kind::Extends => {
|
||||
let third_kind = self.nth_kind(offset + 3);
|
||||
if matches!(third_kind, Kind::Eq | Kind::RAngle) {
|
||||
IsParenthesizedArrowFunction::False
|
||||
} else if third_kind.is_identifier() {
|
||||
IsParenthesizedArrowFunction::Maybe
|
||||
} else {
|
||||
IsParenthesizedArrowFunction::True
|
||||
}
|
||||
}
|
||||
Kind::Eq | Kind::Comma => IsParenthesizedArrowFunction::True,
|
||||
_ => IsParenthesizedArrowFunction::False,
|
||||
};
|
||||
}
|
||||
|
||||
IsParenthesizedArrowFunction::Maybe
|
||||
}
|
||||
_ => IsParenthesizedArrowFunction::False,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_parenthesized_arrow_function(&mut self) -> IsParenthesizedArrowFunction {
|
||||
match self.cur_kind() {
|
||||
Kind::LAngle | Kind::LParen => self.is_parenthesized_arrow_function_expression(false),
|
||||
Kind::Async => {
|
||||
let peeked = self.peek_token();
|
||||
if !peeked.is_on_new_line && matches!(peeked.kind, Kind::LAngle | Kind::LParen) {
|
||||
self.is_parenthesized_arrow_function_expression(true)
|
||||
} else {
|
||||
IsParenthesizedArrowFunction::False
|
||||
}
|
||||
}
|
||||
_ => IsParenthesizedArrowFunction::False,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_parenthesized_arrow_function_head(
|
||||
&mut self,
|
||||
) -> Result<ArrowFunctionHead<'a>> {
|
||||
let span = self.start_span();
|
||||
let r#async = self.eat(Kind::Async);
|
||||
|
||||
let has_await = self.ctx.has_await();
|
||||
self.ctx = self.ctx.union_await_if(r#async);
|
||||
|
||||
let type_parameters = self.parse_ts_type_parameters()?;
|
||||
|
||||
let (this_param, params) =
|
||||
self.parse_formal_parameters(FormalParameterKind::ArrowFormalParameters)?;
|
||||
|
||||
if let Some(this_param) = this_param {
|
||||
// const x = (this: number) => {};
|
||||
self.error(diagnostics::ts_arrow_function_this_parameter(this_param.span));
|
||||
}
|
||||
|
||||
let return_type = self.parse_ts_return_type_annotation()?;
|
||||
|
||||
self.ctx = self.ctx.and_await(has_await);
|
||||
|
||||
if self.cur_token().is_on_new_line {
|
||||
self.error(diagnostics::lineterminator_before_arrow(self.cur_token().span()));
|
||||
}
|
||||
|
||||
self.expect(Kind::Arrow)?;
|
||||
|
||||
Ok((type_parameters, params, return_type, r#async, span))
|
||||
}
|
||||
|
||||
/// [ConciseBody](https://tc39.es/ecma262/#prod-ConciseBody)
|
||||
/// [lookahead ≠ {] `ExpressionBody`[?In, ~Await]
|
||||
/// { `FunctionBody`[~Yield, ~Await] }
|
||||
/// `ExpressionBody`[In, Await] :
|
||||
/// `AssignmentExpression`[?In, ~Yield, ?Await]
|
||||
pub(crate) fn parse_arrow_function_body(
|
||||
&mut self,
|
||||
span: Span,
|
||||
type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
|
||||
params: Box<'a, FormalParameters<'a>>,
|
||||
return_type: Option<Box<'a, TSTypeAnnotation<'a>>>,
|
||||
r#async: bool,
|
||||
) -> Result<Expression<'a>> {
|
||||
let has_await = self.ctx.has_await();
|
||||
let has_yield = self.ctx.has_yield();
|
||||
self.ctx = self.ctx.and_await(r#async).and_yield(false);
|
||||
|
||||
let expression = !self.at(Kind::LCurly);
|
||||
let body = if expression {
|
||||
let expr = self.parse_assignment_expression_base()?;
|
||||
let span = expr.span();
|
||||
let expr_stmt = self.ast.expression_statement(span, expr);
|
||||
self.ast.function_body(span, self.ast.new_vec(), self.ast.new_vec_single(expr_stmt))
|
||||
} else {
|
||||
self.parse_function_body()?
|
||||
};
|
||||
|
||||
self.ctx = self.ctx.and_await(has_await).and_yield(has_yield);
|
||||
|
||||
Ok(self.ast.arrow_function_expression(
|
||||
self.end_span(span),
|
||||
expression,
|
||||
r#async,
|
||||
params,
|
||||
body,
|
||||
type_parameters,
|
||||
return_type,
|
||||
))
|
||||
}
|
||||
|
||||
/// Section [Arrow Function](https://tc39.es/ecma262/#sec-arrow-function-definitions)
|
||||
/// `ArrowFunction`[In, Yield, Await] :
|
||||
/// `ArrowParameters`[?Yield, ?Await] [no `LineTerminator` here] => `ConciseBody`[?In]
|
||||
pub(crate) fn parse_parenthesized_arrow_function(&mut self) -> Result<Expression<'a>> {
|
||||
let (type_parameters, params, return_type, r#async, span) =
|
||||
self.parse_parenthesized_arrow_function_head()?;
|
||||
self.parse_arrow_function_body(span, type_parameters, params, return_type, r#async)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ impl<'a> SeparatedList<'a> for ArrayExpressionList<'a> {
|
|||
let element = match p.cur_kind() {
|
||||
Kind::Comma => Ok(p.parse_elision()),
|
||||
Kind::Dot3 => p.parse_spread_element().map(ArrayExpressionElement::SpreadElement),
|
||||
_ => p.parse_assignment_expression_base().map(ArrayExpressionElement::from),
|
||||
_ => p.parse_assignment_expression_or_higher().map(ArrayExpressionElement::from),
|
||||
};
|
||||
|
||||
if p.at(Kind::Comma) && p.peek_at(self.close()) {
|
||||
|
|
@ -188,7 +188,7 @@ impl<'a> SeparatedList<'a> for CallArguments<'a> {
|
|||
}
|
||||
result
|
||||
} else {
|
||||
p.parse_assignment_expression_base().map(Argument::from)
|
||||
p.parse_assignment_expression_or_higher().map(Argument::from)
|
||||
};
|
||||
self.elements.push(element?);
|
||||
Ok(())
|
||||
|
|
@ -215,7 +215,7 @@ impl<'a> SeparatedList<'a> for SequenceExpressionList<'a> {
|
|||
// read everything as expression and map to it to either
|
||||
// ParenthesizedExpression or ArrowFormalParameters later
|
||||
fn parse_element(&mut self, p: &mut ParserImpl<'a>) -> Result<()> {
|
||||
let element = p.parse_assignment_expression_base()?;
|
||||
let element = p.parse_assignment_expression_or_higher()?;
|
||||
self.elements.push(element);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
mod grammar;
|
||||
pub mod list;
|
||||
|
||||
mod arrow;
|
||||
mod binding;
|
||||
mod class;
|
||||
pub mod declaration;
|
||||
|
|
@ -14,3 +15,10 @@ mod module;
|
|||
mod object;
|
||||
mod operator;
|
||||
mod statement;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Tristate {
|
||||
True,
|
||||
False,
|
||||
Maybe,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,10 @@ impl<'a> ParserImpl<'a> {
|
|||
let has_in = self.ctx.has_in();
|
||||
self.ctx = self.ctx.and_in(true);
|
||||
|
||||
let expression = self.parse_assignment_expression_base()?;
|
||||
let expression = self.parse_assignment_expression_or_higher()?;
|
||||
let mut arguments = self.ast.new_vec();
|
||||
if self.eat(Kind::Comma) && !self.at(Kind::RParen) {
|
||||
arguments.push(self.parse_assignment_expression_base()?);
|
||||
arguments.push(self.parse_assignment_expression_or_higher()?);
|
||||
}
|
||||
|
||||
self.ctx = self.ctx.and_in(has_in);
|
||||
|
|
@ -149,7 +149,7 @@ impl<'a> ParserImpl<'a> {
|
|||
) -> Result<Box<'a, TSExportAssignment<'a>>> {
|
||||
self.expect(Kind::Eq)?;
|
||||
|
||||
let expression = self.parse_assignment_expression_base()?;
|
||||
let expression = self.parse_assignment_expression_or_higher()?;
|
||||
self.asi()?;
|
||||
|
||||
Ok(self.ast.alloc(TSExportAssignment { span: self.end_span(start_span), expression }))
|
||||
|
|
@ -337,7 +337,7 @@ impl<'a> ParserImpl<'a> {
|
|||
.map(ExportDefaultDeclarationKind::FunctionDeclaration)?,
|
||||
_ => {
|
||||
let decl = self
|
||||
.parse_assignment_expression_base()
|
||||
.parse_assignment_expression_or_higher()
|
||||
.map(ExportDefaultDeclarationKind::from)?;
|
||||
self.asi()?;
|
||||
decl
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ impl<'a> ParserImpl<'a> {
|
|||
pub(crate) fn parse_spread_element(&mut self) -> Result<Box<'a, SpreadElement<'a>>> {
|
||||
let span = self.start_span();
|
||||
self.bump_any(); // advance `...`
|
||||
let argument = self.parse_assignment_expression_base()?;
|
||||
let argument = self.parse_assignment_expression_or_higher()?;
|
||||
Ok(self.ast.spread_element(self.end_span(span), argument))
|
||||
}
|
||||
|
||||
|
|
@ -102,7 +102,7 @@ impl<'a> ParserImpl<'a> {
|
|||
let value = Expression::Identifier(self.ast.alloc(identifier.clone()));
|
||||
// CoverInitializedName ({ foo = bar })
|
||||
let init = if self.eat(Kind::Eq) {
|
||||
let right = self.parse_assignment_expression_base()?;
|
||||
let right = self.parse_assignment_expression_or_higher()?;
|
||||
let left = AssignmentTarget::AssignmentTargetIdentifier(self.ast.alloc(identifier));
|
||||
Some(self.ast.assignment_expression(
|
||||
self.end_span(span),
|
||||
|
|
@ -134,7 +134,7 @@ impl<'a> ParserImpl<'a> {
|
|||
computed: bool,
|
||||
) -> Result<Box<'a, ObjectProperty<'a>>> {
|
||||
self.bump_any(); // bump `:`
|
||||
let value = self.parse_assignment_expression_base()?;
|
||||
let value = self.parse_assignment_expression_or_higher()?;
|
||||
Ok(self.ast.object_property(
|
||||
self.end_span(span),
|
||||
PropertyKind::Init,
|
||||
|
|
@ -172,8 +172,11 @@ impl<'a> ParserImpl<'a> {
|
|||
pub(crate) fn parse_computed_property_name(&mut self) -> Result<Expression<'a>> {
|
||||
self.bump_any(); // advance `[`
|
||||
|
||||
let expression =
|
||||
self.context(Context::In, Context::empty(), Self::parse_assignment_expression_base)?;
|
||||
let expression = self.context(
|
||||
Context::In,
|
||||
Context::empty(),
|
||||
Self::parse_assignment_expression_or_higher,
|
||||
)?;
|
||||
|
||||
self.expect(Kind::RBrack)?;
|
||||
Ok(expression)
|
||||
|
|
|
|||
|
|
@ -389,7 +389,7 @@ impl<'a> ParserImpl<'a> {
|
|||
let right = if is_for_in {
|
||||
self.parse_expression()
|
||||
} else {
|
||||
self.parse_assignment_expression_base()
|
||||
self.parse_assignment_expression_or_higher()
|
||||
}?;
|
||||
self.expect(Kind::RParen)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -42,8 +42,11 @@ impl<'a> ParserImpl<'a> {
|
|||
let span = self.start_span();
|
||||
let id = self.parse_ts_enum_member_name()?;
|
||||
|
||||
let initializer =
|
||||
if self.eat(Kind::Eq) { Some(self.parse_assignment_expression_base()?) } else { None };
|
||||
let initializer = if self.eat(Kind::Eq) {
|
||||
Some(self.parse_assignment_expression_or_higher()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(TSEnumMember { span: self.end_span(span), id, initializer })
|
||||
}
|
||||
|
|
|
|||
1
tasks/coverage/misc/fail/oxc-3320.tsx
Normal file
1
tasks/coverage/misc/fail/oxc-3320.tsx
Normal file
|
|
@ -0,0 +1 @@
|
|||
m< $<{3[ $<{3[ $<{3[ m< m$<{3[ m< mm< $<{3[ $<{3[ $<{3[ m< m$<{3[ m< m$<{3[ $<{3[ m< m$<{3[
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
parser_misc Summary:
|
||||
AST Parsed : 16/16 (100.00%)
|
||||
Positive Passed: 16/16 (100.00%)
|
||||
Negative Passed: 8/8 (100.00%)
|
||||
Negative Passed: 9/9 (100.00%)
|
||||
|
||||
× Unexpected token
|
||||
╭─[fail/oxc-169.js:2:1]
|
||||
|
|
@ -99,6 +99,12 @@ Negative Passed: 8/8 (100.00%)
|
|||
21 │ & import("pkg", {"resolution-mode": "import"}).ImportInterface;
|
||||
╰────
|
||||
|
||||
× Unexpected token
|
||||
╭─[fail/oxc-3320.tsx:1:8]
|
||||
1 │ m< $<{3[ $<{3[ $<{3[ m< m$<{3[ m< mm< $<{3[ $<{3[ $<{3[ m< m$<{3[ m< m$<{3[ $<{3[ m< m$<{3[
|
||||
· ─
|
||||
╰────
|
||||
|
||||
× The keyword 'let' is reserved
|
||||
╭─[fail/oxc.js:1:1]
|
||||
1 │ let.a = 1;
|
||||
|
|
|
|||
Loading…
Reference in a new issue