diff --git a/crates/oxc_codegen/examples/codegen.rs b/crates/oxc_codegen/examples/codegen.rs index ed17f7e5d..d527f3fd2 100644 --- a/crates/oxc_codegen/examples/codegen.rs +++ b/crates/oxc_codegen/examples/codegen.rs @@ -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::::new("", &source_text, options.clone()).build(&ret.program).source_text; println!("Printed:"); diff --git a/crates/oxc_parser/examples/parser.rs b/crates/oxc_parser/examples/parser.rs index dd74a2e93..c789513ca 100644 --- a/crates/oxc_parser/examples/parser.rs +++ b/crates/oxc_parser/examples/parser.rs @@ -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()); diff --git a/crates/oxc_parser/src/js/arrow.rs b/crates/oxc_parser/src/js/arrow.rs new file mode 100644 index 000000000..e7313c99f --- /dev/null +++ b/crates/oxc_parser/src/js/arrow.rs @@ -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, FormalParameters<'a>>, + Option>>, + 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); + + // ` { + 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> { + 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>>, + params: Box<'a, FormalParameters<'a>>, + return_type: Option>>, + r#async: bool, + ) -> Result> { + 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>> { + 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> { + 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>> { + 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>> { + 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>> { + 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) + } + } +} diff --git a/crates/oxc_parser/src/js/binding.rs b/crates/oxc_parser/src/js/binding.rs index c7f886fa0..c973fac2f 100644 --- a/crates/oxc_parser/src/js/binding.rs +++ b/crates/oxc_parser/src/js/binding.rs @@ -132,7 +132,7 @@ impl<'a> ParserImpl<'a> { left: BindingPattern<'a>, ) -> Result> { 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) diff --git a/crates/oxc_parser/src/js/class.rs b/crates/oxc_parser/src/js/class.rs index 80ea93016..6db0acc99 100644 --- a/crates/oxc_parser/src/js/class.rs +++ b/crates/oxc_parser/src/js/class.rs @@ -467,7 +467,7 @@ impl<'a> ParserImpl<'a> { r#abstract: bool, ) -> Result> { 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 { diff --git a/crates/oxc_parser/src/js/declaration.rs b/crates/oxc_parser/src/js/declaration.rs index 2fb984d4f..ce764d645 100644 --- a/crates/oxc_parser/src/js/declaration.rs +++ b/crates/oxc_parser/src/js/declaration.rs @@ -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] : diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs index 6984722ea..5e042c40a 100644 --- a/crates/oxc_parser/src/js/expression.rs +++ b/crates/oxc_parser/src/js/expression.rs @@ -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> { - 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> { + 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> { 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)) diff --git a/crates/oxc_parser/src/js/function.rs b/crates/oxc_parser/src/js/function.rs index d947a2a94..8295ace6a 100644 --- a/crates/oxc_parser/src/js/function.rs +++ b/crates/oxc_parser/src/js/function.rs @@ -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, FormalParameters<'a>>, - Option>>, - 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> { - 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); - - // ` { - 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> { - 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>>, - params: Box<'a, FormalParameters<'a>>, - return_type: Option>>, - r#async: bool, - ) -> Result> { - 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> { - 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) - } } diff --git a/crates/oxc_parser/src/js/list.rs b/crates/oxc_parser/src/js/list.rs index 6a09bc9f7..520196ee1 100644 --- a/crates/oxc_parser/src/js/list.rs +++ b/crates/oxc_parser/src/js/list.rs @@ -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(()) } diff --git a/crates/oxc_parser/src/js/mod.rs b/crates/oxc_parser/src/js/mod.rs index dfe67a0ec..dd2dffefd 100644 --- a/crates/oxc_parser/src/js/mod.rs +++ b/crates/oxc_parser/src/js/mod.rs @@ -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, +} diff --git a/crates/oxc_parser/src/js/module.rs b/crates/oxc_parser/src/js/module.rs index eec6ee8d6..d8f34d743 100644 --- a/crates/oxc_parser/src/js/module.rs +++ b/crates/oxc_parser/src/js/module.rs @@ -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>> { 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 diff --git a/crates/oxc_parser/src/js/object.rs b/crates/oxc_parser/src/js/object.rs index 8a74725cf..dcc4a0f0b 100644 --- a/crates/oxc_parser/src/js/object.rs +++ b/crates/oxc_parser/src/js/object.rs @@ -86,7 +86,7 @@ impl<'a> ParserImpl<'a> { pub(crate) fn parse_spread_element(&mut self) -> Result>> { 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>> { 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> { 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) diff --git a/crates/oxc_parser/src/js/statement.rs b/crates/oxc_parser/src/js/statement.rs index f74a153fa..9efa1d34f 100644 --- a/crates/oxc_parser/src/js/statement.rs +++ b/crates/oxc_parser/src/js/statement.rs @@ -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)?; diff --git a/crates/oxc_parser/src/ts/statement.rs b/crates/oxc_parser/src/ts/statement.rs index a4c732118..ecd14c453 100644 --- a/crates/oxc_parser/src/ts/statement.rs +++ b/crates/oxc_parser/src/ts/statement.rs @@ -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 }) } diff --git a/tasks/coverage/misc/fail/oxc-3320.tsx b/tasks/coverage/misc/fail/oxc-3320.tsx new file mode 100644 index 000000000..731e1288a --- /dev/null +++ b/tasks/coverage/misc/fail/oxc-3320.tsx @@ -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[ diff --git a/tasks/coverage/parser_misc.snap b/tasks/coverage/parser_misc.snap index b0d7f5379..47db22b1f 100644 --- a/tasks/coverage/parser_misc.snap +++ b/tasks/coverage/parser_misc.snap @@ -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;