diff --git a/Cargo.lock b/Cargo.lock index 6a0d177f46ba..23cce2ff68cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5133,7 +5133,6 @@ dependencies = [ "bitflags 2.6.0", "codspeed-criterion-compat", "either", - "new_debug_unreachable", "num-bigint", "phf", "rustc-hash 2.1.1", diff --git a/crates/swc/tests/tsc-references/parserArrowFunctionExpression11.1.normal.js b/crates/swc/tests/tsc-references/parserArrowFunctionExpression11.1.normal.js index 05a8fe9c5dfd..1a8c41d135d6 100644 --- a/crates/swc/tests/tsc-references/parserArrowFunctionExpression11.1.normal.js +++ b/crates/swc/tests/tsc-references/parserArrowFunctionExpression11.1.normal.js @@ -3,12 +3,12 @@ //! x Expected ':', got '' //! ,-[1:1] //! 1 | a ? b ? c : (d) : e => f // Legal JS -//! : ^ +//! : ^^ //! 2 | //! `---- //// [fileTs.ts] //! x Expected ':', got '' //! ,---- //! 1 | a ? b ? c : (d) : e => f -//! : ^ +//! : ^^ //! `---- diff --git a/crates/swc/tests/tsc-references/parserArrowFunctionExpression11.2.minified.js b/crates/swc/tests/tsc-references/parserArrowFunctionExpression11.2.minified.js index 05a8fe9c5dfd..1a8c41d135d6 100644 --- a/crates/swc/tests/tsc-references/parserArrowFunctionExpression11.2.minified.js +++ b/crates/swc/tests/tsc-references/parserArrowFunctionExpression11.2.minified.js @@ -3,12 +3,12 @@ //! x Expected ':', got '' //! ,-[1:1] //! 1 | a ? b ? c : (d) : e => f // Legal JS -//! : ^ +//! : ^^ //! 2 | //! `---- //// [fileTs.ts] //! x Expected ':', got '' //! ,---- //! 1 | a ? b ? c : (d) : e => f -//! : ^ +//! : ^^ //! `---- diff --git a/crates/swc_common/src/syntax_pos.rs b/crates/swc_common/src/syntax_pos.rs index 91cb7b6fa77e..3c1bbe5997e2 100644 --- a/crates/swc_common/src/syntax_pos.rs +++ b/crates/swc_common/src/syntax_pos.rs @@ -407,6 +407,7 @@ impl Span { } #[inline] + #[track_caller] pub fn new_with_checked(lo: BytePos, hi: BytePos) -> Self { debug_assert!(lo <= hi, "lo: {lo:#?}, hi: {hi:#?}"); Span { lo, hi } diff --git a/crates/swc_ecma_lexer/Cargo.toml b/crates/swc_ecma_lexer/Cargo.toml index 7ea3dc1fbd6b..091273908680 100644 --- a/crates/swc_ecma_lexer/Cargo.toml +++ b/crates/swc_ecma_lexer/Cargo.toml @@ -25,18 +25,17 @@ typescript = [] verify = ["swc_ecma_visit"] [dependencies] -arrayvec = { workspace = true } -bitflags = { workspace = true } -either = { workspace = true } -new_debug_unreachable = { workspace = true } -num-bigint = { workspace = true } -phf = { workspace = true, features = ["macros"] } -rustc-hash = { workspace = true } -seq-macro = { workspace = true } -serde = { workspace = true, features = ["derive"] } -smallvec = { workspace = true } -smartstring = { workspace = true } -tracing = { workspace = true } +arrayvec = { workspace = true } +bitflags = { workspace = true } +either = { workspace = true } +num-bigint = { workspace = true } +phf = { workspace = true, features = ["macros"] } +rustc-hash = { workspace = true } +seq-macro = { workspace = true } +serde = { workspace = true, features = ["derive"] } +smallvec = { workspace = true } +smartstring = { workspace = true } +tracing = { workspace = true } swc_atoms = { version = "7.0.0", path = "../swc_atoms" } swc_common = { version = "14.0.1", path = "../swc_common" } diff --git a/crates/swc_ecma_lexer/src/common/lexer/mod.rs b/crates/swc_ecma_lexer/src/common/lexer/mod.rs index 6765d5eccfff..06338a8e61b2 100644 --- a/crates/swc_ecma_lexer/src/common/lexer/mod.rs +++ b/crates/swc_ecma_lexer/src/common/lexer/mod.rs @@ -1730,7 +1730,7 @@ pub trait Lexer<'a, TokenAndSpan>: Tokens + Sized { } /// `#` - fn read_token_number_sign(&mut self) -> LexResult> { + fn read_token_number_sign(&mut self) -> LexResult { debug_assert!(self.cur().is_some_and(|c| c == '#')); self.bump(); // '#' @@ -1741,7 +1741,7 @@ pub trait Lexer<'a, TokenAndSpan>: Tokens + Sized { !self.input().is_at_start() || self.cur() != Some('!'), "#! should have already been handled by read_shebang()" ); - Ok(Some(Self::Token::HASH)) + Ok(Self::Token::HASH) } /// Read a token given `.`. @@ -1936,14 +1936,14 @@ pub trait Lexer<'a, TokenAndSpan>: Tokens + Sized { } #[inline(never)] - fn read_slash(&mut self) -> LexResult> { + fn read_slash(&mut self) -> LexResult { debug_assert_eq!(self.cur(), Some('/')); self.bump(); // '/' - Ok(Some(if self.eat(b'=') { + Ok(if self.eat(b'=') { Self::Token::DIV_EQ } else { Self::Token::DIV - })) + }) } /// This can be used if there's no keyword starting with the first @@ -2084,7 +2084,7 @@ pub trait Lexer<'a, TokenAndSpan>: Tokens + Sized { fn read_keyword_with( &mut self, convert: &dyn Fn(&str) -> Option, - ) -> LexResult> { + ) -> LexResult { debug_assert!(self.cur().is_some()); let start = self.cur_pos(); @@ -2100,11 +2100,11 @@ pub trait Lexer<'a, TokenAndSpan>: Tokens + Sized { SyntaxError::EscapeInReservedWord { word: Atom::new(s) }, ) } else { - Ok(Some(word)) + Ok(word) } } else { let atom = self.atom(s); - Ok(Some(Self::Token::unknown_ident(atom, self))) + Ok(Self::Token::unknown_ident(atom, self)) } } diff --git a/crates/swc_ecma_lexer/src/common/lexer/token.rs b/crates/swc_ecma_lexer/src/common/lexer/token.rs index 083f4cd7f858..35def3fef5f3 100644 --- a/crates/swc_ecma_lexer/src/common/lexer/token.rs +++ b/crates/swc_ecma_lexer/src/common/lexer/token.rs @@ -149,6 +149,7 @@ pub trait TokenFactory<'a, TokenAndSpan, I: Tokens>: Sized + Parti const CASE: Self; const DEFAULT: Self; const DEBUGGER: Self; + const EOF: Self; fn jsx_name(name: &'a str, lexer: &mut Self::Lexer) -> Self; fn is_jsx_name(&self) -> bool; @@ -156,6 +157,7 @@ pub trait TokenFactory<'a, TokenAndSpan, I: Tokens>: Sized + Parti fn str(value: Atom, raw: Atom, lexer: &mut Self::Lexer) -> Self; fn is_str(&self) -> bool; + fn is_str_raw_content(&self, content: &str, buffer: &Self::Buffer) -> bool; fn take_str(self, buffer: &mut Self::Buffer) -> (Atom, Atom); fn template(cooked: LexResult, raw: Atom, lexer: &mut Self::Lexer) -> Self; @@ -586,6 +588,10 @@ pub trait TokenFactory<'a, TokenAndSpan, I: Tokens>: Sized + Parti fn is_exp(&self) -> bool { Self::EXP.eq(self) } + #[inline(always)] + fn is_eof(&self) -> bool { + Self::EOF.eq(self) + } fn is_no_substitution_template_literal(&self) -> bool; fn is_template_head(&self) -> bool; } diff --git a/crates/swc_ecma_lexer/src/common/parser/buffer.rs b/crates/swc_ecma_lexer/src/common/parser/buffer.rs index b5af0968d0f1..b8a0fc763990 100644 --- a/crates/swc_ecma_lexer/src/common/parser/buffer.rs +++ b/crates/swc_ecma_lexer/src/common/parser/buffer.rs @@ -1,10 +1,13 @@ -use debug_unreachable::debug_unreachable; +use swc_atoms::Atom; use swc_common::{BytePos, Span}; use swc_ecma_ast::EsVersion; use super::token_and_span::TokenAndSpan as TokenAndSpanTrait; use crate::common::{ - context::Context, input::Tokens, lexer::token::TokenFactory, syntax::SyntaxFlags, + context::Context, + input::Tokens, + lexer::{token::TokenFactory, LexResult}, + syntax::SyntaxFlags, }; pub trait NextTokenAndSpan { @@ -30,9 +33,9 @@ pub trait Buffer<'a> { fn set_next(&mut self, token: Option); fn next_mut(&mut self) -> &mut Option; - fn cur(&mut self) -> Option<&Self::Token>; - fn get_cur(&self) -> Option<&Self::TokenAndSpan>; - fn get_cur_mut(&mut self) -> &mut Option; + fn cur(&self) -> &Self::Token; + fn get_cur(&self) -> &Self::TokenAndSpan; + fn get_cur_mut(&mut self) -> &mut Self::TokenAndSpan; fn prev_span(&self) -> Span; fn set_prev_span(&mut self, span: Span); @@ -43,47 +46,34 @@ pub trait Buffer<'a> { fn store(&mut self, token: Self::Token) { debug_assert!(self.next().is_none()); - debug_assert!(self.get_cur().is_none()); + debug_assert!(!self.cur().is_eof()); let span = self.prev_span(); let token = Self::TokenAndSpan::new(token, span, false); self.set_cur(token); } - #[allow(dead_code)] - fn cur_debug<'b>(&'b self) -> Option<&'b Self::Token> - where - Self::TokenAndSpan: 'b, - { - self.get_cur().map(|it| it.token()) - } - - fn dump_cur(&mut self) -> String; - - /// Returns current token. - fn bump(&mut self) -> Self::Token { - let prev = match self.get_cur_mut().take() { - Some(t) => t, - None => unsafe { - debug_unreachable!( - "Current token is `None`. Parser should not call bump() without knowing \ - current token" - ) - }, - }; - self.set_prev_span(prev.span()); - prev.take_token() - } + fn dump_cur(&self) -> String; + + /// find next token. + fn bump(&mut self); + fn expect_word_token_and_bump(&mut self) -> Atom; + fn expect_number_token_and_bump(&mut self) -> (f64, Atom); + fn expect_string_token_and_bump(&mut self) -> (Atom, Atom); + fn expect_bigint_token_and_bump(&mut self) -> (Box, Atom); + fn expect_regex_token_and_bump(&mut self) -> (Atom, Atom); + fn expect_template_token_and_bump(&mut self) -> (LexResult, Atom); + fn expect_error_token_and_bump(&mut self) -> crate::error::Error; + fn expect_jsx_name_token_and_bump(&mut self) -> Atom; + fn expect_jsx_text_token_and_bump(&mut self) -> (Atom, Atom); + fn expect_shebang_token_and_bump(&mut self) -> Atom; #[inline] fn knows_cur(&self) -> bool { - self.get_cur().is_some() + !self.cur().is_eof() } - fn had_line_break_before_cur(&mut self) -> bool { - self.cur(); - self.get_cur() - .map(|it| it.had_line_break()) - .unwrap_or_else(|| true) + fn had_line_break_before_cur(&self) -> bool { + self.get_cur().had_line_break() } /// This returns true on eof. @@ -118,8 +108,8 @@ pub trait Buffer<'a> { if span.hi != next.span().lo { return; } - let cur = self.get_cur_mut().take().unwrap(); let next = self.next_mut().take().unwrap(); + let cur = self.get_cur(); let cur_token = cur.token(); let token = if cur_token.is_greater() { let next_token = next.token(); @@ -139,7 +129,6 @@ pub trait Buffer<'a> { // >>>= Self::Token::ZERO_FILL_RSHIFT_EQ } else { - self.set_cur(cur); self.set_next(Some(next)); return; } @@ -155,12 +144,10 @@ pub trait Buffer<'a> { // <<= Self::Token::LSHIFT_EQ } else { - self.set_cur(cur); self.set_next(Some(next)); return; } } else { - self.set_cur(cur); self.set_next(Some(next)); return; }; @@ -170,8 +157,8 @@ pub trait Buffer<'a> { } #[inline(always)] - fn is(&mut self, expected: &Self::Token) -> bool { - self.cur().is_some_and(|cur| cur == expected) + fn is(&self, expected: &Self::Token) -> bool { + self.cur() == expected } #[inline(always)] @@ -185,23 +172,13 @@ pub trait Buffer<'a> { /// Returns start of current token. #[inline] - fn cur_pos(&mut self) -> BytePos { - let _ = self.cur(); - self.get_cur() - .map(|item| item.span().lo) - .unwrap_or_else(|| { - // eof - self.last_pos() - }) + fn cur_pos(&self) -> BytePos { + self.get_cur().span().lo } #[inline] fn cur_span(&self) -> Span { - let data = self - .get_cur() - .map(|item| item.span()) - .unwrap_or(self.prev_span()); - Span::new_with_checked(data.lo, data.hi) + self.get_cur().span() } /// Returns last byte position of previous token. diff --git a/crates/swc_ecma_lexer/src/common/parser/class_and_fn.rs b/crates/swc_ecma_lexer/src/common/parser/class_and_fn.rs index d0d36cfc4c5a..2a57d9df7a24 100644 --- a/crates/swc_ecma_lexer/src/common/parser/class_and_fn.rs +++ b/crates/swc_ecma_lexer/src/common/parser/class_and_fn.rs @@ -64,7 +64,9 @@ pub fn parse_maybe_opt_binding_ident<'a>( fn parse_maybe_decorator_args<'a, P: Parser<'a>>(p: &mut P, expr: Box) -> PResult> { let type_args = if p.input().syntax().typescript() && p.input_mut().is(&P::Token::LESS) { - Some(parse_ts_type_args(p)?) + let ret = parse_ts_type_args(p)?; + p.assert_and_bump(&P::Token::GREATER); + Some(ret) } else { None }; @@ -178,7 +180,9 @@ pub fn parse_super_class<'a, P: Parser<'a>>( // may not include `TsExprWithTypeArgs` // but it's a super class with type params, for example, in JSX. if p.syntax().typescript() && p.input_mut().is(&P::Token::LESS) { - Ok((super_class, parse_ts_type_args(p).map(Some)?)) + let ret = parse_ts_type_args(p)?; + p.assert_and_bump(&P::Token::GREATER); + Ok((super_class, Some(ret))) } else { Ok((super_class, None)) } @@ -187,25 +191,19 @@ pub fn parse_super_class<'a, P: Parser<'a>>( } pub fn is_class_method<'a, P: Parser<'a>>(p: &mut P) -> bool { - p.input_mut().is(&P::Token::LPAREN) - || (p.input().syntax().typescript() - && p.input_mut() - .cur() - .is_some_and(|cur| cur.is_less() || cur.is_jsx_tag_start())) + let cur = p.input().cur(); + cur == &P::Token::LPAREN + || (p.input().syntax().typescript() && (cur.is_less() || cur.is_jsx_tag_start())) } pub fn is_class_property<'a, P: Parser<'a>>(p: &mut P, asi: bool) -> bool { - (p.input().syntax().typescript() - && p.input_mut() - .cur() - .is_some_and(|cur| cur.is_bang() || cur.is_colon())) - || p.input_mut() - .cur() - .is_some_and(|cur| cur.is_equal() || cur.is_rbrace()) + let cur = p.input().cur(); + (p.input().syntax().typescript() && (cur.is_bang() || cur.is_colon())) + || (cur.is_equal() || cur.is_rbrace()) || if asi { p.is_general_semi() } else { - p.input_mut().is(&P::Token::SEMI) + p.input().is(&P::Token::SEMI) } } @@ -986,11 +984,7 @@ fn parse_class_member_with_is_static<'a, P: Parser<'a>>( } trace_cur!(p, parse_class_member_with_is_static__normal_class_member); - let key = if readonly.is_some() - && p.input_mut() - .cur() - .is_some_and(|cur| cur.is_bang() || cur.is_colon()) - { + let key = if readonly.is_some() && (p.input().cur().is_bang() || p.input().cur().is_colon()) { Key::Public(PropName::Ident(IdentName::new( atom!("readonly"), readonly.unwrap(), @@ -1657,7 +1651,7 @@ fn parse_class_inner<'a, P: Parser<'a>>( p.do_outside_of_context(Context::HasSuperClass, parse_class_body)? }; - if p.input_mut().cur().is_none() { + if p.input().cur().is_eof() { let eof_text = p.input_mut().dump_cur(); p.emit_err( p.input().cur_span(), diff --git a/crates/swc_ecma_lexer/src/common/parser/expr.rs b/crates/swc_ecma_lexer/src/common/parser/expr.rs index 0fbe7745492f..556ad56e998f 100644 --- a/crates/swc_ecma_lexer/src/common/parser/expr.rs +++ b/crates/swc_ecma_lexer/src/common/parser/expr.rs @@ -35,10 +35,7 @@ use crate::{ }; pub(super) fn is_start_of_left_hand_side_expr<'a>(p: &mut impl Parser<'a>) -> bool { - let ctx = p.ctx(); - let Some(cur) = p.input_mut().cur() else { - return false; - }; + let cur = p.input().cur(); cur.is_this() || cur.is_null() || cur.is_super() @@ -56,8 +53,8 @@ pub(super) fn is_start_of_left_hand_side_expr<'a>(p: &mut impl Parser<'a>) -> bo || cur.is_class() || cur.is_new() || cur.is_regexp() - || cur.is_ident_ref(ctx) || cur.is_import() + || cur.is_ident_ref(p.ctx()) || cur.is_backquote() && { peek!(p).is_some_and(|peek| peek.is_lparen() || peek.is_less() || peek.is_dot()) } @@ -140,9 +137,10 @@ fn parse_yield_expr<'a, P: Parser<'a>>(p: &mut P) -> PResult> { }; if p.is_general_semi() || { - let Some(cur) = p.input_mut().cur() else { + let cur = p.input().cur(); + if cur.is_eof() { return parse_with_arg(p); - }; + } !cur.is_less() && !cur.is_star() && !cur.is_slash() @@ -228,14 +226,10 @@ pub(crate) fn parse_tagged_tpl<'a, P: Parser<'a>>( } pub fn parse_str_lit<'a>(p: &mut impl Parser<'a>) -> swc_ecma_ast::Str { - debug_assert!(p.input_mut().cur().is_some_and(|cur| cur.is_str())); - let Some(token_and_span) = p.input().get_cur() else { - unreachable!(); - }; + debug_assert!(p.input().cur().is_str()); + let token_and_span = p.input().get_cur(); let start = token_and_span.span().lo; - let t = p.bump(); - debug_assert!(t.is_str()); - let (value, raw) = t.take_str(p.input_mut()); + let (value, raw) = p.input_mut().expect_string_token_and_bump(); swc_ecma_ast::Str { span: p.span(start), value, @@ -244,10 +238,7 @@ pub fn parse_str_lit<'a>(p: &mut impl Parser<'a>) -> swc_ecma_ast::Str { } pub fn parse_lit<'a, P: Parser<'a>>(p: &mut P) -> PResult { - p.input_mut().cur(); - let Some(token_and_span) = p.input().get_cur() else { - return Err(eof_error(p)); - }; + let token_and_span = p.input().get_cur(); let start = token_and_span.span().lo; let cur = token_and_span.token(); let v = if cur.is_null() { @@ -262,25 +253,24 @@ pub fn parse_lit<'a, P: Parser<'a>>(p: &mut P) -> PResult { } else if cur.is_str() { Lit::Str(parse_str_lit(p)) } else if cur.is_num() { - let t = p.bump(); - let (value, raw) = t.take_num(p.input_mut()); + let (value, raw) = p.input_mut().expect_number_token_and_bump(); Lit::Num(swc_ecma_ast::Number { span: p.span(start), value, raw: Some(raw), }) } else if cur.is_bigint() { - let t = p.bump(); - let (value, raw) = t.take_bigint(p.input_mut()); + let (value, raw) = p.input_mut().expect_bigint_token_and_bump(); Lit::BigInt(swc_ecma_ast::BigInt { span: p.span(start), value, raw: Some(raw), }) } else if cur.is_error() { - let c = p.input_mut().bump(); - let err = c.take_error(p.input_mut()); + let err = p.input_mut().expect_error_token_and_bump(); return Err(err); + } else if cur.is_eof() { + return Err(eof_error(p)); } else { unreachable!("parse_lit should not be called for {:?}", cur) }; @@ -375,18 +365,12 @@ fn parse_assignment_expr_base<'a, P: Parser<'a>>(p: &mut P) -> PResult let start = p.input().cur_span(); if p.input().syntax().typescript() - && (p - .input_mut() - .cur() - .is_some_and(|cur| cur.is_less() || cur.is_jsx_tag_start())) + && (p.input().cur().is_less() || p.input().cur().is_jsx_tag_start()) && (peek!(p).is_some_and(|peek| peek.is_word() || peek.is_jsx_name())) { let res = p.do_outside_of_context(Context::WillExpectColonForCond, |p| { try_parse_ts(p, |p| { - if p.input_mut() - .cur() - .is_some_and(|cur| cur.is_jsx_tag_start()) - { + if p.input().cur().is_jsx_tag_start() { if let Some(TokenContext::JSXOpeningTag) = p.input_mut().token_context().current() { @@ -428,14 +412,13 @@ fn parse_assignment_expr_base<'a, P: Parser<'a>>(p: &mut P) -> PResult return parse_yield_expr(p); } - let Some(cur) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; + let cur = p.input().cur(); if cur.is_error() { - let c = p.input_mut().bump(); - let err = c.take_error(p.input_mut()); + let err = p.input_mut().expect_error_token_and_bump(); return Err(err); + } else if cur.is_eof() { + return Err(eof_error(p)); } p.state_mut().potential_arrow_start = @@ -469,7 +452,7 @@ pub fn finish_assignment_expr<'a, P: Parser<'a>>( ) -> PResult> { trace_cur!(p, finish_assignment_expr); - if let Some(op) = p.input_mut().cur().and_then(|t| t.as_assign_op()) { + if let Some(op) = p.input().cur().as_assign_op() { let left = if op == AssignOp::Assign { match AssignTarget::try_from(reparse_expr_as_pat(p, PatType::AssignPat, cond)?) { Ok(pat) => pat, @@ -586,7 +569,6 @@ fn parse_subscript<'a, P: Parser<'a>>( no_computed_member: bool, ) -> PResult<(Box, bool)> { trace_cur!(p, parse_subscript); - p.input_mut().cur(); if p.input().syntax().typescript() { if !p.input_mut().had_line_break_before_cur() && p.input_mut().is(&P::Token::BANG) { @@ -651,8 +633,10 @@ fn parse_subscript<'a, P: Parser<'a>>( } let type_args = parse_ts_type_args(p)?; + p.assert_and_bump(&P::Token::GREATER); + let cur = p.input().cur(); - if !no_call && p.input_mut().is(&P::Token::LPAREN) { + if !no_call && cur.is_lparen() { // possibleAsync always false here, because we would have handled it // above. (won't be any undefined arguments) let args = parse_args(p, is_dynamic_import)?; @@ -690,11 +674,10 @@ fn parse_subscript<'a, P: Parser<'a>>( .into(), true, ))) - } else if p.input_mut().cur().is_some_and(|cur| { - cur.is_no_substitution_template_literal() - || cur.is_template_head() - || cur.is_backquote() - }) { + } else if cur.is_no_substitution_template_literal() + || cur.is_template_head() + || cur.is_backquote() + { p.parse_tagged_tpl( match mut_obj_opt { Some(Callee::Expr(obj)) => obj.take(), @@ -704,11 +687,7 @@ fn parse_subscript<'a, P: Parser<'a>>( ) .map(|expr| (expr.into(), true)) .map(Some) - } else if p - .input_mut() - .cur() - .is_some_and(|cur| cur.is_equal() || cur.is_as() || cur.is_satisfies()) - { + } else if cur.is_equal() || cur.is_as() || cur.is_satisfies() { Ok(Some(( TsInstantiation { span: p.span(start), @@ -742,12 +721,9 @@ fn parse_subscript<'a, P: Parser<'a>>( None }; - if obj.is_import() - && !p - .input_mut() - .cur() - .is_some_and(|cur| cur.is_dot() || cur.is_lparen()) - { + let cur = p.input().cur(); + + if obj.is_import() && !(cur.is_dot() || cur.is_lparen()) { unexpected!(p, "`.` or `(`") } @@ -792,12 +768,13 @@ fn parse_subscript<'a, P: Parser<'a>>( if !p.ctx().contains(Context::AllowDirectSuper) && !p.input().syntax().allow_super_outside_method() { - syntax_error!(p, p.input().cur_span(), SyntaxError::InvalidSuper); + syntax_error!(p, obj.span, SyntaxError::InvalidSuper); } else if question_dot_token.is_some() { if no_call { - syntax_error!(p, p.input().cur_span(), SyntaxError::InvalidSuperCall); + syntax_error!(p, obj.span, SyntaxError::InvalidSuperCall); + } else { + syntax_error!(p, obj.span, SyntaxError::InvalidSuper); } - syntax_error!(p, p.input().cur_span(), SyntaxError::InvalidSuper); } else { SuperPropExpr { span, @@ -849,7 +826,9 @@ fn parse_subscript<'a, P: Parser<'a>>( || (!no_call && p.input_mut().is(&P::Token::LPAREN)) { let type_args = if p.syntax().typescript() && p.input_mut().is(&P::Token::LESS) { - parse_ts_type_args(p).map(Some)? + let ret = parse_ts_type_args(p)?; + p.assert_and_bump(&P::Token::GREATER); + Some(ret) } else { None }; @@ -947,12 +926,13 @@ fn parse_subscript<'a, P: Parser<'a>>( if !p.ctx().contains(Context::AllowDirectSuper) && !p.input().syntax().allow_super_outside_method() { - syntax_error!(p, p.input().cur_span(), SyntaxError::InvalidSuper); + syntax_error!(p, obj.span, SyntaxError::InvalidSuper); } else if question_dot_token.is_some() { if no_call { - syntax_error!(p, p.input().cur_span(), SyntaxError::InvalidSuperCall); + syntax_error!(p, obj.span, SyntaxError::InvalidSuperCall); + } else { + syntax_error!(p, obj.span, SyntaxError::InvalidSuper); } - syntax_error!(p, p.input().cur_span(), SyntaxError::InvalidSuper); } else { match prop { MemberProp::Ident(ident) => SuperPropExpr { @@ -1016,11 +996,11 @@ fn parse_subscript<'a, P: Parser<'a>>( }; // MemberExpression[?Yield, ?Await] TemplateLiteral[?Yield, ?Await, +Tagged] - if p.input_mut().cur().is_some_and(|cur| { - cur.is_template_head() - || cur.is_no_substitution_template_literal() - || cur.is_backquote() - }) { + let cur = p.input().cur(); + if cur.is_template_head() + || cur.is_no_substitution_template_literal() + || cur.is_backquote() + { let tpl = p.do_outside_of_context(Context::WillExpectColonForCond, |p| { p.parse_tagged_tpl(expr, None) })?; @@ -1158,14 +1138,14 @@ fn parse_member_expr_or_new_expr_inner<'a, P: Parser<'a>>( } } - let type_args = if p.input().syntax().typescript() - && p.input_mut() - .cur() - .is_some_and(|cur| cur.is_less() || cur.is_lshift()) - { + let type_args = if p.input().syntax().typescript() && { + let cur = p.input().cur(); + cur.is_less() || cur.is_lshift() + } { try_parse_ts(p, |p| { let args = p.do_outside_of_context(Context::ShouldNotLexLtOrGtAsType, parse_ts_type_args)?; + p.assert_and_bump(&P::Token::GREATER); if !p.input_mut().is(&P::Token::LPAREN) { let span = p.input().cur_span(); let cur = p.input_mut().dump_cur(); @@ -1259,12 +1239,9 @@ pub fn parse_bin_expr<'a, P: Parser<'a>>(p: &mut P) -> PResult> { Err(err) => { trace_cur!(p, parse_bin_expr__recovery_unary_err); - let Some(cur) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; + let cur = p.input().cur(); if cur.is_error() { - let c = p.input_mut().bump(); - let err = c.take_error(p.input_mut()); + let err = p.input_mut().expect_error_token_and_bump(); return Err(err); } else if (cur.is_in() && ctx.contains(Context::IncludeInExpr)) || cur.is_instanceof() @@ -1272,6 +1249,8 @@ pub fn parse_bin_expr<'a, P: Parser<'a>>(p: &mut P) -> PResult> { { p.emit_err(p.input().cur_span(), SyntaxError::TS1109); Invalid { span: err.span() }.into() + } else if cur.is_eof() { + return Err(eof_error(p)); } else { return Err(err); } @@ -1340,7 +1319,6 @@ fn parse_bin_op_recursively_inner<'a, P: Parser<'a>>( let expr = left; let node = if peek!(p).is_some_and(|cur| cur.is_const()) { p.bump(); // as - p.input_mut().cur(); p.bump(); // const TsConstAssertion { span: p.span(start), @@ -1377,15 +1355,12 @@ fn parse_bin_op_recursively_inner<'a, P: Parser<'a>>( let ctx = p.ctx(); // Return left on eof - let word = match p.input_mut().cur() { - Some(cur) => cur, - None => return Ok((left, None)), - }; - let op = if word.is_in() && ctx.contains(Context::IncludeInExpr) { + let cur = p.input().cur(); + let op = if cur.is_in() && ctx.contains(Context::IncludeInExpr) { op!("in") - } else if word.is_instanceof() { + } else if cur.is_instanceof() { op!("instanceof") - } else if let Some(op) = word.as_bin_op() { + } else if let Some(op) = cur.as_bin_op() { op } else { return Ok((left, None)); @@ -1490,10 +1465,7 @@ fn parse_bin_op_recursively_inner<'a, P: Parser<'a>>( pub(crate) fn parse_unary_expr<'a, P: Parser<'a>>(p: &mut P) -> PResult> { trace_cur!(p, parse_unary_expr); - p.input_mut().cur(); - let Some(token_and_span) = p.input().get_cur() else { - syntax_error!(p, p.input().cur_span(), SyntaxError::TS1109); - }; + let token_and_span = p.input().get_cur(); let start = token_and_span.span().lo; let cur = token_and_span.token(); @@ -1514,11 +1486,12 @@ pub(crate) fn parse_unary_expr<'a, P: Parser<'a>>(p: &mut P) -> PResult>(p: &mut P) -> PResult>(p: &mut P) -> PResult expr, @@ -1583,6 +1556,8 @@ pub(crate) fn parse_unary_expr<'a, P: Parser<'a>>(p: &mut P) -> PResult>(p: &mut P) -> PResult>( let span = p.span(start); if !ctx.contains(Context::InAsync) - && (p.is_general_semi() - || p.input_mut() - .cur() - .is_some_and(|cur| cur.is_rparen() || cur.is_rbracket() || cur.is_comma())) + && (p.is_general_semi() || { + let cur = p.input().cur(); + cur.is_rparen() || cur.is_rbracket() || cur.is_comma() + }) { if ctx.contains(Context::Module) { p.emit_err(span, SyntaxError::InvalidIdentInAsync); @@ -1686,8 +1659,6 @@ pub(super) fn parse_for_head_prefix<'a>(p: &mut impl Parser<'a>) -> PResult, const PARSE_JSX: bool>(p: &mut P) -> PResult> { trace_cur!(p, parse_lhs_expr); - p.input_mut().cur(); - // parse jsx if PARSE_JSX && p.input().syntax().jsx() { fn into_expr(e: Either) -> Box { @@ -1696,18 +1667,17 @@ pub fn parse_lhs_expr<'a, P: Parser<'a>, const PARSE_JSX: bool>(p: &mut P) -> PR Either::Right(r) => r.into(), } } - let Some(token_and_span) = p.input().get_cur() else { - return Err(eof_error(p)); - }; + let token_and_span = p.input().get_cur(); let cur = token_and_span.token(); if cur.is_jsx_text() { return Ok(Box::new(Expr::Lit(Lit::JSXText(parse_jsx_text(p))))); } else if cur.is_jsx_tag_start() { return parse_jsx_element(p).map(into_expr); } else if cur.is_error() { - let c = p.input_mut().bump(); - let err = c.take_error(p.input_mut()); + let err = p.input_mut().expect_error_token_and_bump(); return Err(err); + } else if cur.is_eof() { + return Err(eof_error(p)); } if p.input_mut().is(&P::Token::LESS) && !peek!(p).is_some_and(|peek| peek.is_bang()) { @@ -1721,9 +1691,7 @@ pub fn parse_lhs_expr<'a, P: Parser<'a>, const PARSE_JSX: bool>(p: &mut P) -> PR } } - let Some(token_and_span) = p.input().get_cur() else { - return Err(eof_error(p)); - }; + let token_and_span = p.input().get_cur(); let start = token_and_span.span().lo; let cur = token_and_span.token(); @@ -1737,18 +1705,20 @@ pub fn parse_lhs_expr<'a, P: Parser<'a>, const PARSE_JSX: bool>(p: &mut P) -> PR } else if cur.is_import() { p.bump(); // eat `import` return parse_dynamic_import_or_import_meta(p, start, false); + } else if cur.is_eof() { + return Err(eof_error(p)); } let callee = parse_new_expr(p)?; return_if_arrow!(p, callee); - let type_args = if p.input().syntax().typescript() - && p.input_mut() - .cur() - .is_some_and(|cur| cur.is_less() || cur.is_lshift()) - { + let type_args = if p.input().syntax().typescript() && { + let cur = p.input().cur(); + cur.is_less() || cur.is_lshift() + } { try_parse_ts(p, |p| { let type_args = parse_ts_type_args(p)?; + p.assert_and_bump(&P::Token::GREATER); if p.input_mut().is(&P::Token::LPAREN) { Ok(Some(type_args)) } else { @@ -1767,7 +1737,7 @@ pub fn parse_lhs_expr<'a, P: Parser<'a>, const PARSE_JSX: bool>(p: &mut P) -> PR expect!(p, &P::Token::LPAREN); } debug_assert!( - !p.input_mut().cur().is_some_and(|cur| cur.is_lparen()), + !p.input().cur().is_lparen(), "parse_new_expr() should eat paren if it exists" ); return Ok(NewExpr { type_args, ..ne }.into()); @@ -1879,7 +1849,7 @@ fn parse_args_or_pats_inner<'a, P: Parser<'a>>( } else { let mut expr = parse_bin_expr(p)?; - if p.input_mut().cur().is_some_and(|t| t.is_assign_op()) { + if p.input().cur().is_assign_op() { expr = finish_assignment_expr(p, start, expr)? } @@ -1898,7 +1868,6 @@ fn parse_args_or_pats_inner<'a, P: Parser<'a>>( peek.is_comma() || peek.is_equal() || peek.is_rparen() || peek.is_colon() }) { p.assert_and_bump(&P::Token::QUESTION); - p.input_mut().cur(); if arg.spread.is_some() { p.emit_err(p.input().prev_span(), SyntaxError::TS1047); } @@ -2205,7 +2174,7 @@ pub fn parse_paren_expr_or_arrow_fn<'a, P: Parser<'a>>( ..Default::default() }; if let BlockStmtOrExpr::BlockStmt(..) = &*arrow_expr.body { - if p.input_mut().cur().is_some_and(|t| t.is_bin_op()) { + if p.input().cur().is_bin_op() { // ) is required p.emit_err(p.input().cur_span(), SyntaxError::TS1005); let errorred_expr = parse_bin_op_recursively(p, Box::new(arrow_expr.into()), 0)?; @@ -2329,14 +2298,13 @@ pub fn parse_primary_expr_rest<'a, P: Parser<'a>>( None }; - p.input_mut().cur(); - let Some(token_and_span) = p.input().get_cur() else { - return Err(eof_error(p)); - }; + let token_and_span = p.input().get_cur(); let cur = token_and_span.token(); if cur.is_class() { return parse_class_expr(p, start, decorators.unwrap_or_default()); + } else if cur.is_eof() { + return Err(eof_error(p)); } let try_parse_arrow_expr = |p: &mut P, id: Ident, id_is_async| -> PResult> { @@ -2447,11 +2415,9 @@ pub fn parse_primary_expr_rest<'a, P: Parser<'a>>( } .into()) } else if p.is_ident_ref() { - let cur = p.bump(); + let cur = p.input().cur(); let id_is_async = cur.is_async(); - let Some(word) = cur.take_word(p.input_mut()) else { - unreachable!() - }; + let word = p.input_mut().expect_word_token_and_bump(); if p.ctx().contains(Context::InClassField) && word == atom!("arguments") { p.emit_err(p.input().prev_span(), SyntaxError::ArgumentsInClassField) }; @@ -2464,20 +2430,16 @@ pub fn parse_primary_expr_rest<'a, P: Parser<'a>>( pub fn try_parse_regexp<'a, P: Parser<'a>>(p: &mut P, start: BytePos) -> Option> { // Regexp - debug_assert!(p - .input_mut() - .cur() - .is_some_and(|token| token.is_slash() || token.is_slash_eq())); - - p.bump(); // `/` or `/=` + debug_assert!(p.input().cur().is_slash() || p.input().cur().is_slash_eq()); p.input_mut().set_next_regexp(Some(start)); - if p.input_mut().cur().is_some_and(|cur| cur.is_regexp()) { - p.input_mut().set_next_regexp(None); + p.bump(); // `/` or `/=` - let t = p.bump(); - let (exp, flags) = t.take_regexp(p.input_mut()); + let cur = p.input().cur(); + if cur.is_regexp() { + p.input_mut().set_next_regexp(None); + let (exp, flags) = p.input_mut().expect_regex_token_and_bump(); let span = p.span(start); let mut flags_count = @@ -2551,7 +2513,7 @@ pub fn try_parse_async_start<'a, P: Parser<'a>>( } pub fn parse_this_expr<'a>(p: &mut impl Parser<'a>, start: BytePos) -> PResult> { - debug_assert!(p.input_mut().cur().is_some_and(|t| t.is_this())); + debug_assert!(p.input().cur().is_this()); p.input_mut().bump(); Ok(ThisExpr { span: p.span(start), @@ -2567,7 +2529,6 @@ pub fn parse_this_expr<'a>(p: &mut impl Parser<'a>, start: BytePos) -> PResult>(p: &mut P) -> PResult> { trace_cur!(p, parse_primary_expr); - let _ = p.input_mut().cur(); let start = p.cur_pos(); let can_be_arrow = p @@ -2576,43 +2537,39 @@ pub(crate) fn parse_primary_expr<'a, P: Parser<'a>>(p: &mut P) -> PResult>(p: &mut P) -> PResult { - let Some(cur) = p.input_mut().cur() else { - unexpected!(p, "identifier or string"); - }; + let cur = p.input().cur(); let module_export_name = if cur.is_str() { ModuleExportName::Str(parse_str_lit(p)) } else if cur.is_word() { @@ -31,18 +25,13 @@ pub fn parse_module_export_name<'a, P: Parser<'a>>(p: &mut P) -> PResult>(p: &mut P) -> PResult { - p.input_mut().cur(); - let Some(token_and_span) = p.input().get_cur() else { - return Err(eof_error(p)); - }; + let token_and_span = p.input().get_cur(); let start = token_and_span.span().lo; let cur = token_and_span.token(); let w = if cur.is_word() { - let t = p.bump(); - t.take_word(p.input_mut()).unwrap() + p.input_mut().expect_word_token_and_bump() } else if cur.is_jsx_name() && p.ctx().contains(Context::InType) { - let t = p.bump(); - t.take_jsx_name(p.input_mut()) + p.input_mut().expect_jsx_name_token_and_bump() } else { syntax_error!(p, SyntaxError::ExpectedIdent) }; @@ -100,7 +89,7 @@ pub fn parse_binding_ident<'a>( ) -> PResult { trace_cur!(p, parse_binding_ident); - if disallow_let && p.input_mut().cur().is_some_and(|cur| cur.is_let()) { + if disallow_let && p.input().cur().is_let() { unexpected!(p, "let is reserved in const, let, class declaration") } @@ -126,18 +115,14 @@ pub fn parse_opt_binding_ident<'a>( disallow_let: bool, ) -> PResult> { trace_cur!(p, parse_opt_binding_ident); - let ctx = p.ctx(); - p.input_mut().cur(); - let Some(token_and_span) = p.input().get_cur() else { - return Ok(None); - }; + let token_and_span = p.input().get_cur(); let cur = token_and_span.token(); if cur.is_this() && p.input().syntax().typescript() { let start = token_and_span.span().lo; Ok(Some( Ident::new_no_ctxt(atom!("this"), p.span(start)).into(), )) - } else if cur.is_word() && !cur.is_reserved(ctx) { + } else if cur.is_word() && !cur.is_reserved(p.ctx()) { parse_binding_ident(p, disallow_let).map(Some) } else { Ok(None) @@ -154,25 +139,22 @@ pub fn parse_ident<'a>( ) -> PResult { trace_cur!(p, parse_ident); - p.input_mut().cur(); - let Some(token_and_span) = p.input().get_cur() else { - return Err(eof_error(p)); - }; + let token_and_span = p.input().get_cur(); if !token_and_span.token().is_word() { syntax_error!(p, SyntaxError::ExpectedIdent) } - let start = token_and_span.span().lo; - let t = p.bump(); + let span = token_and_span.span(); + let start = span.lo; + let t = token_and_span.token(); // Spec: // It is a Syntax Error if this phrase is contained in strict mode code and the // StringValue of IdentifierName is: "implements", "interface", "let", // "package", "private", "protected", "public", "static", or "yield". if t.is_enum() { - p.emit_err( - p.input().prev_span(), - SyntaxError::InvalidIdentInStrict(t.clone().take_word(p.input()).unwrap()), - ); + let word = p.input_mut().expect_word_token_and_bump(); + p.emit_err(span, SyntaxError::InvalidIdentInStrict(word.clone())); + return Ok(Ident::new_no_ctxt(word, p.span(start))); } else if t.is_yield() || t.is_let() || t.is_static() @@ -183,10 +165,9 @@ pub fn parse_ident<'a>( || t.is_protected() || t.is_public() { - p.emit_strict_mode_err( - p.input().prev_span(), - SyntaxError::InvalidIdentInStrict(t.clone().take_word(p.input()).unwrap()), - ); + let word = p.input_mut().expect_word_token_and_bump(); + p.emit_strict_mode_err(span, SyntaxError::InvalidIdentInStrict(word.clone())); + return Ok(Ident::new_no_ctxt(word, p.span(start))); }; let word; @@ -199,13 +180,13 @@ pub fn parse_ident<'a>( if ctx.contains(Context::InDeclare) { word = atom!("await"); } else if ctx.contains(Context::InStaticBlock) { - syntax_error!(p, p.input().prev_span(), SyntaxError::ExpectedIdent) + syntax_error!(p, span, SyntaxError::ExpectedIdent) } else if ctx.contains(Context::Module) | ctx.contains(Context::InAsync) { - syntax_error!(p, p.input().prev_span(), SyntaxError::InvalidIdentInAsync) + syntax_error!(p, span, SyntaxError::InvalidIdentInAsync) } else if incl_await { word = atom!("await") } else { - syntax_error!(p, p.input().prev_span(), SyntaxError::ExpectedIdent) + syntax_error!(p, span, SyntaxError::ExpectedIdent) } } else if t.is_this() && p.input().syntax().typescript() { word = atom!("this") @@ -215,18 +196,19 @@ pub fn parse_ident<'a>( let ident = t.take_known_ident(); word = ident } else if t.is_unknown_ident() { - let ident = t.take_unknown_ident(p.input_mut()); - if p.ctx().contains(Context::InClassField) && ident == atom!("arguments") { - p.emit_err(p.input().prev_span(), SyntaxError::ArgumentsInClassField) + let word = p.input_mut().expect_word_token_and_bump(); + if p.ctx().contains(Context::InClassField) && word == atom!("arguments") { + p.emit_err(span, SyntaxError::ArgumentsInClassField) } - word = ident + return Ok(Ident::new_no_ctxt(word, p.span(start))); } else if t.is_yield() && incl_yield { word = atom!("yield") } else if t.is_null() || t.is_true() || t.is_false() || t.is_keyword() { - syntax_error!(p, p.input().prev_span(), SyntaxError::ExpectedIdent) + syntax_error!(p, span, SyntaxError::ExpectedIdent) } else { unreachable!() } + p.bump(); Ok(Ident::new_no_ctxt(word, p.span(start))) } diff --git a/crates/swc_ecma_lexer/src/common/parser/jsx.rs b/crates/swc_ecma_lexer/src/common/parser/jsx.rs index 0f1be29ad33e..009e891b5942 100644 --- a/crates/swc_ecma_lexer/src/common/parser/jsx.rs +++ b/crates/swc_ecma_lexer/src/common/parser/jsx.rs @@ -63,9 +63,9 @@ pub fn parse_jsx_expr_container<'a, P: Parser<'a>>(p: &mut P) -> PResult>(p: &mut P) -> PResult { debug_assert!(p.input().syntax().jsx()); trace_cur!(p, parse_jsx_ident); - if p.input_mut().cur().is_some_and(|cur| cur.is_jsx_name()) { - let t = p.bump(); - let name = t.take_jsx_name(p.input_mut()); + let cur = p.input().cur(); + if cur.is_jsx_name() { + let name = p.input_mut().expect_jsx_name_token_and_bump(); let span = p.input().prev_span(); Ok(Ident::new_no_ctxt(name, span)) } else if p.ctx().contains(Context::InForcedJsxContext) { @@ -121,7 +121,7 @@ fn parse_jsx_element_name<'a, P: Parser<'a>>(p: &mut P) -> PResult(p: &mut impl Parser<'a>) -> JSXEmptyExpr { +pub fn parse_jsx_empty_expr<'a>(p: &mut impl Parser<'a>) -> JSXEmptyExpr { debug_assert!(p.input().syntax().jsx()); let start = p.input_mut().cur_pos(); JSXEmptyExpr { @@ -131,10 +131,11 @@ fn parse_jsx_empty_expr<'a>(p: &mut impl Parser<'a>) -> JSXEmptyExpr { pub fn parse_jsx_text<'a>(p: &mut impl Parser<'a>) -> JSXText { debug_assert!(p.input().syntax().jsx()); - debug_assert!(p.input_mut().cur().is_some_and(|t| t.is_jsx_text())); - let token = p.bump(); + + let cur = p.input().cur(); + debug_assert!(cur.is_jsx_text()); + let (value, raw) = p.input_mut().expect_jsx_text_token_and_bump(); let span = p.input().prev_span(); - let (value, raw) = token.take_jsx_text(p.input_mut()); JSXText { span, value, raw } } @@ -160,9 +161,7 @@ fn parse_jsx_attr_value<'a, P: Parser<'a>>(p: &mut P) -> PResult { let start = p.cur_pos(); - let Some(cur) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; + let cur = p.input().cur(); if cur.is_lbrace() { let node = parse_jsx_expr_container(p)?; jsx_expr_container_to_jsx_attr_value(p, start, node) @@ -181,14 +180,13 @@ fn parse_jsx_attr_value<'a, P: Parser<'a>>(p: &mut P) -> PResult { } /// Parse JSX spread child -pub fn parse_jsx_spread_child<'a, P: Parser<'a>>(p: &mut P) -> PResult { +fn parse_jsx_spread_child<'a, P: Parser<'a>>(p: &mut P) -> PResult { debug_assert!(p.input().syntax().jsx()); - debug_assert!(p.input_mut().cur().is_some_and(|cur| cur.is_lbrace())); + debug_assert!(p.input().cur().is_lbrace()); debug_assert!(peek!(p).is_some_and(|peek| peek.is_dotdotdot())); let start = p.cur_pos(); p.bump(); // bump "{" - let _ = p.input_mut().cur(); p.bump(); // bump "..." let expr = p.parse_expr()?; expect!(p, &P::Token::RBRACE); @@ -256,13 +254,11 @@ fn parse_jsx_opening_element_at<'a, P: Parser<'a>>( fn parse_jsx_attrs<'a, P: Parser<'a>>(p: &mut P) -> PResult> { let mut attrs = Vec::with_capacity(8); - while p.input_mut().cur().is_some() { + while !p.input().cur().is_eof() { trace_cur!(p, parse_jsx_opening__attrs_loop); - if p.input_mut() - .cur() - .is_some_and(|cur| cur.is_slash() || cur.is_jsx_tag_end()) - { + let cur = p.input().cur(); + if cur.is_slash() || cur.is_jsx_tag_end() { break; } @@ -282,7 +278,11 @@ fn parse_jsx_opening_element_after_name<'a, P: Parser<'a>>( debug_assert!(p.input().syntax().jsx()); let type_args = if p.input().syntax().typescript() && p.input_mut().is(&P::Token::LESS) { - try_parse_ts(p, |p| parse_ts_type_args(p).map(Some)) + try_parse_ts(p, |p| { + let ret = parse_ts_type_args(p)?; + p.assert_and_bump(&P::Token::GREATER); + Ok(Some(ret)) + }) } else { None }; @@ -314,22 +314,21 @@ fn parse_jsx_element_at<'a, P: Parser<'a>>( ) -> PResult> { debug_assert!(p.input().syntax().jsx()); - let Some(cur) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; + let cur = p.input().cur(); if cur.is_error() { - let c = p.input_mut().bump(); - let err = c.take_error(p.input_mut()); - return Err(err); + let error = p.input_mut().expect_error_token_and_bump(); + return Err(error); + } else if cur.is_eof() { + return Err(eof_error(p)); } - let start = p.cur_pos(); - let cur = p.bump(); let forced_jsx_context = if cur.is_less() { true } else { debug_assert!(cur.is_jsx_tag_start()); false }; + let start = p.cur_pos(); + p.bump(); p.do_outside_of_context(Context::ShouldNotLexLtOrGtAsType, |p| { let f = |p: &mut P| { @@ -349,16 +348,14 @@ fn parse_jsx_element_at<'a, P: Parser<'a>>( if !self_closing { 'contents: loop { - let Some(cur) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; + let cur = p.input().cur(); if cur.is_jsx_tag_start() { let start = p.cur_pos(); if peek!(p).is_some_and(|peek| peek.is_slash()) { p.bump(); // JSXTagStart - let Some(_) = p.input_mut().cur() else { + if p.input().cur().is_eof() { return Err(eof_error(p)); - }; + } p.assert_and_bump(&P::Token::DIV); closing_element = parse_jsx_closing_element_at(p, start).map(Some)?; break 'contents; @@ -446,10 +443,10 @@ pub(crate) fn parse_jsx_element<'a, P: Parser<'a>>( trace_cur!(p, parse_jsx_element); debug_assert!(p.input().syntax().jsx()); - debug_assert!(p - .input_mut() - .cur() - .is_some_and(|cur| cur.is_jsx_tag_start() || cur.is_less())); + debug_assert!({ + let cur = p.input().cur(); + cur.is_jsx_tag_start() || cur.is_less() + }); let start_pos = p.cur_pos(); diff --git a/crates/swc_ecma_lexer/src/common/parser/macros.rs b/crates/swc_ecma_lexer/src/common/parser/macros.rs index 857fb3922715..d46ea4fbf1f6 100644 --- a/crates/swc_ecma_lexer/src/common/parser/macros.rs +++ b/crates/swc_ecma_lexer/src/common/parser/macros.rs @@ -29,10 +29,10 @@ macro_rules! syntax_error { ($p:expr, $span:expr, $err:expr) => {{ let err = $crate::error::Error::new($span, $err); { - if $p.input_mut().cur().is_some_and(|t| t.is_error()) { - let c = $p.input_mut().bump(); - let err = c.take_error($p.input_mut()); - $p.emit_error(err); + let cur = $p.input().cur(); + if cur.is_error() { + let error = $p.input_mut().expect_error_token_and_bump(); + $p.emit_error(error); } } if cfg!(feature = "debug") { @@ -41,7 +41,7 @@ macro_rules! syntax_error { file!(), line!(), column!(), - $p.input_mut().cur() + $p.input().cur() ); } return Err(err.into()); @@ -54,7 +54,7 @@ macro_rules! peek { $p.input().knows_cur(), "parser should not call peek() without knowing current token. Current token is {:?}", - $p.input_mut().cur(), + $p.input().cur(), ); $p.input_mut().peek() }}; @@ -63,7 +63,7 @@ Current token is {:?}", macro_rules! trace_cur { ($p:expr, $name:ident) => {{ if cfg!(feature = "debug") { - tracing::debug!("{}: {:?}", stringify!($name), $p.input_mut().cur()); + tracing::debug!("{}: {:?}", stringify!($name), $p.input().cur()); } }}; } @@ -85,7 +85,7 @@ macro_rules! debug_tracing { /// Returns true on eof. macro_rules! eof { ($p:expr) => { - $p.input_mut().cur().is_none() + $p.input().cur().is_eof() }; } diff --git a/crates/swc_ecma_lexer/src/common/parser/mod.rs b/crates/swc_ecma_lexer/src/common/parser/mod.rs index e5dbf2994ccb..60ac34af3464 100644 --- a/crates/swc_ecma_lexer/src/common/parser/mod.rs +++ b/crates/swc_ecma_lexer/src/common/parser/mod.rs @@ -155,9 +155,9 @@ pub trait Parser<'a>: Sized + Clone { if self.ctx().contains(Context::IgnoreError) || !self.syntax().early_errors() { return; } - if self.input_mut().cur().is_some_and(|cur| cur.is_error()) { - let err = self.input_mut().bump(); - let err = err.take_error(self.input_mut()); + let cur = self.input().cur(); + if cur.is_error() { + let err = self.input_mut().expect_error_token_and_bump(); self.input().iter().add_error(err); } self.input().iter().add_error(error); @@ -190,8 +190,8 @@ pub trait Parser<'a>: Sized + Clone { } #[inline(always)] - fn cur_pos(&mut self) -> BytePos { - self.input_mut().cur_pos() + fn cur_pos(&self) -> BytePos { + self.input().cur_pos() } #[inline(always)] @@ -201,24 +201,20 @@ pub trait Parser<'a>: Sized + Clone { #[inline] fn is_general_semi(&mut self) -> bool { - let Some(cur) = self.input_mut().cur() else { - return true; - }; - cur.is_semi() || cur.is_rbrace() || self.input_mut().had_line_break_before_cur() + let cur = self.input().cur(); + cur.is_semi() || cur.is_rbrace() || cur.is_eof() || self.input().had_line_break_before_cur() } fn eat_general_semi(&mut self) -> bool { if cfg!(feature = "debug") { - tracing::trace!("eat(';'): cur={:?}", self.input_mut().cur()); + tracing::trace!("eat(';'): cur={:?}", self.input().cur()); } - let Some(cur) = self.input_mut().cur() else { - return true; - }; + let cur = self.input().cur(); if cur.is_semi() { self.bump(); true } else { - cur.is_rbrace() || self.input_mut().had_line_break_before_cur() + cur.is_rbrace() || self.input().had_line_break_before_cur() || cur.is_eof() } } @@ -255,7 +251,7 @@ pub trait Parser<'a>: Sized + Clone { } #[inline(always)] - fn bump(&mut self) -> Self::Token { + fn bump(&mut self) { debug_assert!( self.input().knows_cur(), "parser should not call bump() without knowing current token" @@ -276,11 +272,10 @@ pub trait Parser<'a>: Sized + Clone { #[inline(always)] fn assert_and_bump(&mut self, token: &Self::Token) { debug_assert!( - self.input_mut().is(token), + self.input().is(token), "assertion failed: expected {token:?}, got {:?}", - self.input_mut().cur() + self.input().cur() ); - let _ = self.input_mut().cur(); self.bump(); } @@ -325,12 +320,9 @@ pub trait Parser<'a>: Sized + Clone { fn parse_tpl_element(&mut self, is_tagged_tpl: bool) -> PResult { let start = self.cur_pos(); - let Some(cur) = self.input_mut().cur() else { - return Err(eof_error(self)); - }; + let cur = self.input().cur(); let (raw, cooked) = if cur.is_template() { - let cur = self.bump(); - let (cooked, raw) = cur.take_template(self.input_mut()); + let (cooked, raw) = self.input_mut().expect_template_token_and_bump(); match cooked { Ok(cooked) => (raw, Some(cooked)), Err(err) => { @@ -358,30 +350,25 @@ pub trait Parser<'a>: Sized + Clone { trace_cur!(self, parse_prop_name); self.do_inside_of_context(Context::InPropertyName, |p| { let start = p.input_mut().cur_pos(); - let Some(cur) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; + let cur = p.input().cur(); let v = if cur.is_str() { PropName::Str(parse_str_lit(p)) } else if cur.is_num() { - let t = p.bump(); - let (value, raw) = t.take_num(p.input_mut()); + let (value, raw) = p.input_mut().expect_number_token_and_bump(); PropName::Num(Number { span: p.span(start), value, raw: Some(raw), }) } else if cur.is_bigint() { - let t = p.bump(); - let (value, raw) = t.take_bigint(p.input_mut()); + let (value, raw) = p.input_mut().expect_bigint_token_and_bump(); PropName::BigInt(BigInt { span: p.span(start), value, raw: Some(raw), }) } else if cur.is_word() { - let t = p.bump(); - let w = t.take_word(p.input_mut()).unwrap(); + let w = p.input_mut().expect_word_token_and_bump(); PropName::Ident(IdentName::new(w, p.span(start))) } else if cur.is_lbracket() { p.bump(); @@ -469,10 +456,8 @@ pub trait Parser<'a>: Sized + Clone { #[inline] fn is_ident_ref(&mut self) -> bool { - let ctx = self.ctx(); - self.input_mut() - .cur() - .is_some_and(|cur| cur.is_word() && !cur.is_reserved(ctx)) + let cur = self.input().cur(); + cur.is_word() && !cur.is_reserved(self.ctx()) } #[inline] @@ -509,9 +494,10 @@ pub trait Parser<'a>: Sized + Clone { } pub fn parse_shebang<'a>(p: &mut impl Parser<'a>) -> PResult> { - Ok(if p.input_mut().cur().is_some_and(|t| t.is_shebang()) { - let t = p.bump(); - Some(t.take_shebang(p.input_mut())) + let cur = p.input().cur(); + Ok(if cur.is_shebang() { + let ret = p.input_mut().expect_shebang_token_and_bump(); + Some(ret) } else { None }) @@ -521,7 +507,7 @@ pub fn parse_shebang<'a>(p: &mut impl Parser<'a>) -> PResult> { #[inline(never)] pub fn eof_error<'a, P: Parser<'a>>(p: &mut P) -> crate::error::Error { debug_assert!( - p.input_mut().cur().is_none(), + p.input().cur().is_eof(), "Parser should not call throw_eof_error() without knowing current token" ); let pos = p.input().end_pos(); diff --git a/crates/swc_ecma_lexer/src/common/parser/module_item.rs b/crates/swc_ecma_lexer/src/common/parser/module_item.rs index a1ba62aeae64..277f32d3fb8d 100644 --- a/crates/swc_ecma_lexer/src/common/parser/module_item.rs +++ b/crates/swc_ecma_lexer/src/common/parser/module_item.rs @@ -67,15 +67,12 @@ fn parse_from_clause_and_semi<'a, P: Parser<'a>>( ) -> PResult<(Box, Option>)> { expect!(p, &P::Token::FROM); - let Some(cur) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; + let cur = p.input().cur(); let src = if cur.is_str() { Box::new(parse_str_lit(p)) } else { unexpected!(p, "a string literal") }; - p.input_mut().cur(); let with = if p.input().syntax().import_attributes() && !p.input_mut().had_line_break_before_cur() && (p.input_mut().eat(&P::Token::ASSERT) || p.input_mut().eat(&P::Token::WITH)) @@ -107,14 +104,11 @@ fn parse_named_export_specifier<'a, P: Parser<'a>>( // `export { type as }` // `export { type as as }` // `export { type as as as }` - if p.syntax().typescript() - && orig_ident.sym == "type" - && p.input_mut().cur().is_some_and(|cur| cur.is_word()) - { + if p.syntax().typescript() && orig_ident.sym == "type" && p.input().cur().is_word() { let possibly_orig = parse_ident_name(p).map(Ident::from)?; if possibly_orig.sym == "as" { // `export { type as }` - if !p.input_mut().cur().is_some_and(|cur| cur.is_word()) { + if !p.input().cur().is_word() { if type_only { p.emit_err(orig_ident.span, SyntaxError::TS2207); } @@ -129,7 +123,7 @@ fn parse_named_export_specifier<'a, P: Parser<'a>>( let maybe_as = parse_ident_name(p).map(Ident::from)?; if maybe_as.sym == "as" { - if p.input_mut().cur().is_some_and(|cur| cur.is_word()) { + if p.input().cur().is_word() { // `export { type as as as }` // `export { type as as foo }` let exported = parse_ident_name(p).map(Ident::from)?; @@ -222,14 +216,11 @@ fn parse_import_specifier<'a, P: Parser<'a>>( // `import { type as } from 'mod'` // `import { type as as } from 'mod'` // `import { type as as as } from 'mod'` - if p.syntax().typescript() - && orig_name.sym == "type" - && p.input_mut().cur().is_some_and(|cur| cur.is_word()) - { + if p.syntax().typescript() && orig_name.sym == "type" && p.input().cur().is_word() { let possibly_orig_name = parse_ident_name(p).map(Ident::from)?; if possibly_orig_name.sym == "as" { // `import { type as } from 'mod'` - if !p.input_mut().cur().is_some_and(|cur| cur.is_word()) { + if !p.input().cur().is_word() { if p.ctx().is_reserved_word(&possibly_orig_name.sym) { syntax_error!( p, @@ -252,7 +243,7 @@ fn parse_import_specifier<'a, P: Parser<'a>>( let maybe_as: Ident = parse_binding_ident(p, false)?.into(); if maybe_as.sym == "as" { - if p.input_mut().cur().is_some_and(|cur| cur.is_word()) { + if p.input().cur().is_word() { // `import { type as as as } from 'mod'` // `import { type as as foo } from 'mod'` let local: Ident = parse_binding_ident(p, false)?.into(); @@ -356,13 +347,12 @@ fn parse_export<'a, P: Parser<'a>>( let start = p.cur_pos(); p.assert_and_bump(&P::Token::EXPORT); - let Some(cur) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; + let cur = p.input().cur(); if cur.is_error() { - let c = p.input_mut().bump(); - let err = c.take_error(p.input_mut()); + let err = p.input_mut().expect_error_token_and_bump(); return Err(err); + } else if cur.is_eof() { + return Err(eof_error(p)); } let after_export_start = p.cur_pos(); @@ -382,7 +372,8 @@ fn parse_export<'a, P: Parser<'a>>( } if p.input().syntax().typescript() { - if let Some(cur) = p.input_mut().cur().filter(|cur| cur.is_word()) { + let cur = p.input().cur(); + if cur.is_word() { let sym = cur.clone().take_word(p.input()).unwrap(); // TODO: remove clone if let Some(decl) = try_parse_ts_export_decl(p, decorators.clone(), sym) { @@ -393,9 +384,7 @@ fn parse_export<'a, P: Parser<'a>>( .into()); } } - } - if p.input().syntax().typescript() { if p.input_mut().eat(&P::Token::IMPORT) { let is_type_only = p.input_mut().is(&P::Token::TYPE) && peek!(p).is_some_and(|p| p.is_word()); @@ -468,13 +457,12 @@ fn parse_export<'a, P: Parser<'a>>( { let class_start = p.cur_pos(); p.assert_and_bump(&P::Token::ABSTRACT); - let Some(cur) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; + let cur = p.input().cur(); if cur.is_error() { - let c = p.input_mut().bump(); - let err = c.take_error(p.input_mut()); + let err = p.input_mut().expect_error_token_and_bump(); return Err(err); + } else if cur.is_eof() { + return Err(eof_error(p)); } return parse_default_class(p, start, class_start, decorators, true) @@ -558,9 +546,6 @@ fn parse_export<'a, P: Parser<'a>>( { let enum_start = p.cur_pos(); p.assert_and_bump(&P::Token::CONST); - let Some(_) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; p.assert_and_bump(&P::Token::ENUM); return parse_ts_enum_decl(p, enum_start, /* is_const */ true) .map(Decl::from) @@ -596,9 +581,7 @@ fn parse_export<'a, P: Parser<'a>>( let default = match export_default { Some(default) => Some(default), None => { - if p.input().syntax().export_default_from() - && p.input_mut().cur().is_some_and(|cur| cur.is_word()) - { + if p.input().syntax().export_default_from() && p.input().cur().is_word() { Some(parse_ident(p, false, false)?) } else { None @@ -787,10 +770,8 @@ fn parse_import<'a, P: Parser<'a>>(p: &mut P) -> PResult { expect!(p, &P::Token::IMPORT); // Handle import 'mod.js' - if p.input_mut().cur().is_some_and(|cur| cur.is_str()) { + if p.input().cur().is_str() { let src = Box::new(parse_str_lit(p)); - debug_assert!(p.input_mut().get_cur().is_none()); - p.input_mut().cur(); let with = if p.input().syntax().import_attributes() && !p.input_mut().had_line_break_before_cur() && (p.input_mut().eat(&P::Token::ASSERT) || p.input_mut().eat(&P::Token::WITH)) @@ -823,10 +804,8 @@ fn parse_import<'a, P: Parser<'a>>(p: &mut P) -> PResult { let mut local = parse_imported_default_binding(p)?; if p.input().syntax().typescript() && local.sym == "type" { - if p.input_mut() - .cur() - .is_some_and(|cur| cur.is_lbrace() || cur.is_star()) - { + let cur = p.input().cur(); + if cur.is_lbrace() || cur.is_star() { type_only = true; break 'import_maybe_ident; } @@ -857,10 +836,8 @@ fn parse_import<'a, P: Parser<'a>>(p: &mut P) -> PResult { _ => unreachable!(), }; - if p.input_mut() - .cur() - .is_some_and(|cur| cur.is_lbrace() || cur.is_star()) - { + let cur = p.input().cur(); + if cur.is_lbrace() || cur.is_star() { phase = new_phase; break 'import_maybe_ident; } @@ -909,14 +886,13 @@ fn parse_import<'a, P: Parser<'a>>(p: &mut P) -> PResult { let src = { expect!(p, &P::Token::FROM); - if p.input_mut().cur().is_some_and(|cur| cur.is_str()) { + if p.input().cur().is_str() { Box::new(parse_str_lit(p)) } else { unexpected!(p, "a string literal") } }; - p.input_mut().cur(); let with = if p.input().syntax().import_attributes() && !p.input_mut().had_line_break_before_cur() && (p.input_mut().eat(&P::Token::ASSERT) || p.input_mut().eat(&P::Token::WITH)) diff --git a/crates/swc_ecma_lexer/src/common/parser/object.rs b/crates/swc_ecma_lexer/src/common/parser/object.rs index 39c102a67f13..9d9866183f59 100644 --- a/crates/swc_ecma_lexer/src/common/parser/object.rs +++ b/crates/swc_ecma_lexer/src/common/parser/object.rs @@ -198,19 +198,18 @@ fn parse_expr_object_prop<'a, P: Parser<'a>>(p: &mut P) -> PResult let key = p.parse_prop_name()?; + let cur = p.input().cur(); if p.input().syntax().typescript() - && !p.input_mut().cur().is_some_and(|cur| { - cur.is_lparen() - || cur.is_lbracket() - || cur.is_colon() - || cur.is_comma() - || cur.is_question() - || cur.is_equal() - || cur.is_star() - || cur.is_str() - || cur.is_num() - || cur.is_word() - }) + && !(cur.is_lparen() + || cur.is_lbracket() + || cur.is_colon() + || cur.is_comma() + || cur.is_question() + || cur.is_equal() + || cur.is_star() + || cur.is_str() + || cur.is_num() + || cur.is_word()) && !(p.input().syntax().typescript() && p.input_mut().is(&P::Token::LESS)) && !(p.input_mut().is(&P::Token::RBRACE) && matches!(key, PropName::Ident(..))) { @@ -272,10 +271,8 @@ fn parse_expr_object_prop<'a, P: Parser<'a>>(p: &mut P) -> PResult // `ident` from parse_prop_name is parsed as 'IdentifierName' // It means we should check for invalid expressions like { for, } - if p.input_mut() - .cur() - .is_some_and(|cur| cur.is_equal() || cur.is_comma() || cur.is_rbrace()) - { + let cur = p.input().cur(); + if cur.is_equal() || cur.is_comma() || cur.is_rbrace() { if p.ctx().is_reserved_word(&ident.sym) { p.emit_err(ident.span, SyntaxError::ReservedWordInObjShorthandOrPat); } diff --git a/crates/swc_ecma_lexer/src/common/parser/pat.rs b/crates/swc_ecma_lexer/src/common/parser/pat.rs index 59d31c44f011..42423ed00658 100644 --- a/crates/swc_ecma_lexer/src/common/parser/pat.rs +++ b/crates/swc_ecma_lexer/src/common/parser/pat.rs @@ -15,7 +15,7 @@ use crate::{ context::Context, lexer::token::TokenFactory, parser::{ - buffer::Buffer, eof_error, expr::parse_assignment_expr, expr_ext::ExprExt, + buffer::Buffer, expr::parse_assignment_expr, expr_ext::ExprExt, ident::parse_binding_ident, object::parse_object_pat, }, }, @@ -392,9 +392,7 @@ pub fn parse_binding_pat_or_ident<'a, P: Parser<'a>>( ) -> PResult { trace_cur!(p, parse_binding_pat_or_ident); - let Some(cur) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; + let cur = p.input().cur(); if cur.is_word() { parse_binding_ident(p, disallow_let).map(Pat::from) } else if cur.is_lbracket() { @@ -402,8 +400,7 @@ pub fn parse_binding_pat_or_ident<'a, P: Parser<'a>>( } else if cur.is_lbrace() { parse_object_pat(p) } else if cur.is_error() { - let c = p.input_mut().bump(); - let err = c.take_error(p.input_mut()); + let err = p.input_mut().expect_error_token_and_bump(); Err(err) } else { unexpected!(p, "yield, an identifier, [ or {") diff --git a/crates/swc_ecma_lexer/src/common/parser/stmt.rs b/crates/swc_ecma_lexer/src/common/parser/stmt.rs index 27cd24726e56..a1ed5249733b 100644 --- a/crates/swc_ecma_lexer/src/common/parser/stmt.rs +++ b/crates/swc_ecma_lexer/src/common/parser/stmt.rs @@ -70,7 +70,8 @@ fn parse_normal_for_head<'a, P: Parser<'a>>( } fn parse_for_each_head<'a, P: Parser<'a>>(p: &mut P, left: ForHead) -> PResult { - let is_of = p.bump().is_of(); + let is_of = p.input().cur().is_of(); + p.bump(); if is_of { let right = p.allow_in_expr(parse_assignment_expr)?; Ok(TempForHead::ForOf { left, right }) @@ -151,12 +152,8 @@ fn parse_var_declarator<'a, P: Parser<'a>>( } //FIXME: This is wrong. Should check in/of only on first loop. - let init = if !for_loop - || !p - .input_mut() - .cur() - .is_some_and(|cur| cur.is_in() || cur.is_of()) - { + let cur = p.input().cur(); + let init = if !for_loop || !(cur.is_in() || cur.is_of()) { if p.input_mut().eat(&P::Token::EQUAL) { let expr = parse_assignment_expr(p)?; let expr = p.verify_expr(expr)?; @@ -201,7 +198,7 @@ fn parse_var_declarator<'a, P: Parser<'a>>( pub fn parse_var_stmt<'a, P: Parser<'a>>(p: &mut P, for_loop: bool) -> PResult> { let start = p.cur_pos(); - let t = p.bump(); + let t = p.input().cur(); let kind = if t.is_const() { VarDeclKind::Const } else if t.is_let() { @@ -211,15 +208,13 @@ pub fn parse_var_stmt<'a, P: Parser<'a>>(p: &mut P, for_loop: bool) -> PResult>(p: &mut P, for_loop: bool) -> PResult>( // reader = init() // is two statements - p.input_mut().cur(); if p.input_mut().has_linebreak_between_cur_and_peeked() { return Ok(None); } @@ -380,17 +374,15 @@ pub fn parse_using_decl<'a, P: Parser<'a>>( pub fn parse_for_head<'a, P: Parser<'a>>(p: &mut P) -> PResult { // let strict = p.ctx().contains(Context::Strict); - if p.input_mut() - .cur() - .is_some_and(|cur| cur.is_const() || cur.is_var()) + let cur = p.input().cur(); + if cur.is_const() + || cur.is_var() || (p.input_mut().is(&P::Token::LET) && peek!(p).map_or(false, |v| v.follows_keyword_let())) { let decl = parse_var_stmt(p, true)?; - if p.input_mut() - .cur() - .is_some_and(|cur| cur.is_of() || cur.is_in()) - { + let cur = p.input().cur(); + if cur.is_of() || cur.is_in() { if decl.decls.len() != 1 { for d in decl.decls.iter().skip(1) { p.emit_err(d.name.span(), SyntaxError::TooManyVarInForInHead); @@ -479,24 +471,20 @@ pub fn parse_for_head<'a, P: Parser<'a>>(p: &mut P) -> PResult { decls: vec![decl], }); - let Some(cur) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; - + let cur = p.input().cur(); if cur.is_error() { - let c = p.input_mut().bump(); - let err = c.take_error(p.input_mut()); + let err = p.input_mut().expect_error_token_and_bump(); return Err(err); + } else if cur.is_eof() { + return Err(eof_error(p)); } return parse_for_each_head(p, ForHead::UsingDecl(pat)); } // for (a of b) - if p.input_mut() - .cur() - .is_some_and(|cur| cur.is_of() || cur.is_in()) - { + let cur = p.input().cur(); + if cur.is_of() || cur.is_in() { let is_in = p.input_mut().is(&P::Token::IN); let pat = reparse_expr_as_pat(p, PatType::AssignPat, init)?; @@ -967,11 +955,10 @@ fn parse_switch_stmt<'a, P: Parser<'a>>(p: &mut P) -> PResult { expect!(p, &P::Token::LBRACE); p.do_inside_of_context(Context::IsBreakAllowed, |p| { - while p - .input_mut() - .cur() - .is_some_and(|cur| cur.is_case() || cur.is_default()) - { + while { + let cur = p.input().cur(); + cur.is_case() || cur.is_default() + } { let mut cons = Vec::new(); let is_case = p.input_mut().is(&P::Token::CASE); let case_start = p.cur_pos(); @@ -988,12 +975,10 @@ fn parse_switch_stmt<'a, P: Parser<'a>>(p: &mut P) -> PResult { }; expect!(p, &P::Token::COLON); - while !eof!(p) - && !p - .input_mut() - .cur() - .is_some_and(|cur| cur.is_case() || cur.is_default() || cur.is_rbrace()) - { + while !eof!(p) && { + let cur = p.input().cur(); + !(cur.is_case() || cur.is_default() || cur.is_rbrace()) + } { cons.push(p.do_outside_of_context(Context::TopLevel, parse_stmt_list_item)?); } @@ -1035,20 +1020,14 @@ pub fn parse_stmt_like<'a, P: Parser<'a>, Type: IsDirective + From>( debug_tracing!(p, "parse_stmt_like"); let start = p.cur_pos(); - let decorators = if p - .input() - .get_cur() - .is_some_and(|cur| cur.token() == &P::Token::AT) - { + let decorators = if p.input().get_cur().token() == &P::Token::AT { parse_decorators(p, true)? } else { vec![] }; - if p.input_mut() - .cur() - .is_some_and(|cur| cur.is_import() || cur.is_export()) - { + let cur = p.input().cur(); + if cur.is_import() || cur.is_export() { return handle_import_export(p, decorators); } @@ -1113,9 +1092,7 @@ fn parse_stmt_internal<'a, P: Parser<'a>>( let top_level = p.ctx().contains(Context::TopLevel); - let Some(cur) = p.input_mut().cur().cloned() else { - return Err(eof_error(p)); - }; + let cur = p.input().cur().clone(); if cur.is_await() && (include_decl || top_level) { if top_level { @@ -1274,17 +1251,17 @@ fn parse_stmt_internal<'a, P: Parser<'a>>( return p .do_inside_of_context(Context::AllowUsingDecl, |p| parse_block(p, false)) .map(Stmt::Block); - } else if cur.is_error() { - let c = p.input_mut().bump(); - let err = c.take_error(p.input_mut()); - return Err(err); - } - - if p.input_mut().eat(&P::Token::SEMI) { + } else if cur.is_semi() { + p.bump(); return Ok(EmptyStmt { span: p.span(start), } .into()); + } else if cur.is_error() { + let err = p.input_mut().expect_error_token_and_bump(); + return Err(err); + } else if cur.is_eof() { + return Err(eof_error(p)); } // Handle async function foo() {} @@ -1357,9 +1334,7 @@ fn parse_stmt_internal<'a, P: Parser<'a>>( } .into()) } else { - let Some(cur) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; + let cur = p.input().cur(); if cur.is_bin_op() { p.emit_err(p.input().cur_span(), SyntaxError::TS1005); let expr = parse_bin_op_recursively(p, expr, 0)?; @@ -1395,37 +1370,34 @@ pub(super) fn parse_block_body<'a, P: Parser<'a>, Type: IsDirective + From let mut stmts = Vec::with_capacity(8); - let is_stmt_start = |p: &mut P| match (p.input_mut().cur(), end) { - (Some(cur), Some(end)) => cur != end, - (Some(_), None) => true, - (None, None) => false, - (None, Some(_)) => { - let eof_text = p.input_mut().dump_cur(); - p.emit_err( - p.input().cur_span(), - SyntaxError::Expected(format!("{:?}", end.unwrap()), eof_text), - ); - false - } - }; - - let mut has_strict_directive = false; - if is_stmt_start(p) { - let stmt = parse_stmt_like(p, true, &handle_import_export)?; - if allow_directives && stmt.is_use_strict() { - has_strict_directive = true; - if p.input().knows_cur() && !p.is_general_semi() { - unreachable!( - "'use strict'; directive requires parser.input.cur to be empty or '}}', but \ - current token was: {:?}", - p.input_mut().cur() - ) + let has_strict_directive = allow_directives + && (p + .input() + .cur() + .is_str_raw_content("\"use strict\"", p.input()) + || p.input() + .cur() + .is_str_raw_content("'use strict'", p.input())); + + let parse_stmts = |p: &mut P, stmts: &mut Vec| -> PResult<()> { + let is_stmt_start = |p: &mut P| { + let cur = p.input().cur(); + match end { + Some(end) => { + if cur.is_eof() { + let eof_text = p.input_mut().dump_cur(); + p.emit_err( + p.input().cur_span(), + SyntaxError::Expected(format!("{end:?}"), eof_text), + ); + false + } else { + cur != end + } + } + None => !cur.is_eof(), } - } - stmts.push(stmt); - } - - let parse_rest_stmts = |p: &mut P, stmts: &mut Vec| -> PResult<()> { + }; while is_stmt_start(p) { let stmt = parse_stmt_like(p, true, &handle_import_export)?; stmts.push(stmt); @@ -1434,12 +1406,12 @@ pub(super) fn parse_block_body<'a, P: Parser<'a>, Type: IsDirective + From }; if has_strict_directive { - p.do_inside_of_context(Context::Strict, |p| parse_rest_stmts(p, &mut stmts))?; + p.do_inside_of_context(Context::Strict, |p| parse_stmts(p, &mut stmts))?; } else { - parse_rest_stmts(p, &mut stmts)?; + parse_stmts(p, &mut stmts)?; }; - if p.input_mut().cur().is_some() && end.is_some() { + if !p.input().cur().is_eof() && end.is_some() { p.bump(); } diff --git a/crates/swc_ecma_lexer/src/common/parser/typescript.rs b/crates/swc_ecma_lexer/src/common/parser/typescript.rs index a61b83d19c34..8f419dd93f6b 100644 --- a/crates/swc_ecma_lexer/src/common/parser/typescript.rs +++ b/crates/swc_ecma_lexer/src/common/parser/typescript.rs @@ -127,10 +127,8 @@ where if kind == ParsingContext::EnumMembers { let expect = P::Token::COMMA; - let cur = match p.input_mut().cur() { - Some(tok) => tok.clone().to_string(p.input()), - None => "EOF".to_string(), - }; + let cur = p.input().cur(); + let cur = cur.clone().to_string(p.input()); p.emit_err( p.input().cur_span(), SyntaxError::Expected(format!("{expect:?}"), cur), @@ -163,9 +161,7 @@ where /// `tsIsListTerminator` fn is_ts_list_terminator<'a>(p: &mut impl Parser<'a>, kind: ParsingContext) -> PResult { debug_assert!(p.input().syntax().typescript()); - let Some(cur) = p.input_mut().cur() else { - return Ok(false); - }; + let cur = p.input().cur(); Ok(match kind { ParsingContext::EnumMembers | ParsingContext::TypeMembers => cur.is_rbrace(), ParsingContext::HeritageClauseElement => { @@ -185,18 +181,17 @@ pub(super) fn ts_next_token_can_follow_modifier<'a>(p: &mut impl Parser<'a>) -> // itself. And "static". TODO: Would be nice to avoid lookahead. Want a // hasLineBreakUpNext() method... p.bump(); - Ok(!p.input_mut().had_line_break_before_cur() - && p.input_mut().cur().is_some_and(|cur| { - cur.is_lbracket() - || cur.is_lbrace() - || cur.is_star() - || cur.is_dotdotdot() - || cur.is_hash() - || cur.is_word() - || cur.is_str() - || cur.is_num() - || cur.is_bigint() - })) + + let cur = p.input().cur(); + Ok(!p.input().had_line_break_before_cur() && cur.is_lbracket() + || cur.is_lbrace() + || cur.is_star() + || cur.is_dotdotdot() + || cur.is_hash() + || cur.is_word() + || cur.is_str() + || cur.is_num() + || cur.is_bigint()) } /// `tsTryParse` @@ -251,9 +246,7 @@ fn is_ts_start_of_construct_signature<'a, P: Parser<'a>>(p: &mut P) -> PResult(p: &mut impl Parser<'a>) -> PResult { if p.syntax().typescript() && { - let Some(cur) = p.input_mut().cur() else { + let cur = p.input().cur(); + if cur.is_eof() { return Err(eof_error(p)); - }; + } cur.is_public() || cur.is_protected() || cur.is_private() || cur.is_readonly() } && peek!(p).is_some_and(|t| t.is_word() || t.is_lbrace() || t.is_lbracket()) @@ -347,9 +341,7 @@ pub fn parse_ts_modifier<'a, P: Parser<'a>>( return Ok(None); } let pos = { - let Some(cur) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; + let cur = p.input().cur(); let modifier = if cur.is_unknown_ident() { cur.clone().take_unknown_ident_ref(p.input()).clone() } else if cur.is_known_ident() { @@ -359,9 +351,10 @@ pub fn parse_ts_modifier<'a, P: Parser<'a>>( } else if cur.is_const() { atom!("const") } else if cur.is_error() { - let c = p.input_mut().bump(); - let err = c.take_error(p.input_mut()); + let err = p.input_mut().expect_error_token_and_bump(); return Err(err); + } else if cur.is_eof() { + return Err(eof_error(p)); } else { return Ok(None); }; @@ -437,14 +430,8 @@ pub fn parse_ts_entity_name<'a, P: Parser<'a>>( let mut entity = TsEntityName::Ident(init.into()); while p.input_mut().eat(&P::Token::DOT) { let dot_start = p.input_mut().cur_pos(); - let Some(cur) = p.input_mut().cur() else { - p.emit_err( - Span::new_with_checked(dot_start, dot_start), - SyntaxError::TS1003, - ); - return Ok(entity); - }; - if !cur.is_hash() && !cur.is_word() { + let cur = p.input().cur(); + if cur.is_eof() || (!cur.is_hash() && !cur.is_word()) { p.emit_err( Span::new_with_checked(dot_start, dot_start), SyntaxError::TS1003, @@ -499,11 +486,9 @@ pub fn parse_ts_type_args<'a, P: Parser<'a>>(p: &mut P) -> PResult />`, so set exprAllowed = false p.input_mut().set_expr_allowed(false); - expect!(p, &P::Token::GREATER); - Ok(Box::new(TsTypeParamInstantiation { - span: p.span(start), - params, - })) + p.expect_without_advance(&P::Token::GREATER)?; + let span = Span::new_with_checked(start, p.input_mut().cur_span().hi); + Ok(Box::new(TsTypeParamInstantiation { span, params })) } /// `tsParseTypeReference` @@ -517,12 +502,15 @@ pub fn parse_ts_type_ref<'a, P: Parser<'a>>(p: &mut P) -> PResult { let type_name = parse_ts_entity_name(p, /* allow_reserved_words */ true)?; trace_cur!(p, parse_ts_type_ref__type_args); - let type_params = - if !p.input_mut().had_line_break_before_cur() && p.input_mut().is(&P::Token::LESS) { - Some(p.do_outside_of_context(Context::ShouldNotLexLtOrGtAsType, parse_ts_type_args)?) - } else { - None - }; + let type_params = if !p.input_mut().had_line_break_before_cur() + && p.input_mut().is(&P::Token::LESS) + { + let ret = p.do_outside_of_context(Context::ShouldNotLexLtOrGtAsType, parse_ts_type_args)?; + p.assert_and_bump(&P::Token::GREATER); + Some(ret) + } else { + None + }; if has_modifier { p.emit_err(p.span(start), SyntaxError::TS2369); @@ -620,7 +608,7 @@ fn expect_then_parse_ts_type<'a, P: Parser<'a>>( p.in_type(|p| { if !p.input_mut().eat(token) { - let got = format!("{:?}", p.input_mut().cur()); + let got = format!("{:?}", p.input().cur()); syntax_error!( p, p.input().cur_span(), @@ -738,11 +726,8 @@ pub fn parse_ts_type_params<'a, P: Parser<'a>>( ) -> PResult> { p.in_type(|p| { p.ts_in_no_context(|p| { - let start = p.input_mut().cur_pos(); - - let Some(cur) = p.input_mut().cur() else { - unexpected!(p, "< (jsx tag start)") - }; + let start = p.input().cur_pos(); + let cur = p.input().cur(); if !cur.is_less() && !cur.is_jsx_tag_start() { unexpected!(p, "< (jsx tag start)") } @@ -775,7 +760,7 @@ pub fn try_parse_ts_type_params<'a, P: Parser<'a>>( return Ok(None); } - if p.input_mut().cur().is_some_and(|cur| cur.is_less()) { + if p.input().cur().is_less() { return parse_ts_type_params(p, permit_in_out, permit_const).map(Some); } @@ -792,7 +777,7 @@ pub fn parse_ts_type_or_type_predicate_ann<'a, P: Parser<'a>>( p.in_type(|p| { let return_token_start = p.input_mut().cur_pos(); if !p.input_mut().eat(return_token) { - let cur = format!("{:?}", p.input_mut().cur()); + let cur = format!("{:?}", p.input().cur()); let span = p.input_mut().cur_span(); syntax_error!( p, @@ -802,7 +787,7 @@ pub fn parse_ts_type_or_type_predicate_ann<'a, P: Parser<'a>>( } let type_pred_start = p.input_mut().cur_pos(); - let has_type_pred_asserts = p.input_mut().cur().is_some_and(|cur| cur.is_asserts()) && { + let has_type_pred_asserts = p.input().cur().is_asserts() && { let ctx = p.ctx(); peek!(p).is_some_and(|peek| { if peek.is_word() { @@ -815,7 +800,6 @@ pub fn parse_ts_type_or_type_predicate_ann<'a, P: Parser<'a>>( if has_type_pred_asserts { p.assert_and_bump(&P::Token::ASSERTS); - debug_assert!(p.input_mut().cur().is_some()); } let has_type_pred_is = p.is_ident_ref() @@ -859,9 +843,7 @@ pub fn parse_ts_type_or_type_predicate_ann<'a, P: Parser<'a>>( fn is_start_of_expr<'a>(p: &mut impl Parser<'a>) -> bool { is_start_of_left_hand_side_expr(p) || { - let Some(cur) = p.input_mut().cur() else { - return false; - }; + let cur = p.input().cur(); cur.is_plus() || cur.is_minus() || cur.is_tilde() @@ -890,19 +872,20 @@ pub(super) fn try_parse_ts_type_args<'a, P: Parser<'a>>( try_parse_ts(p, |p| { let type_args = parse_ts_type_args(p)?; + p.assert_and_bump(&P::Token::GREATER); let cur = p.input_mut().cur(); - if cur.is_some_and(|cur| { - cur.is_less() // invalid syntax + if cur.is_less() // invalid syntax || cur.is_greater() || cur.is_equal() || cur.is_rshift() || cur.is_greater_eq() || cur.is_plus() || cur.is_minus() // becomes relational expression - || cur.is_lparen() || cur.is_no_substitution_template_literal() || cur.is_template_head() || cur.is_backquote() // these should be type - // arguments in function - // call or template, not - // instantiation - // expression - }) { + || cur.is_lparen() || cur.is_no_substitution_template_literal() || cur.is_template_head() || cur.is_backquote() + // these should be type + // arguments in function + // call or template, not + // instantiation + // expression + { Ok(None) } else if p.input_mut().had_line_break_before_cur() - || p.input_mut().cur().is_some_and(|t| t.is_bin_op()) + || p.input().cur().is_bin_op() || !is_start_of_expr(p) { Ok(Some(type_args)) @@ -948,11 +931,10 @@ pub(super) fn next_then_parse_ts_type<'a, P: Parser<'a>>(p: &mut P) -> PResult>(p: &mut P) -> PResult { let start = p.cur_pos(); // Computed property names are grammar errors in an enum, so accept just string // literal or identifier. - let Some(cur) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; + let cur = p.input().cur(); let id = if cur.is_str() { TsEnumMemberId::Str(parse_str_lit(p)) } else if cur.is_num() { - let cur = p.bump(); - let (value, raw) = cur.take_num(p.input_mut()); + let (value, raw) = p.input_mut().expect_number_token_and_bump(); let mut new_raw = String::new(); new_raw.push('"'); @@ -997,9 +976,10 @@ fn parse_ts_enum_member<'a, P: Parser<'a>>(p: &mut P) -> PResult { p.assert_and_bump(&P::Token::RBRACKET); TsEnumMemberId::Ident(Ident::new_no_ctxt(atom!(""), p.span(start))) } else if cur.is_error() { - let c = p.input_mut().bump(); - let err = c.take_error(p.input_mut()); + let err = p.input_mut().expect_error_token_and_bump(); return Err(err); + } else if cur.is_eof() { + return Err(eof_error(p)); } else { parse_ident_name(p) .map(Ident::from) @@ -1008,11 +988,7 @@ fn parse_ts_enum_member<'a, P: Parser<'a>>(p: &mut P) -> PResult { let init = if p.input_mut().eat(&P::Token::EQUAL) { Some(parse_assignment_expr(p)?) - } else if p - .input_mut() - .cur() - .is_some_and(|cur| cur.is_comma() || cur.is_rbrace()) - { + } else if p.input().cur().is_comma() || p.input().cur().is_rbrace() { None } else { let start = p.cur_pos(); @@ -1174,7 +1150,9 @@ fn parse_ts_heritage_clause_element<'a, P: Parser<'a>>(p: &mut P) -> PResult { let type_args = if p.input_mut().is(&P::Token::LESS) { - Some(parse_ts_type_args(p)?) + let ret = parse_ts_type_args(p)?; + p.assert_and_bump(&P::Token::GREATER); + Some(ret) } else { None }; @@ -1194,23 +1172,16 @@ fn skip_ts_parameter_start<'a, P: Parser<'a>>(p: &mut P) -> PResult { let _ = eat_any_ts_modifier(p)?; - if p.input_mut() - .cur() - .is_some_and(|cur| cur.is_word() || cur.is_this()) - { + let cur = p.input().cur(); + if cur.is_word() || cur.is_this() { p.bump(); - return Ok(true); - } - - if p.input_mut() - .cur() - .is_some_and(|cur| cur.is_lbrace() || cur.is_lbracket()) - && parse_binding_pat_or_ident(p, false).is_ok() + Ok(true) + } else if (cur.is_lbrace() || cur.is_lbracket()) && parse_binding_pat_or_ident(p, false).is_ok() { - return Ok(true); + Ok(true) + } else { + Ok(false) } - - Ok(false) } /// `tsIsUnambiguouslyStartOfFunctionType` @@ -1219,27 +1190,22 @@ fn is_ts_unambiguously_start_of_fn_type<'a, P: Parser<'a>>(p: &mut P) -> PResult p.assert_and_bump(&P::Token::LPAREN); - if p.input_mut() - .cur() - .is_some_and(|cur| cur.is_rparen() || cur.is_dotdotdot()) - { + let cur = p.input().cur(); + if cur.is_rparen() || cur.is_dotdotdot() { // ( ) // ( ... return Ok(true); } if skip_ts_parameter_start(p)? { - if p.input_mut().cur().is_some_and(|cur| { - cur.is_colon() || cur.is_comma() || cur.is_equal() || cur.is_question() - }) { + let cur = p.input().cur(); + if cur.is_colon() || cur.is_comma() || cur.is_equal() || cur.is_question() { // ( xxx : // ( xxx , // ( xxx ? // ( xxx = return Ok(true); } - if p.input_mut().eat(&P::Token::RPAREN) - && p.input_mut().cur().is_some_and(|cur| cur.is_arrow()) - { + if p.input_mut().eat(&P::Token::RPAREN) && p.input().cur().is_arrow() { // ( xxx ) => return Ok(true); } @@ -1250,12 +1216,11 @@ fn is_ts_unambiguously_start_of_fn_type<'a, P: Parser<'a>>(p: &mut P) -> PResult fn is_ts_start_of_fn_type<'a, P: Parser<'a>>(p: &mut P) -> PResult { debug_assert!(p.input().syntax().typescript()); - if p.input_mut().cur().is_some_and(|cur| cur.is_less()) { + if p.input().cur().is_less() { return Ok(true); } - Ok(p.input_mut().cur().is_some_and(|cur| cur.is_lparen()) - && ts_look_ahead(p, is_ts_unambiguously_start_of_fn_type)?) + Ok(p.input().cur().is_lparen() && ts_look_ahead(p, is_ts_unambiguously_start_of_fn_type)?) } /// `tsIsUnambiguouslyIndexSignature` @@ -1266,10 +1231,10 @@ fn is_ts_unambiguously_index_signature<'a, P: Parser<'a>>(p: &mut P) -> PResult< p.assert_and_bump(&P::Token::LBRACKET); // Skip '[' // ',' is for error recovery - Ok(p.eat_ident_ref() - && p.input_mut() - .cur() - .is_some_and(|cur| cur.is_comma() || cur.is_colon())) + Ok(p.eat_ident_ref() && { + let cur = p.input().cur(); + cur.is_comma() || cur.is_colon() + }) } /// `tsTryParseIndexSignature` @@ -1283,9 +1248,7 @@ pub fn try_parse_ts_index_signature<'a, P: Parser<'a>>( return Ok(Default::default()); } - if !(p.input_mut().cur().is_some_and(|cur| cur.is_lbracket()) - && ts_look_ahead(p, is_ts_unambiguously_index_signature)?) - { + if !(p.input().cur().is_lbracket() && ts_look_ahead(p, is_ts_unambiguously_index_signature)?) { return Ok(None); } @@ -1347,13 +1310,9 @@ fn parse_ts_external_module_ref<'a, P: Parser<'a>>(p: &mut P) -> PResult>(p: &mut P) -> PResult>(p: &mut P) -> PResult>(p: &mut P) -> PResult<(bool, Box { @@ -2003,10 +1957,8 @@ fn parse_ts_property_or_method_signature<'a, P: Parser<'a>>( let optional = p.input_mut().eat(&P::Token::QUESTION); - if p.input_mut() - .cur() - .is_some_and(|cur| cur.is_lparen() || cur.is_less()) - { + let cur = p.input().cur(); + if cur.is_lparen() || cur.is_less() { if readonly { syntax_error!(p, SyntaxError::ReadOnlyMethod) } @@ -2056,10 +2008,8 @@ fn parse_ts_type_member<'a, P: Parser<'a>>(p: &mut P) -> PResult Either::Right(e) => e.into(), } } - if p.input_mut() - .cur() - .is_some_and(|cur| cur.is_lparen() || cur.is_less()) - { + let cur = p.input().cur(); + if cur.is_lparen() || cur.is_less() { return parse_ts_signature_member(p, SignatureParsingMode::TSCallSignatureDeclaration) .map(into_type_elem); } @@ -2236,15 +2186,12 @@ fn parse_ts_import_type<'a, P: Parser<'a>>(p: &mut P) -> PResult { expect!(p, &P::Token::LPAREN); - let Some(cur) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; + let cur = p.input().cur(); let arg = if cur.is_str() { parse_str_lit(p) } else if cur.is_error() { - let c = p.input_mut().bump(); - let err = c.take_error(p.input_mut()); + let err = p.input_mut().expect_error_token_and_bump(); return Err(err); } else { let arg_span = p.input().cur_span(); @@ -2277,8 +2224,9 @@ fn parse_ts_import_type<'a, P: Parser<'a>>(p: &mut P) -> PResult { }; let type_args = if p.input_mut().is(&P::Token::LESS) { - p.do_outside_of_context(Context::ShouldNotLexLtOrGtAsType, parse_ts_type_args) - .map(Some)? + let ret = p.do_outside_of_context(Context::ShouldNotLexLtOrGtAsType, parse_ts_type_args)?; + p.assert_and_bump(&P::Token::GREATER); + Some(ret) } else { None }; @@ -2328,12 +2276,15 @@ fn parse_ts_type_query<'a, P: Parser<'a>>(p: &mut P) -> PResult { .map(From::from)? }; - let type_args = - if !p.input_mut().had_line_break_before_cur() && p.input_mut().is(&P::Token::LESS) { - Some(p.do_outside_of_context(Context::ShouldNotLexLtOrGtAsType, parse_ts_type_args)?) - } else { - None - }; + let type_args = if !p.input_mut().had_line_break_before_cur() + && p.input_mut().is(&P::Token::LESS) + { + let ret = p.do_outside_of_context(Context::ShouldNotLexLtOrGtAsType, parse_ts_type_args)?; + p.assert_and_bump(&P::Token::GREATER); + Some(ret) + } else { + None + }; Ok(TsTypeQuery { span: p.span(start), @@ -2409,7 +2360,7 @@ fn parse_ts_ambient_external_module_decl<'a, P: Parser<'a>>( let (global, id) = if p.input_mut().is(&P::Token::GLOBAL) { let id = parse_ident_name(p)?; (true, TsModuleName::Ident(id.into())) - } else if p.input_mut().cur().is_some_and(|cur| cur.is_str()) { + } else if p.input().cur().is_str() { let id = TsModuleName::Str(parse_str_lit(p)); (false, id) } else { @@ -2443,9 +2394,7 @@ fn parse_ts_non_array_type<'a, P: Parser<'a>>(p: &mut P) -> PResult> let start = p.cur_pos(); - let Some(cur) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; + let cur = p.input().cur(); if cur.is_known_ident() || cur.is_unknown_ident() || cur.is_void() @@ -2520,9 +2469,7 @@ fn parse_ts_non_array_type<'a, P: Parser<'a>>(p: &mut P) -> PResult> p.bump(); - let Some(cur) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; + let cur = p.input().cur(); if !(cur.is_num() || cur.is_bigint()) { unexpected!(p, "numeric literal or bigint literal") } @@ -2722,9 +2669,6 @@ pub fn try_parse_ts_declare<'a, P: Parser<'a>>( if p.input_mut().is(&P::Token::CONST) && peek!(p).is_some_and(|peek| peek.is_enum()) { p.assert_and_bump(&P::Token::CONST); - let Some(_) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; p.assert_and_bump(&P::Token::ENUM); return parse_ts_enum_decl(p, start, /* is_const */ true) @@ -2740,10 +2684,9 @@ pub fn try_parse_ts_declare<'a, P: Parser<'a>>( .map(From::from) .map(Some); } - if p.input_mut() - .cur() - .is_some_and(|cur| cur.is_const() || cur.is_var() || cur.is_let()) - { + + let cur = p.input().cur(); + if cur.is_const() || cur.is_var() || cur.is_let() { return parse_var_stmt(p, false) .map(|decl| VarDecl { declare: true, @@ -2763,8 +2706,13 @@ pub fn try_parse_ts_declare<'a, P: Parser<'a>>( .map(Decl::from) .map(make_decl_declare) .map(Some); - } else if let Some(cur) = p.input_mut().cur().filter(|cur| cur.is_word()) { - let value = cur.clone().take_word(p.input_mut()).unwrap(); + } else if p.input().cur().is_word() { + let value = p + .input_mut() + .cur() + .clone() + .take_word(p.input_mut()) + .unwrap(); return parse_ts_decl(p, start, decorators, value, /* next */ true) .map(|v| v.map(make_decl_declare)); } @@ -2847,17 +2795,16 @@ fn parse_ts_decl<'a, P: Parser<'a>>( p.bump(); } - let Some(cur) = p.input_mut().cur() else { - return Err(eof_error(p)); - }; + let cur = p.input().cur(); if cur.is_str() { return parse_ts_ambient_external_module_decl(p, start) .map(From::from) .map(Some); } else if cur.is_error() { - let c = p.input_mut().bump(); - let err = c.take_error(p.input_mut()); + let err = p.input_mut().expect_error_token_and_bump(); return Err(err); + } else if cur.is_eof() { + return Err(eof_error(p)); } else if next || p.is_ident_ref() { return parse_ts_module_or_ns_decl(p, start, false) .map(From::from) @@ -2900,11 +2847,8 @@ pub fn try_parse_ts_generic_async_arrow_fn<'a, P: Parser<'a>>( return Ok(Default::default()); } - let res = if p - .input_mut() - .cur() - .is_some_and(|cur| cur.is_less() || cur.is_jsx_tag_start()) - { + let cur = p.input().cur(); + let res = if cur.is_less() || cur.is_jsx_tag_start() { try_parse_ts(p, |p| { let type_params = parse_ts_type_params(p, false, false)?; // Don't use overloaded parseFunctionParams which would look for "<" again. diff --git a/crates/swc_ecma_lexer/src/input.rs b/crates/swc_ecma_lexer/src/input.rs index 963d7ab331cf..63366f480075 100644 --- a/crates/swc_ecma_lexer/src/input.rs +++ b/crates/swc_ecma_lexer/src/input.rs @@ -7,6 +7,8 @@ use swc_ecma_ast::EsVersion; use crate::{ common::{ input::Tokens, + lexer::token::TokenFactory, + parser::token_and_span::TokenAndSpan as TokenAndSpanTrait, syntax::{Syntax, SyntaxFlags}, }, error::Error, @@ -304,11 +306,26 @@ pub struct Buffer> { pub iter: I, /// Span of the previous token. pub prev_span: Span, - pub cur: Option, + pub cur: TokenAndSpan, /// Peeked token pub next: Option, } +impl> Buffer { + fn bump(&mut self) -> Token { + let next = if let Some(next) = self.next.take() { + next + } else if let Some(next) = self.iter.next() { + next + } else { + TokenAndSpan::new(Token::Eof, self.prev_span, true) + }; + let prev = mem::replace(&mut self.cur, next); + self.prev_span = prev.span(); + prev.token + } +} + impl<'a, I: Tokens> crate::common::parser::buffer::Buffer<'a> for Buffer { type I = I; type Lexer = super::lexer::Lexer<'a>; @@ -318,17 +335,18 @@ impl<'a, I: Tokens> crate::common::parser::buffer::Buffer<'a> for fn new(lexer: I) -> Self { let start_pos = lexer.start_pos(); + let prev_span = Span::new_with_checked(start_pos, start_pos); Buffer { iter: lexer, - cur: None, - prev_span: Span::new_with_checked(start_pos, start_pos), + cur: TokenAndSpan::new(Token::Eof, prev_span, false), + prev_span, next: None, } } #[inline(always)] fn set_cur(&mut self, token: TokenAndSpan) { - self.cur = Some(token); + self.cur = token; } #[inline(always)] @@ -336,15 +354,6 @@ impl<'a, I: Tokens> crate::common::parser::buffer::Buffer<'a> for self.next.as_ref() } - #[cold] - #[inline(never)] - fn dump_cur(&mut self) -> String { - match self.cur() { - Some(v) => format!("{v:?}"), - None => "".to_string(), - } - } - #[inline(always)] fn set_next(&mut self, token: Option) { self.next = token; @@ -356,16 +365,8 @@ impl<'a, I: Tokens> crate::common::parser::buffer::Buffer<'a> for } #[inline] - fn cur(&mut self) -> Option<&Token> { - if self.cur.is_none() { - // If we have peeked a token, take it instead of calling lexer.next() - self.cur = self.next.take().or_else(|| self.iter.next()); - } - - match &self.cur { - Some(v) => Some(&v.token), - None => None, - } + fn cur(&self) -> &Token { + &self.cur.token } fn peek<'b>(&'b mut self) -> Option<&'b Token> @@ -373,7 +374,7 @@ impl<'a, I: Tokens> crate::common::parser::buffer::Buffer<'a> for TokenAndSpan: 'b, { debug_assert!( - self.cur().is_some(), + self.cur() != &Token::Eof, "parser should not call peek() without knowing current token" ); @@ -386,12 +387,12 @@ impl<'a, I: Tokens> crate::common::parser::buffer::Buffer<'a> for } #[inline(always)] - fn get_cur(&self) -> Option<&TokenAndSpan> { - self.cur.as_ref() + fn get_cur(&self) -> &TokenAndSpan { + &self.cur } #[inline(always)] - fn get_cur_mut(&mut self) -> &mut Option { + fn get_cur_mut(&mut self) -> &mut TokenAndSpan { &mut self.cur } @@ -414,4 +415,70 @@ impl<'a, I: Tokens> crate::common::parser::buffer::Buffer<'a> for fn iter_mut(&mut self) -> &mut I { &mut self.iter } + + fn bump(&mut self) { + self.bump(); + } + + fn expect_word_token_and_bump(&mut self) -> swc_atoms::Atom { + let t = self.bump(); + t.take_word(self).unwrap() + } + + fn expect_jsx_name_token_and_bump(&mut self) -> swc_atoms::Atom { + let t = self.bump(); + t.take_jsx_name(self) + } + + fn expect_jsx_text_token_and_bump(&mut self) -> (swc_atoms::Atom, swc_atoms::Atom) { + let t = self.bump(); + t.take_jsx_text(self) + } + + fn expect_number_token_and_bump(&mut self) -> (f64, swc_atoms::Atom) { + let t = self.bump(); + t.take_num(self) + } + + fn expect_string_token_and_bump(&mut self) -> (swc_atoms::Atom, swc_atoms::Atom) { + let t = self.bump(); + t.take_str(self) + } + + fn expect_bigint_token_and_bump(&mut self) -> (Box, swc_atoms::Atom) { + let t = self.bump(); + t.take_bigint(self) + } + + fn expect_regex_token_and_bump(&mut self) -> (swc_atoms::Atom, swc_atoms::Atom) { + let t = self.bump(); + t.take_regexp(self) + } + + fn expect_template_token_and_bump( + &mut self, + ) -> ( + crate::common::lexer::LexResult, + swc_atoms::Atom, + ) { + let t = self.bump(); + t.take_template(self) + } + + fn expect_error_token_and_bump(&mut self) -> crate::error::Error { + let t = self.bump(); + t.take_error(self) + } + + fn expect_shebang_token_and_bump(&mut self) -> swc_atoms::Atom { + let t = self.bump(); + t.take_shebang(self) + } + + #[cold] + #[inline(never)] + fn dump_cur(&self) -> String { + let cur = self.cur(); + format!("{cur:?}") + } } diff --git a/crates/swc_ecma_lexer/src/lexer/jsx.rs b/crates/swc_ecma_lexer/src/lexer/jsx.rs index 590522446152..5fbc92266b80 100644 --- a/crates/swc_ecma_lexer/src/lexer/jsx.rs +++ b/crates/swc_ecma_lexer/src/lexer/jsx.rs @@ -3,7 +3,7 @@ use either::Either; use super::*; impl Lexer<'_> { - pub(super) fn read_jsx_token(&mut self) -> LexResult> { + pub(super) fn read_jsx_token(&mut self) -> LexResult { debug_assert!(self.syntax.jsx()); let start = self.input.cur_pos(); @@ -37,7 +37,7 @@ impl Lexer<'_> { // Safety: cur() was Some('<') self.input.bump(); } - return Ok(Some(Token::JSXTagStart)); + return Ok(Token::JSXTagStart); } return self.read_token(); } @@ -62,7 +62,7 @@ impl Lexer<'_> { self.atoms.atom(s) }; - return Ok(Some(Token::JSXText { raw, value })); + return Ok(Token::JSXText { raw, value }); } '>' => { self.emit_error( diff --git a/crates/swc_ecma_lexer/src/lexer/mod.rs b/crates/swc_ecma_lexer/src/lexer/mod.rs index 4c8c3aa4609f..786d4ae495a2 100644 --- a/crates/swc_ecma_lexer/src/lexer/mod.rs +++ b/crates/swc_ecma_lexer/src/lexer/mod.rs @@ -143,10 +143,10 @@ impl<'a> Lexer<'a> { } /// babel: `getTokenFromCode` - fn read_token(&mut self) -> LexResult> { + fn read_token(&mut self) -> LexResult { let byte = match self.input.as_str().as_bytes().first() { Some(&v) => v, - None => return Ok(None), + None => return Ok(Token::Eof), }; let handler = unsafe { *(&BYTE_HANDLERS as *const ByteHandler).offset(byte as isize) }; @@ -164,7 +164,7 @@ impl<'a> Lexer<'a> { } } - fn read_token_plus_minus(&mut self) -> LexResult> { + fn read_token_plus_minus(&mut self) -> LexResult { let start = self.cur_pos(); unsafe { @@ -173,7 +173,7 @@ impl<'a> Lexer<'a> { } // '++', '--' - Ok(Some(if self.input.cur() == Some(C as char) { + Ok(if self.input.cur() == Some(C as char) { unsafe { // Safety: cur() is Some(c) self.input.bump(); @@ -204,10 +204,10 @@ impl<'a> Lexer<'a> { } else { BinOpToken::Sub }) - })) + }) } - fn read_token_bang_or_eq(&mut self) -> LexResult> { + fn read_token_bang_or_eq(&mut self) -> LexResult { let start = self.cur_pos(); let had_line_break_before_last = self.had_line_break_before_last(); @@ -216,7 +216,7 @@ impl<'a> Lexer<'a> { self.input.bump(); } - Ok(Some(if self.input.eat_byte(b'=') { + Ok(if self.input.eat_byte(b'=') { // "==" if self.input.eat_byte(b'=') { @@ -247,13 +247,13 @@ impl<'a> Lexer<'a> { Token::Bang } else { Token::AssignOp(AssignOp::Assign) - })) + }) } } impl Lexer<'_> { #[inline(never)] - fn read_token_lt_gt(&mut self) -> LexResult> { + fn read_token_lt_gt(&mut self) -> LexResult { let had_line_break_before_last = self.had_line_break_before_last(); let start = self.cur_pos(); self.bump(); @@ -263,9 +263,9 @@ impl Lexer<'_> { && !self.ctx.contains(Context::ShouldNotLexLtOrGtAsType) { if C == b'<' { - return Ok(Some(tok!('<'))); + return Ok(tok!('<')); } else if C == b'>' { - return Ok(Some(tok!('>'))); + return Ok(tok!('>')); } } @@ -333,6 +333,6 @@ impl Lexer<'_> { return self.read_token(); } - Ok(Some(token)) + Ok(token) } } diff --git a/crates/swc_ecma_lexer/src/lexer/state.rs b/crates/swc_ecma_lexer/src/lexer/state.rs index 817473c7f6c5..8bf3b14bfd9a 100644 --- a/crates/swc_ecma_lexer/src/lexer/state.rs +++ b/crates/swc_ecma_lexer/src/lexer/state.rs @@ -739,14 +739,14 @@ impl Tokens for Lexer<'_> { } impl Lexer<'_> { - fn next_token(&mut self, start: &mut BytePos) -> Result, Error> { + fn next_token(&mut self, start: &mut BytePos) -> Result { if let Some(start) = self.state.next_regexp { - return Ok(Some(self.read_regexp(start)?)); + return self.read_regexp(start); } if self.state.is_first { if let Some(shebang) = self.read_shebang()? { - return Ok(Some(Token::Shebang(shebang))); + return Ok(Token::Shebang(shebang)); } } @@ -768,7 +768,7 @@ impl Lexer<'_> { if self.input.last_pos() == self.input.end_pos() { // End of input. self.consume_pending_comments(); - return Ok(None); + return Ok(Token::Eof); } // println!( @@ -794,7 +794,7 @@ impl Lexer<'_> { || self.state.context.current() == Some(TokenContext::JSXClosingTag) { if c.is_ident_start() { - return self.read_jsx_word().map(Some); + return self.read_jsx_word(); } if c == '>' { @@ -802,13 +802,13 @@ impl Lexer<'_> { // Safety: cur() is Some('>') self.input.bump(); } - return Ok(Some(Token::JSXTagEnd)); + return Ok(Token::JSXTagEnd); } if (c == '\'' || c == '"') && self.state.context.current() == Some(TokenContext::JSXOpeningTag) { - return self.read_jsx_str(c).map(Some); + return self.read_jsx_str(c); } } @@ -830,14 +830,14 @@ impl Lexer<'_> { return self.read_token(); } - return Ok(Some(Token::JSXTagStart)); + return Ok(Token::JSXTagStart); } } } if let Some(TokenContext::Tpl) = self.state.context.current() { let start = self.state.tpl_start; - return self.read_tmpl_token(start).map(Some); + return self.read_tmpl_token(start); } self.read_token() @@ -852,13 +852,13 @@ impl Iterator for Lexer<'_> { let res = self.next_token(&mut start); - let token = match res.map_err(Token::Error).map_err(Some) { + let token = match res.map_err(Token::Error) { Ok(t) => t, Err(e) => e, }; let span = self.span(start); - if let Some(ref token) = token { + if !matches!(token, Token::Eof) { if let Some(comments) = self.comments_buffer.as_mut() { for comment in comments.take_pending_leading() { comments.push(BufferedComment { @@ -872,16 +872,15 @@ impl Iterator for Lexer<'_> { self.state.update(start, token.kind()); self.state.prev_hi = self.last_pos(); self.state.had_line_break_before_last = self.had_line_break_before_last(); - } - - token.map(|token| { // Attach span to token. - TokenAndSpan { + Some(TokenAndSpan { token, had_line_break: self.had_line_break_before_last(), span, - } - }) + }) + } else { + None + } } } diff --git a/crates/swc_ecma_lexer/src/lexer/table.rs b/crates/swc_ecma_lexer/src/lexer/table.rs index bdd1fe3a8bdf..798c5194a371 100644 --- a/crates/swc_ecma_lexer/src/lexer/table.rs +++ b/crates/swc_ecma_lexer/src/lexer/table.rs @@ -15,7 +15,7 @@ use crate::{ token::{BinOpToken, IdentLike, Keyword, KnownIdent, Token, Word}, }; -pub(super) type ByteHandler = Option fn(&mut Lexer<'aa>) -> LexResult>>; +pub(super) type ByteHandler = Option fn(&mut Lexer<'aa>) -> LexResult>; /// Lookup table mapping any incoming byte to a handler function defined below. pub(super) static BYTE_HANDLERS: [ByteHandler; 256] = [ @@ -43,7 +43,7 @@ const ___: ByteHandler = None; const EOF: ByteHandler = Some(|lexer| { lexer.input.bump_bytes(1); - Ok(None) + Ok(Token::Eof) }); const ERR: ByteHandler = Some(|lexer| { @@ -61,7 +61,7 @@ const ERR: ByteHandler = Some(|lexer| { }); /// Identifier and we know that this cannot be a keyword or known ident. -const IDN: ByteHandler = Some(|lexer| lexer.read_ident_unknown().map(Some)); +const IDN: ByteHandler = Some(|lexer| lexer.read_ident_unknown()); const L_A: ByteHandler = Some(|lexer| { lexer.read_keyword_with(&|s| match s { @@ -347,22 +347,19 @@ const L_Y: ByteHandler = Some(|lexer| { const L_Z: ByteHandler = IDN; /// `0` -const ZER: ByteHandler = Some(|lexer| lexer.read_token_zero().map(Some)); +const ZER: ByteHandler = Some(|lexer| lexer.read_token_zero()); /// Numbers const DIG: ByteHandler = Some(|lexer| { debug_assert!(lexer.cur().is_some_and(|cur| cur != '0')); - lexer - .read_number::() - .map(|v| match v { - Either::Left((value, raw)) => Token::Num { value, raw }, - Either::Right((value, raw)) => Token::BigInt { value, raw }, - }) - .map(Some) + lexer.read_number::().map(|v| match v { + Either::Left((value, raw)) => Token::Num { value, raw }, + Either::Right((value, raw)) => Token::BigInt { value, raw }, + }) }); /// String literals with `'` or `"` -const QOT: ByteHandler = Some(|lexer| lexer.read_str_lit().map(Some)); +const QOT: ByteHandler = Some(|lexer| lexer.read_str_lit()); /// Unicode const UNI: ByteHandler = Some(|lexer| { @@ -374,7 +371,7 @@ const UNI: ByteHandler = Some(|lexer| { // Identifier or keyword. '\uXXXX' sequences are allowed in // identifiers, so '\' also dispatches to that. if c == '\\' || c.is_ident_start() { - return lexer.read_ident_unknown().map(Some); + return lexer.read_ident_unknown(); } let start = lexer.cur_pos(); @@ -386,28 +383,28 @@ const UNI: ByteHandler = Some(|lexer| { }); /// `:` -const COL: ByteHandler = Some(|lexer| lexer.read_token_colon().map(Some)); +const COL: ByteHandler = Some(|lexer| lexer.read_token_colon()); /// `%` -const PRC: ByteHandler = Some(|lexer| lexer.read_token_mul_mod(false).map(Some)); +const PRC: ByteHandler = Some(|lexer| lexer.read_token_mul_mod(false)); /// `*` -const ATR: ByteHandler = Some(|lexer| lexer.read_token_mul_mod(true).map(Some)); +const ATR: ByteHandler = Some(|lexer| lexer.read_token_mul_mod(true)); /// `?` -const QST: ByteHandler = Some(|lexer| lexer.read_token_question_mark().map(Some)); +const QST: ByteHandler = Some(|lexer| lexer.read_token_question_mark()); /// `&` -const AMP: ByteHandler = Some(|lexer| lexer.read_token_logical::().map(Some)); +const AMP: ByteHandler = Some(|lexer| lexer.read_token_logical::()); /// `|` -const PIP: ByteHandler = Some(|lexer| lexer.read_token_logical::().map(Some)); +const PIP: ByteHandler = Some(|lexer| lexer.read_token_logical::()); macro_rules! single_char { ($name:ident, $c:literal, $token:ident) => { const $name: ByteHandler = Some(|lexer| { lexer.input.bump_bytes(1); - Ok(Some(Token::$token)) + Ok(Token::$token) }); }; } @@ -431,12 +428,12 @@ single_char!(BEC, b'}', RBrace); const CRT: ByteHandler = Some(|lexer| { // Bitwise xor lexer.input.bump_bytes(1); - Ok(Some(if lexer.input.cur_as_ascii() == Some(b'=') { + Ok(if lexer.input.cur_as_ascii() == Some(b'=') { lexer.input.bump_bytes(1); Token::AssignOp(AssignOp::BitXorAssign) } else { Token::BinOp(BinOpToken::BitXor) - })) + }) }); /// `+` @@ -452,7 +449,7 @@ const EXL: ByteHandler = Some(|lexer| lexer.read_token_bang_or_eq::()); const EQL: ByteHandler = Some(|lexer| lexer.read_token_bang_or_eq::()); /// `.` -const PRD: ByteHandler = Some(|lexer| lexer.read_token_dot().map(Some)); +const PRD: ByteHandler = Some(|lexer| lexer.read_token_dot()); /// `<` const LSS: ByteHandler = Some(|lexer| lexer.read_token_lt_gt::()); diff --git a/crates/swc_ecma_lexer/src/parser/mod.rs b/crates/swc_ecma_lexer/src/parser/mod.rs index cdd43b6cf43a..7307365bbb3c 100644 --- a/crates/swc_ecma_lexer/src/parser/mod.rs +++ b/crates/swc_ecma_lexer/src/parser/mod.rs @@ -116,11 +116,13 @@ impl> Parser { ctx.set(Context::InDeclare, in_declare); input.set_ctx(ctx); - Parser { + let mut p = Parser { state: Default::default(), input: Buffer::new(input), found_module_item: false, - } + }; + p.input.bump(); // consume EOF + p } pub fn take_errors(&mut self) -> Vec { @@ -141,11 +143,13 @@ impl> Parser { let shebang = parse_shebang(self)?; - parse_stmt_block_body(self, true, None).map(|body| Script { + let ret = parse_stmt_block_body(self, true, None).map(|body| Script { span: self.span(start), body, shebang, - }) + })?; + + Ok(ret) } pub fn parse_commonjs(&mut self) -> PResult