diff --git a/crates/oxc_parser/src/js/arrow.rs b/crates/oxc_parser/src/js/arrow.rs index a5ad80e7b0128..b49cf2b160e8a 100644 --- a/crates/oxc_parser/src/js/arrow.rs +++ b/crates/oxc_parser/src/js/arrow.rs @@ -192,20 +192,19 @@ impl<'a> ParserImpl<'a> { } fn is_un_parenthesized_async_arrow_function_worker(&mut self) -> bool { - let checkpoint = self.checkpoint(); - self.bump(Kind::Async); - // If the "async" is followed by "=>" token then it is not a beginning of an async arrow-function - // but instead a simple arrow-function which will be parsed inside "parseAssignmentExpressionOrHigher" - if !self.cur_token().is_on_new_line() && self.cur_kind().is_binding_identifier() { - // Arrow before newline is checked in `parse_simple_arrow_function_expression` - self.bump_any(); - if self.at(Kind::Arrow) { - self.rewind(checkpoint); - return true; + // Use lookahead to avoid checkpoint/rewind + self.lookahead(|parser| { + parser.bump(Kind::Async); + // If the "async" is followed by "=>" token then it is not a beginning of an async arrow-function + // but instead a simple arrow-function which will be parsed inside "parseAssignmentExpressionOrHigher" + if !parser.cur_token().is_on_new_line() && parser.cur_kind().is_binding_identifier() { + // Arrow before newline is checked in `parse_simple_arrow_function_expression` + parser.bump_any(); + parser.at(Kind::Arrow) + } else { + false } - } - self.rewind(checkpoint); - false + }) } pub(crate) fn parse_simple_arrow_function_expression( diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs index 3b5676caca256..99adcc547904f 100644 --- a/crates/oxc_parser/src/js/expression.rs +++ b/crates/oxc_parser/src/js/expression.rs @@ -731,14 +731,16 @@ impl<'a> ParserImpl<'a> { let mut question_dot = false; let is_property_access = if allow_optional_chain && self.at(Kind::QuestionDot) { // ?. - let checkpoint = self.checkpoint(); - self.bump_any(); - let kind = self.cur_kind(); - let is_identifier_or_keyword = kind.is_identifier_or_keyword(); - if kind == Kind::LBrack - || is_identifier_or_keyword - || kind.is_template_start_of_tagged_template() + // Fast check to avoid checkpoint/rewind in common cases + let next_kind = self.lexer.peek_token().kind(); + if next_kind == Kind::LBrack + || next_kind.is_identifier_or_keyword() + || next_kind.is_template_start_of_tagged_template() { + // This is likely a valid optional chain, proceed with normal parsing + self.bump_any(); // consume ?. + let kind = self.cur_kind(); + let is_identifier_or_keyword = kind.is_identifier_or_keyword(); // ?.[ // ?.something // ?.template`...` @@ -746,9 +748,9 @@ impl<'a> ParserImpl<'a> { question_dot = true; is_identifier_or_keyword } else { + // This is not a valid optional chain pattern, don't consume ?. // Should be a cold branch here, as most real-world optional chaining will look like // `?.something` or `?.[expr]` - self.rewind(checkpoint); false } } else { diff --git a/crates/oxc_parser/src/ts/types.rs b/crates/oxc_parser/src/ts/types.rs index 31885ee201858..97f76e3cc08b6 100644 --- a/crates/oxc_parser/src/ts/types.rs +++ b/crates/oxc_parser/src/ts/types.rs @@ -430,14 +430,15 @@ impl<'a> ParserImpl<'a> { Kind::LParen => self.parse_parenthesized_type(), Kind::Import => TSType::TSImportType(self.parse_ts_import_type()), Kind::Asserts => { - let checkpoint = self.checkpoint(); - let asserts_start_span = self.start_span(); - self.bump_any(); // bump `asserts` - - if self.is_token_identifier_or_keyword_on_same_line() { + // Use lookahead to check if this is an asserts type predicate + if self.lookahead(|parser| { + parser.bump(Kind::Asserts); + parser.is_token_identifier_or_keyword_on_same_line() + }) { + let asserts_start_span = self.start_span(); + self.bump_any(); // bump `asserts` self.parse_asserts_type_predicate(asserts_start_span) } else { - self.rewind(checkpoint); self.parse_type_reference() } }