diff --git a/.changeset/eighty-pigs-drum.md b/.changeset/eighty-pigs-drum.md new file mode 100644 index 000000000000..fd8532e65320 --- /dev/null +++ b/.changeset/eighty-pigs-drum.md @@ -0,0 +1,7 @@ +--- +swc_common: patch +swc_ecma_lexer: major +swc_ecma_compat_es2015: patch +--- + +refactor(es/parser): rm span swap in parser diff --git a/crates/swc/tests/fixture/issues-2xxx/2844/input/index.swc-stderr b/crates/swc/tests/fixture/issues-2xxx/2844/input/index.swc-stderr new file mode 100644 index 000000000000..32d5f8886a51 --- /dev/null +++ b/crates/swc/tests/fixture/issues-2xxx/2844/input/index.swc-stderr @@ -0,0 +1,10 @@ + x Unexpected escape sequence in reserved word: static + ,-[$DIR/tests/fixture/issues-2xxx/2844/input/index.js:1:1] + 1 | class X { st\u0061tic y() { } } + : ^^^^^^^^^^^ + `---- + x Unexpected token ``. Expected identifier, string literal, numeric literal or [ for the computed key + ,-[$DIR/tests/fixture/issues-2xxx/2844/input/index.js:1:1] + 1 | class X { st\u0061tic y() { } } + : ^^^^^^^^^^^ + `---- diff --git a/crates/swc/tests/tsc-references/parserStrictMode1.1.normal.js b/crates/swc/tests/tsc-references/parserStrictMode1.1.normal.js index 5940090fba1c..ff13e84fc5ad 100644 --- a/crates/swc/tests/tsc-references/parserStrictMode1.1.normal.js +++ b/crates/swc/tests/tsc-references/parserStrictMode1.1.normal.js @@ -1,5 +1,5 @@ //// [parserStrictMode1.ts] -//! x `static` cannot be used as an identifier in strict mode +//! x Expression expected //! ,-[4:1] //! 1 | foo1(); //! 2 | foo1(); diff --git a/crates/swc/tests/tsc-references/parserStrictMode1.2.minified.js b/crates/swc/tests/tsc-references/parserStrictMode1.2.minified.js index 5940090fba1c..ff13e84fc5ad 100644 --- a/crates/swc/tests/tsc-references/parserStrictMode1.2.minified.js +++ b/crates/swc/tests/tsc-references/parserStrictMode1.2.minified.js @@ -1,5 +1,5 @@ //// [parserStrictMode1.ts] -//! x `static` cannot be used as an identifier in strict mode +//! x Expression expected //! ,-[4:1] //! 1 | foo1(); //! 2 | foo1(); diff --git a/crates/swc_common/src/syntax_pos.rs b/crates/swc_common/src/syntax_pos.rs index 1dabb9ca83bb..91cb7b6fa77e 100644 --- a/crates/swc_common/src/syntax_pos.rs +++ b/crates/swc_common/src/syntax_pos.rs @@ -406,12 +406,18 @@ impl Span { Span { lo, hi } } + #[inline] + pub fn new_with_checked(lo: BytePos, hi: BytePos) -> Self { + debug_assert!(lo <= hi, "lo: {lo:#?}, hi: {hi:#?}"); + Span { lo, hi } + } + #[inline] pub fn with_lo(&self, lo: BytePos) -> Span { Span::new(lo, self.hi) } - #[inline] + #[inline(always)] pub fn hi(self) -> BytePos { self.hi } diff --git a/crates/swc_ecma_compat_es2015/src/generator.rs b/crates/swc_ecma_compat_es2015/src/generator.rs index 2d90135a3598..924cbba6383c 100644 --- a/crates/swc_ecma_compat_es2015/src/generator.rs +++ b/crates/swc_ecma_compat_es2015/src/generator.rs @@ -2642,7 +2642,7 @@ impl Generator { .push(expr); } return Invalid { - span: Span::new(BytePos(label.0 as _), BytePos(label.0 as _)), + span: Span::new_with_checked(BytePos(label.0 as _), BytePos(label.0 as _)), } .into(); } diff --git a/crates/swc_ecma_lexer/src/common/lexer/mod.rs b/crates/swc_ecma_lexer/src/common/lexer/mod.rs index e32c94edb80f..4df2108da2d5 100644 --- a/crates/swc_ecma_lexer/src/common/lexer/mod.rs +++ b/crates/swc_ecma_lexer/src/common/lexer/mod.rs @@ -267,7 +267,7 @@ pub trait Lexer<'a, TokenAndSpan>: Tokens + Sized { let s = unsafe { self.input_slice(slice_start, end) }; let cmt = swc_common::comments::Comment { kind: swc_common::comments::CommentKind::Line, - span: Span::new(start, end), + span: Span::new_with_checked(start, end), text: self.atom(s), }; @@ -298,7 +298,7 @@ pub trait Lexer<'a, TokenAndSpan>: Tokens + Sized { }; let cmt = swc_common::comments::Comment { kind: swc_common::comments::CommentKind::Line, - span: Span::new(start, end), + span: Span::new_with_checked(start, end), text: self.atom(s), }; @@ -371,7 +371,7 @@ pub trait Lexer<'a, TokenAndSpan>: Tokens + Sized { self.state_mut().mark_had_line_break(); } let end_pos = self.input().end_pos(); - let span = Span::new(end_pos, end_pos); + let span = Span::new_with_checked(end_pos, end_pos); self.emit_error_span(span, SyntaxError::UnterminatedBlockComment); return; } @@ -407,7 +407,7 @@ pub trait Lexer<'a, TokenAndSpan>: Tokens + Sized { let s = &src[..src.len() - 2]; let cmt = Comment { kind: CommentKind::Block, - span: Span::new(start, end), + span: Span::new_with_checked(start, end), text: self.atom(s), }; @@ -642,20 +642,20 @@ pub trait Lexer<'a, TokenAndSpan>: Tokens + Sized { } /// Reads an integer, octal integer, or floating-point number - fn read_number( + fn read_number( &mut self, - starts_with_dot: bool, ) -> LexResult, Atom)>> { + debug_assert!(!(START_WITH_DOT && START_WITH_ZERO)); debug_assert!(self.cur().is_some()); let start = self.cur_pos(); let mut has_underscore = false; - let lazy_integer = if starts_with_dot { + let lazy_integer = if START_WITH_DOT { // first char is '.' debug_assert!( self.cur().is_some_and(|c| c == '.'), - "read_number(starts_with_dot = true) expects current char to be '.'" + "read_number expects current char to be '.'" ); LazyInteger { start, @@ -664,7 +664,8 @@ pub trait Lexer<'a, TokenAndSpan>: Tokens + Sized { has_underscore: false, } } else { - let starts_with_zero = self.cur().unwrap() == '0'; + debug_assert!(!START_WITH_DOT); + debug_assert!(!START_WITH_ZERO || self.cur().unwrap() == '0'); // Use read_number_no_dot to support long numbers. let lazy_integer = self.read_number_no_dot_as_str::<10>()?; @@ -683,7 +684,7 @@ pub trait Lexer<'a, TokenAndSpan>: Tokens + Sized { return Ok(Either::Right((Box::new(bigint_value), self.atom(raw)))); } - if starts_with_zero { + if START_WITH_ZERO { // TODO: I guess it would be okay if I don't use -ffast-math // (or something like that), but needs review. if s.as_bytes().iter().all(|&c| c == b'0') { @@ -736,8 +737,8 @@ pub trait Lexer<'a, TokenAndSpan>: Tokens + Sized { if has_dot { self.bump(); - // equal: if starts_with_dot { debug_assert!(xxxx) } - debug_assert!(!starts_with_dot || self.cur().is_some_and(|cur| cur.is_ascii_digit())); + // equal: if START_WITH_DOT { debug_assert!(xxxx) } + debug_assert!(!START_WITH_DOT || self.cur().is_some_and(|cur| cur.is_ascii_digit())); // Read numbers after dot self.read_digits::<_, (), 10>(|_, _, _| Ok(((), true)), true, &mut has_underscore)?; @@ -807,7 +808,7 @@ pub trait Lexer<'a, TokenAndSpan>: Tokens + Sized { .checked_mul(radix as u32) .and_then(|v| v.checked_add(val)) .ok_or_else(|| { - let span = Span::new(start, start); + let span = Span::new_with_checked(start, start); crate::error::Error::new(span, SyntaxError::InvalidUnicodeEscape) })?; @@ -1788,9 +1789,9 @@ pub trait Lexer<'a, TokenAndSpan>: Tokens + Sized { } }; if next.is_ascii_digit() { - return self.read_number(true).map(|v| match v { + return self.read_number::().map(|v| match v { Left((value, raw)) => Self::Token::num(value, raw, self), - Right((value, raw)) => Self::Token::bigint(value, raw, self), + Right(_) => unreachable!("read_number should not return bigint for leading dot"), }); } @@ -1847,7 +1848,7 @@ pub trait Lexer<'a, TokenAndSpan>: Tokens + Sized { Some('o') | Some('O') => self.read_radix_number::<8>(), Some('b') | Some('B') => self.read_radix_number::<2>(), _ => { - return self.read_number(false).map(|v| match v { + return self.read_number::().map(|v| match v { Left((value, raw)) => Self::Token::num(value, raw, self), Right((value, raw)) => Self::Token::bigint(value, raw, self), }); @@ -2110,20 +2111,16 @@ pub trait Lexer<'a, TokenAndSpan>: Tokens + Sized { } } - /// This can be used if there's no keyword starting with the first - /// character. - fn read_word_with( + fn read_keyword_with( &mut self, convert: &dyn Fn(&str) -> Option, ) -> LexResult> { debug_assert!(self.cur().is_some()); let start = self.cur_pos(); - let (word, has_escape) = self.read_word_as_str_with(|l, s, _, can_be_known| { - if can_be_known { - if let Some(word) = convert(s) { - return word; - } + let (word, has_escape) = self.read_keyword_as_str_with(|l, s, _, _| { + if let Some(word) = convert(s) { + return word; } let atom = l.atom(s); Self::Token::unknown_ident(atom, l) @@ -2133,7 +2130,6 @@ pub trait Lexer<'a, TokenAndSpan>: Tokens + Sized { // 'await' and 'yield' may have semantic of reserved word, which means lexer // should know context or parser should handle this error. Our approach to this // problem is former one. - if has_escape && word.is_reserved(self.ctx()) { let word = word.into_atom(self).unwrap(); self.error(start, SyntaxError::EscapeInReservedWord { word })? @@ -2141,12 +2137,64 @@ pub trait Lexer<'a, TokenAndSpan>: Tokens + Sized { Ok(Some(word)) } } + + /// This is a performant version of [Lexer::read_word_as_str_with] for + /// reading keywords. We should make sure the first byte is a valid + /// ASCII. + fn read_keyword_as_str_with(&mut self, convert: F) -> LexResult<(Ret, bool)> + where + F: FnOnce(&mut Self, &str, bool, bool) -> Ret, + { + let slice_start = self.cur_pos(); + let has_escape = false; + + // Fast path: try to scan ASCII identifier using byte_search + // Performance optimization: check if first char disqualifies as keyword + // Advance past first byte + self.bump(); + + // Use byte_search to quickly scan to end of ASCII identifier + let next_byte = byte_search! { + lexer: self, + table: NOT_ASCII_ID_CONTINUE_TABLE, + handle_eof: { + // Reached EOF, entire remainder is identifier + let end = self.cur_pos(); + let s = unsafe { + // Safety: slice_start and end are valid position because we got them from + // `self.input` + self.input_slice(slice_start, end) + }; + + return Ok((convert(self, s, false, true), false)); + }, + }; + + // Check if we hit end of identifier or need to fall back to slow path + if !next_byte.is_ascii() { + // Hit Unicode character, fall back to slow path from current position + self.read_word_as_str_with_slow_path(convert, slice_start, has_escape, true) + } else if next_byte == b'\\' { + // Hit escape sequence, fall back to slow path from current position + self.read_word_as_str_with_slow_path(convert, slice_start, has_escape, true) + } else { + // Hit end of identifier (non-continue ASCII char) + let end = self.cur_pos(); + let s = unsafe { + // Safety: slice_start and end are valid position because we got them from + // `self.input` + self.input_slice(slice_start, end) + }; + + return Ok((convert(self, s, has_escape, true), has_escape)); + } + } } pub fn pos_span(p: BytePos) -> Span { - Span::new(p, p) + Span::new_with_checked(p, p) } pub fn fixed_len_span(p: BytePos, len: u32) -> Span { - Span::new(p, p + BytePos(len)) + Span::new_with_checked(p, p + BytePos(len)) } diff --git a/crates/swc_ecma_lexer/src/common/lexer/token.rs b/crates/swc_ecma_lexer/src/common/lexer/token.rs index e4a49f5800b8..083f4cd7f858 100644 --- a/crates/swc_ecma_lexer/src/common/lexer/token.rs +++ b/crates/swc_ecma_lexer/src/common/lexer/token.rs @@ -185,7 +185,7 @@ pub trait TokenFactory<'a, TokenAndSpan, I: Tokens>: Sized + Parti fn unknown_ident(value: Atom, lexer: &mut Self::Lexer) -> Self; fn is_unknown_ident(&self) -> bool; fn take_unknown_ident(self, buffer: &mut Self::Buffer) -> Atom; - fn take_unknown_ident_ref<'b>(&'b self, buffer: &'b mut Self::Buffer) -> &'b Atom; + fn take_unknown_ident_ref<'b>(&'b self, buffer: &'b Self::Buffer) -> &'b Atom; fn is_known_ident(&self) -> bool; fn take_known_ident(&self) -> Atom; diff --git a/crates/swc_ecma_lexer/src/common/parser/buffer.rs b/crates/swc_ecma_lexer/src/common/parser/buffer.rs index ce686e648ccd..b5af0968d0f1 100644 --- a/crates/swc_ecma_lexer/src/common/parser/buffer.rs +++ b/crates/swc_ecma_lexer/src/common/parser/buffer.rs @@ -201,7 +201,7 @@ pub trait Buffer<'a> { .get_cur() .map(|item| item.span()) .unwrap_or(self.prev_span()); - Span::new(data.lo, data.hi) + Span::new_with_checked(data.lo, data.hi) } /// 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 3bb6e720a96e..d0d36cfc4c5a 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 @@ -1508,9 +1508,8 @@ fn parse_class_body<'a, P: Parser<'a>>(p: &mut P) -> PResult> { while !eof!(p) && !p.input_mut().is(&P::Token::RBRACE) { if p.input_mut().eat(&P::Token::SEMI) { let span = p.input().prev_span(); - elems.push(ClassMember::Empty(EmptyStmt { - span: Span::new(span.lo, span.hi), - })); + debug_assert!(span.lo <= span.hi); + elems.push(ClassMember::Empty(EmptyStmt { span })); continue; } let elem = p.do_inside_of_context(Context::AllowDirectSuper, parse_class_member)?; @@ -1667,12 +1666,12 @@ fn parse_class_inner<'a, P: Parser<'a>>( } else { expect!(p, &P::Token::RBRACE); } - let end = p.last_pos(); + let span = p.span(class_start); Ok(( ident, Box::new(Class { - span: Span::new(class_start, end), + span, decorators, is_abstract: false, type_params, diff --git a/crates/swc_ecma_lexer/src/common/parser/expr.rs b/crates/swc_ecma_lexer/src/common/parser/expr.rs index 648cbf10bc23..93c2cc2a1dfb 100644 --- a/crates/swc_ecma_lexer/src/common/parser/expr.rs +++ b/crates/swc_ecma_lexer/src/common/parser/expr.rs @@ -408,7 +408,7 @@ fn parse_assignment_expr_base<'a, P: Parser<'a>>(p: &mut P) -> PResult ref mut type_params, .. }) => { - *span = Span::new(type_parameters.span.lo, span.hi); + *span = Span::new_with_checked(type_parameters.span.lo, span.hi); *type_params = Some(type_parameters); } _ => unexpected!(p, "("), @@ -547,7 +547,7 @@ fn parse_cond_expr<'a, P: Parser<'a>>(p: &mut P) -> PResult> { p.do_outside_of_context(Context::WillExpectColonForCond, parse_assignment_expr) })?; - let span = Span::new(start, alt.span_hi()); + let span = Span::new_with_checked(start, alt.span_hi()); Ok(CondExpr { span, test, @@ -772,10 +772,10 @@ fn parse_subscript<'a, P: Parser<'a>>( let bracket_lo = p.input().prev_span().lo; let prop = p.allow_in_expr(|p| p.parse_expr())?; expect!(p, &P::Token::RBRACKET); - let span = Span::new(obj.span_lo(), p.input().last_pos()); + let span = Span::new_with_checked(obj.span_lo(), p.input().last_pos()); debug_assert_eq!(obj.span_lo(), span.lo()); let prop = ComputedPropName { - span: Span::new(bracket_lo, p.input().last_pos()), + span: Span::new_with_checked(bracket_lo, p.input().last_pos()), expr: prop, }; @@ -1334,51 +1334,45 @@ fn parse_bin_op_recursively_inner<'a, P: Parser<'a>>( ) -> PResult<(Box, Option)> { const PREC_OF_IN: u8 = 7; - if p.input().syntax().typescript() - && PREC_OF_IN > min_prec - && !p.input_mut().had_line_break_before_cur() - && p.input_mut().is(&P::Token::AS) - { - let start = left.span_lo(); - 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), - expr, - } - .into() - } else { - let type_ann = next_then_parse_ts_type(p)?; - TsAsExpr { - span: p.span(start), - expr, - type_ann, - } - .into() - }; + if p.input().syntax().typescript() && !p.input_mut().had_line_break_before_cur() { + if PREC_OF_IN > min_prec && p.input_mut().is(&P::Token::AS) { + let start = left.span_lo(); + 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), + expr, + } + .into() + } else { + let type_ann = next_then_parse_ts_type(p)?; + TsAsExpr { + span: p.span(start), + expr, + type_ann, + } + .into() + }; - return parse_bin_op_recursively_inner(p, node, min_prec); - } - if p.input().syntax().typescript() - && !p.input_mut().had_line_break_before_cur() - && p.input_mut().is(&P::Token::SATISFIES) - { - let start = left.span_lo(); - let expr = left; - let node = { - let type_ann = next_then_parse_ts_type(p)?; - TsSatisfiesExpr { - span: p.span(start), - expr, - type_ann, - } - .into() - }; + return parse_bin_op_recursively_inner(p, node, min_prec); + } else if p.input_mut().is(&P::Token::SATISFIES) { + let start = left.span_lo(); + let expr = left; + let node = { + let type_ann = next_then_parse_ts_type(p)?; + TsSatisfiesExpr { + span: p.span(start), + expr, + type_ann, + } + .into() + }; - return parse_bin_op_recursively_inner(p, node, min_prec); + return parse_bin_op_recursively_inner(p, node, min_prec); + } } let ctx = p.ctx(); @@ -1480,7 +1474,7 @@ fn parse_bin_op_recursively_inner<'a, P: Parser<'a>>( } let node = BinExpr { - span: Span::new(left.span_lo(), right.span_hi()), + span: Span::new_with_checked(left.span_lo(), right.span_hi()), op, left, right, @@ -1527,7 +1521,7 @@ pub(crate) fn parse_unary_expr<'a, P: Parser<'a>>(p: &mut P) -> PResult>(p: &mut P) -> PResult { p.emit_error(err); Invalid { - span: Span::new(arg_start, arg_start), + span: Span::new_with_checked(arg_start, arg_start), } .into() } @@ -1582,7 +1576,7 @@ pub(crate) fn parse_unary_expr<'a, P: Parser<'a>>(p: &mut P) -> PResult>( arg = ExprOrSpread { spread: None, expr: CondExpr { - span: Span::new(start, alt.span_hi()), + span: Span::new_with_checked(start, alt.span_hi()), test, cons, alt, @@ -1994,7 +1988,7 @@ fn parse_args_or_pats_inner<'a, P: Parser<'a>>( }) => { let new_type_ann = try_parse_ts_type_ann(p)?; if new_type_ann.is_some() { - *span = Span::new(pat_start, p.input().prev_span().hi); + *span = Span::new_with_checked(pat_start, p.input().prev_span().hi); } *type_ann = new_type_ann; } @@ -2044,9 +2038,7 @@ fn parse_args_or_pats_inner<'a, P: Parser<'a>>( _ => false, } } { - let params: Vec = parse_paren_items_as_params(p, items.clone(), None)? - .into_iter() - .collect(); + let params: Vec = parse_paren_items_as_params(p, items.clone(), None)?; let body: Box = parse_fn_block_or_expr_body( p, @@ -2126,9 +2118,7 @@ pub fn parse_paren_expr_or_arrow_fn<'a, P: Parser<'a>>( expect!(p, &P::Token::ARROW); let params: Vec = - parse_paren_items_as_params(p, items_ref.clone(), trailing_comma)? - .into_iter() - .collect(); + parse_paren_items_as_params(p, items_ref.clone(), trailing_comma)?; let body: Box = parse_fn_block_or_expr_body( p, @@ -2188,9 +2178,7 @@ pub fn parse_paren_expr_or_arrow_fn<'a, P: Parser<'a>>( } expect!(p, &P::Token::ARROW); - let params: Vec = parse_paren_items_as_params(p, paren_items, trailing_comma)? - .into_iter() - .collect(); + let params: Vec = parse_paren_items_as_params(p, paren_items, trailing_comma)?; let body: Box = parse_fn_block_or_expr_body( p, @@ -2269,7 +2257,7 @@ pub fn parse_paren_expr_or_arrow_fn<'a, P: Parser<'a>>( if expr_or_spreads.is_empty() { syntax_error!( p, - Span::new(expr_start, p.last_pos()), + Span::new_with_checked(expr_start, p.last_pos()), SyntaxError::EmptyParenExpr ); } @@ -2307,7 +2295,7 @@ pub fn parse_paren_expr_or_arrow_fn<'a, P: Parser<'a>>( // span of sequence expression should not include '(', ')' let seq_expr = SeqExpr { - span: Span::new( + span: Span::new_with_checked( exprs.first().unwrap().span_lo(), exprs.last().unwrap().span_hi(), ), @@ -2333,125 +2321,137 @@ pub fn parse_primary_expr_rest<'a, P: Parser<'a>>( None }; - if p.input_mut().is(&P::Token::CLASS) { + p.input_mut().cur(); + let Some(token_and_span) = p.input().get_cur() else { + return Err(eof_error(p)); + }; + let cur = token_and_span.token(); + + if cur.is_class() { return parse_class_expr(p, start, decorators.unwrap_or_default()); } - if p.input_mut().is(&P::Token::LET) - || (p.input().syntax().typescript() - && (p.input_mut().cur().is_some_and(|cur| cur.is_await()) || p.is_ident_ref())) - || p.is_ident_ref() - { - let ctx = p.ctx(); - let id = parse_ident( - p, - !ctx.contains(Context::InGenerator), - !ctx.contains(Context::InAsync), - )?; - - if can_be_arrow - && id.sym == "async" - && !p.input_mut().had_line_break_before_cur() - && p.is_ident_ref() - { - // see https://github.com/tc39/ecma262/issues/2034 - // ```js - // for(async of - // for(async of x); - // for(async of =>{};;); - // ``` - if ctx.contains(Context::ForLoopInit) - && p.input_mut().is(&P::Token::OF) - && !peek!(p).is_some_and(|peek| peek.is_arrow()) - { - // ```spec https://tc39.es/ecma262/#prod-ForInOfStatement - // for ( [lookahead ∉ { let, async of }] LeftHandSideExpression[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] - // [+Await] for await ( [lookahead ≠ let] LeftHandSideExpression[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] + let try_parse_arrow_expr = |p: &mut P, id: Ident, id_is_async| -> PResult> { + if can_be_arrow && !p.input_mut().had_line_break_before_cur() { + if id_is_async && p.is_ident_ref() { + // see https://github.com/tc39/ecma262/issues/2034 + // ```js + // for(async of + // for(async of x); + // for(async of =>{};;); // ``` + let ctx = p.ctx(); + if ctx.contains(Context::ForLoopInit) + && p.input_mut().is(&P::Token::OF) + && !peek!(p).is_some_and(|peek| peek.is_arrow()) + { + // ```spec https://tc39.es/ecma262/#prod-ForInOfStatement + // for ( [lookahead ∉ { let, async of }] LeftHandSideExpression[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] + // [+Await] for await ( [lookahead ≠ let] LeftHandSideExpression[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] + // ``` + + if !ctx.contains(Context::ForAwaitLoopInit) { + p.emit_err(p.input().prev_span(), SyntaxError::TS1106); + } - if !ctx.contains(Context::ForAwaitLoopInit) { - p.emit_err(p.input().prev_span(), SyntaxError::TS1106); + return Ok(id.into()); } - return Ok(id.into()); - } + let ident = parse_binding_ident(p, false)?; + if p.input().syntax().typescript() + && ident.sym == "as" + && !p.input_mut().is(&P::Token::ARROW) + { + // async as type + let type_ann = p.in_type(parse_ts_type)?; + return Ok(TsAsExpr { + span: p.span(start), + expr: Box::new(id.into()), + type_ann, + } + .into()); + } - let ident = parse_binding_ident(p, false)?; - if p.input().syntax().typescript() - && ident.sym == "as" - && !p.input_mut().is(&P::Token::ARROW) - { - // async as type - let type_ann = p.in_type(parse_ts_type)?; - return Ok(TsAsExpr { + // async a => body + let arg = ident.into(); + let params = vec![arg]; + expect!(p, &P::Token::ARROW); + let body = parse_fn_block_or_expr_body( + p, + true, + false, + true, + params.is_simple_parameter_list(), + )?; + + return Ok(ArrowExpr { span: p.span(start), - expr: Box::new(id.into()), - type_ann, + body, + params, + is_async: true, + is_generator: false, + ..Default::default() } .into()); - } - - // async a => body - let arg = ident.into(); - let params = vec![arg]; - expect!(p, &P::Token::ARROW); - let body = parse_fn_block_or_expr_body( - p, - true, - false, - true, - params.is_simple_parameter_list(), - )?; - - return Ok(ArrowExpr { - span: p.span(start), - body, - params, - is_async: true, - is_generator: false, - ..Default::default() - } - .into()); - } else if can_be_arrow - && !p.input_mut().had_line_break_before_cur() - && p.input_mut().eat(&P::Token::ARROW) - { - if p.ctx().contains(Context::Strict) && id.is_reserved_in_strict_bind() { - p.emit_strict_mode_err(id.span, SyntaxError::EvalAndArgumentsInStrict) - } - let params = vec![id.into()]; - let body = parse_fn_block_or_expr_body( - p, - false, - false, - true, - params.is_simple_parameter_list(), - )?; + } else if p.input_mut().eat(&P::Token::ARROW) { + if p.ctx().contains(Context::Strict) && id.is_reserved_in_strict_bind() { + p.emit_strict_mode_err(id.span, SyntaxError::EvalAndArgumentsInStrict) + } + let params = vec![id.into()]; + let body = parse_fn_block_or_expr_body( + p, + false, + false, + true, + params.is_simple_parameter_list(), + )?; - return Ok(ArrowExpr { - span: p.span(start), - body, - params, - is_async: false, - is_generator: false, - ..Default::default() + return Ok(ArrowExpr { + span: p.span(start), + body, + params, + is_async: false, + is_generator: false, + ..Default::default() + } + .into()); } - .into()); - } else { - return Ok(id.into()); } - } - if p.input_mut().eat(&P::Token::HASH) { + Ok(id.into()) + }; + + let token_start = token_and_span.span().lo; + if cur.is_let() || (p.input().syntax().typescript() && cur.is_await()) { + let ctx = p.ctx(); + let id = parse_ident( + p, + !ctx.contains(Context::InGenerator), + !ctx.contains(Context::InAsync), + )?; + try_parse_arrow_expr(p, id, false) + } else if cur.is_hash() { + p.bump(); // consume `#` let id = parse_ident_name(p)?; - return Ok(PrivateName { + Ok(PrivateName { span: p.span(start), name: id.sym, } - .into()); + .into()) + } else if p.is_ident_ref() { + let cur = p.bump(); + let id_is_async = cur.is_async(); + let Some(word) = cur.take_word(p.input_mut()) else { + unreachable!() + }; + if p.ctx().contains(Context::InClassField) && word == atom!("arguments") { + p.emit_err(p.input().prev_span(), SyntaxError::ArgumentsInClassField) + }; + let id = Ident::new_no_ctxt(word, p.span(token_start)); + try_parse_arrow_expr(p, id, id_is_async) + } else { + syntax_error!(p, p.input().cur_span(), SyntaxError::TS1109) } - - syntax_error!(p, p.input().cur_span(), SyntaxError::TS1109) } pub fn try_parse_regexp<'a, P: Parser<'a>>(p: &mut P, start: BytePos) -> Option> { @@ -2542,13 +2542,12 @@ pub fn try_parse_async_start<'a, P: Parser<'a>>( None } -pub fn parse_this_expr<'a>(p: &mut impl Parser<'a>, start: BytePos) -> PResult> { +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())); p.input_mut().bump(); Ok(ThisExpr { span: p.span(start), - } - .into()) + }) } /// Parse a primary expression or arrow function @@ -2570,7 +2569,7 @@ pub(crate) fn parse_primary_expr<'a, P: Parser<'a>>(p: &mut P) -> PResult>(p: &mut P) -> PResult(p: &mut impl Parser<'a>) -> JSXEmptyExpr { debug_assert!(p.input().syntax().jsx()); let start = p.input_mut().cur_pos(); JSXEmptyExpr { - span: Span::new(start, start), + span: Span::new_with_checked(start, start), } } diff --git a/crates/swc_ecma_lexer/src/common/parser/mod.rs b/crates/swc_ecma_lexer/src/common/parser/mod.rs index 15fb25061644..e5dbf2994ccb 100644 --- a/crates/swc_ecma_lexer/src/common/parser/mod.rs +++ b/crates/swc_ecma_lexer/src/common/parser/mod.rs @@ -270,7 +270,7 @@ pub trait Parser<'a>: Sized + Clone { start <= end, "assertion failed: (span.start <= span.end). start = {start:?}, end = {end:?}", ); - Span::new(start, end) + Span::new_with_checked(start, end) } #[inline(always)] @@ -525,6 +525,6 @@ pub fn eof_error<'a, P: Parser<'a>>(p: &mut P) -> crate::error::Error { "Parser should not call throw_eof_error() without knowing current token" ); let pos = p.input().end_pos(); - let last = Span::new(pos, pos); + let last = Span { lo: pos, hi: pos }; crate::error::Error::new(last, crate::error::SyntaxError::Eof) } 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 df418b52cfd7..a1ba62aeae64 100644 --- a/crates/swc_ecma_lexer/src/common/parser/module_item.rs +++ b/crates/swc_ecma_lexer/src/common/parser/module_item.rs @@ -138,8 +138,9 @@ fn parse_named_export_specifier<'a, P: Parser<'a>>( p.emit_err(orig_ident.span, SyntaxError::TS2207); } + debug_assert!(start <= orig_ident.span.hi()); return Ok(ExportNamedSpecifier { - span: Span::new(start, orig_ident.span.hi()), + span: Span::new_with_checked(start, orig_ident.span.hi()), orig: ModuleExportName::Ident(possibly_orig), exported: Some(ModuleExportName::Ident(exported)), is_type_only: true, @@ -147,7 +148,7 @@ fn parse_named_export_specifier<'a, P: Parser<'a>>( } else { // `export { type as as }` return Ok(ExportNamedSpecifier { - span: Span::new(start, orig_ident.span.hi()), + span: Span::new_with_checked(start, orig_ident.span.hi()), orig: ModuleExportName::Ident(orig_ident), exported: Some(ModuleExportName::Ident(maybe_as)), is_type_only: false, @@ -156,7 +157,7 @@ fn parse_named_export_specifier<'a, P: Parser<'a>>( } else { // `export { type as xxx }` return Ok(ExportNamedSpecifier { - span: Span::new(start, orig_ident.span.hi()), + span: Span::new_with_checked(start, orig_ident.span.hi()), orig: ModuleExportName::Ident(orig_ident), exported: Some(ModuleExportName::Ident(maybe_as)), is_type_only: false, @@ -261,7 +262,7 @@ fn parse_import_specifier<'a, P: Parser<'a>>( } return Ok(ImportSpecifier::Named(ImportNamedSpecifier { - span: Span::new(start, orig_name.span.hi()), + span: Span::new_with_checked(start, orig_name.span.hi()), local, imported: Some(ModuleExportName::Ident(possibly_orig_name)), is_type_only: true, @@ -269,7 +270,7 @@ fn parse_import_specifier<'a, P: Parser<'a>>( } else { // `import { type as as } from 'mod'` return Ok(ImportSpecifier::Named(ImportNamedSpecifier { - span: Span::new(start, maybe_as.span.hi()), + span: Span::new_with_checked(start, maybe_as.span.hi()), local: maybe_as, imported: Some(ModuleExportName::Ident(orig_name)), is_type_only: false, @@ -278,7 +279,7 @@ fn parse_import_specifier<'a, P: Parser<'a>>( } else { // `import { type as xxx } from 'mod'` return Ok(ImportSpecifier::Named(ImportNamedSpecifier { - span: Span::new(start, orig_name.span.hi()), + span: Span::new_with_checked(start, orig_name.span.hi()), local: maybe_as, imported: Some(ModuleExportName::Ident(orig_name)), is_type_only: false, @@ -299,7 +300,7 @@ fn parse_import_specifier<'a, P: Parser<'a>>( if p.input_mut().eat(&P::Token::AS) { let local: Ident = parse_binding_ident(p, false)?.into(); return Ok(ImportSpecifier::Named(ImportNamedSpecifier { - span: Span::new(start, local.span.hi()), + span: Span::new_with_checked(start, local.span.hi()), local, imported: Some(ModuleExportName::Ident(orig_name)), is_type_only, @@ -326,7 +327,7 @@ fn parse_import_specifier<'a, P: Parser<'a>>( if p.input_mut().eat(&P::Token::AS) { let local: Ident = parse_binding_ident(p, false)?.into(); Ok(ImportSpecifier::Named(ImportNamedSpecifier { - span: Span::new(start, local.span.hi()), + span: Span::new_with_checked(start, local.span.hi()), local, imported: Some(ModuleExportName::Str(orig_str)), is_type_only: false, diff --git a/crates/swc_ecma_lexer/src/common/parser/pat.rs b/crates/swc_ecma_lexer/src/common/parser/pat.rs index ac527d1f775c..f8bd26ed7c22 100644 --- a/crates/swc_ecma_lexer/src/common/parser/pat.rs +++ b/crates/swc_ecma_lexer/src/common/parser/pat.rs @@ -530,7 +530,7 @@ fn parse_formal_param_pat<'a, P: Parser<'a>>(p: &mut P) -> PResult { }) => { let new_type_ann = try_parse_ts_type_ann(p)?; if new_type_ann.is_some() { - *span = Span::new(pat_start, p.input().prev_span().hi); + *span = Span::new_with_checked(pat_start, p.input().prev_span().hi); } *type_ann = new_type_ann; } @@ -544,7 +544,7 @@ fn parse_formal_param_pat<'a, P: Parser<'a>>(p: &mut P) -> PResult { Pat::Assign(AssignPat { ref mut span, .. }) => { if (try_parse_ts_type_ann(p)?).is_some() { - *span = Span::new(pat_start, p.input().prev_span().hi); + *span = Span::new_with_checked(pat_start, p.input().prev_span().hi); p.emit_err(*span, SyntaxError::TSTypeAnnotationAfterAssign); } } @@ -643,7 +643,7 @@ pub fn parse_constructor_params<'a, P: Parser<'a>>(p: &mut P) -> PResult>(p: &mut P) -> PResult> let type_ann = if p.input().syntax().typescript() && p.input_mut().is(&P::Token::COLON) { let cur_pos = p.cur_pos(); - let ty = parse_ts_type_ann(p, /* eat_colon */ true, cur_pos)?; + let ty = parse_ts_type_ann(p, /* eat_colon */ true, cur_pos).map(Box::new)?; Some(ty) } else { None diff --git a/crates/swc_ecma_lexer/src/common/parser/stmt.rs b/crates/swc_ecma_lexer/src/common/parser/stmt.rs index d2b5bc94711a..0df0650c9847 100644 --- a/crates/swc_ecma_lexer/src/common/parser/stmt.rs +++ b/crates/swc_ecma_lexer/src/common/parser/stmt.rs @@ -238,7 +238,7 @@ pub fn parse_var_stmt<'a, P: Parser<'a>>(p: &mut P, for_loop: bool) -> PResult { let pos = var_span.hi(); - let span = Span::new(pos, pos); + let span = Span::new_with_checked(pos, pos); p.emit_err(span, SyntaxError::TS1123); return Ok(Box::new(VarDecl { @@ -263,7 +263,7 @@ pub fn parse_var_stmt<'a, P: Parser<'a>>(p: &mut P, for_loop: bool) -> PResult>(p: &mut P) -> PResult { let finalizer = parse_finally_block(p)?; if handler.is_none() && finalizer.is_none() { - p.emit_err(Span::new(catch_start, catch_start), SyntaxError::TS1005); + p.emit_err( + Span::new_with_checked(catch_start, catch_start), + SyntaxError::TS1005, + ); } let span = p.span(start); @@ -995,7 +998,7 @@ fn parse_switch_stmt<'a, P: Parser<'a>>(p: &mut P) -> PResult { } cases.push(SwitchCase { - span: Span::new(case_start, p.input().prev_span().hi), + span: Span::new_with_checked(case_start, p.input().prev_span().hi), test, cons, }); diff --git a/crates/swc_ecma_lexer/src/common/parser/typescript.rs b/crates/swc_ecma_lexer/src/common/parser/typescript.rs index 39a336ad4eb1..bd3fb1bdf628 100644 --- a/crates/swc_ecma_lexer/src/common/parser/typescript.rs +++ b/crates/swc_ecma_lexer/src/common/parser/typescript.rs @@ -351,7 +351,7 @@ pub fn parse_ts_modifier<'a, P: Parser<'a>>( return Err(eof_error(p)); }; let modifier = if cur.is_unknown_ident() { - cur.clone().take_unknown_ident_ref(p.input_mut()).clone() + cur.clone().take_unknown_ident_ref(p.input()).clone() } else if cur.is_known_ident() { cur.take_known_ident() } else if cur.is_in() { @@ -438,11 +438,17 @@ pub fn parse_ts_entity_name<'a, P: Parser<'a>>( 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(dot_start, dot_start), SyntaxError::TS1003); + p.emit_err( + Span::new_with_checked(dot_start, dot_start), + SyntaxError::TS1003, + ); return Ok(entity); }; if !cur.is_hash() && !cur.is_word() { - p.emit_err(Span::new(dot_start, dot_start), SyntaxError::TS1003); + p.emit_err( + Span::new_with_checked(dot_start, dot_start), + SyntaxError::TS1003, + ); return Ok(entity); } let left = entity; @@ -533,11 +539,11 @@ pub fn parse_ts_type_ref<'a, P: Parser<'a>>(p: &mut P) -> PResult { feature = "tracing-spans", tracing::instrument(level = "debug", skip_all) )] -pub fn parse_ts_type_ann<'a, P: Parser<'a>>( +pub(super) fn parse_ts_type_ann<'a, P: Parser<'a>>( p: &mut P, eat_colon: bool, start: BytePos, -) -> PResult> { +) -> PResult { trace_cur!(p, parse_ts_type_ann); debug_assert!(p.input().syntax().typescript()); @@ -551,10 +557,10 @@ pub fn parse_ts_type_ann<'a, P: Parser<'a>>( let type_ann = parse_ts_type(p)?; - Ok(Box::new(TsTypeAnn { + Ok(TsTypeAnn { span: p.span(start), type_ann, - })) + }) }) } @@ -570,10 +576,10 @@ pub fn parse_ts_this_type_predicate<'a, P: Parser<'a>>( let param_name = TsThisTypeOrIdent::TsThisType(lhs); let type_ann = if p.input_mut().eat(&P::Token::IS) { let cur_pos = p.input_mut().cur_pos(); - Some(parse_ts_type_ann( + Some(Box::new(parse_ts_type_ann( p, // eat_colon false, cur_pos, - )?) + )?)) } else { None }; @@ -822,17 +828,21 @@ pub fn parse_ts_type_or_type_predicate_ann<'a, P: Parser<'a>>( // eat_colon false, return_token_start, - ); + ) + .map(Box::new); } let type_pred_var = parse_ident_name(p)?; let type_ann = if has_type_pred_is { p.assert_and_bump(&P::Token::IS); let pos = p.input_mut().cur_pos(); - Some(parse_ts_type_ann( - p, // eat_colon - false, pos, - )?) + Some( + parse_ts_type_ann( + p, // eat_colon + false, pos, + ) + .map(Box::new)?, + ) } else { None }; @@ -920,17 +930,19 @@ fn try_parse_ts_type<'a, P: Parser<'a>>(p: &mut P) -> PResult feature = "tracing-spans", tracing::instrument(level = "debug", skip_all) )] -pub fn try_parse_ts_type_ann<'a, P: Parser<'a>>(p: &mut P) -> PResult>> { +pub(super) fn try_parse_ts_type_ann<'a, P: Parser<'a>>( + p: &mut P, +) -> PResult>> { if !cfg!(feature = "typescript") { return Ok(None); } if p.input_mut().is(&P::Token::COLON) { let pos = p.cur_pos(); - return parse_ts_type_ann(p, /* eat_colon */ true, pos).map(Some); + parse_ts_type_ann(p, /* eat_colon */ true, pos).map(|t| Some(Box::new(t))) + } else { + Ok(None) } - - Ok(None) } /// `tsNextThenParseType` @@ -1012,7 +1024,7 @@ fn parse_ts_enum_member<'a, P: Parser<'a>>(p: &mut P) -> PResult { let start = p.cur_pos(); p.bump(); p.input_mut().store(P::Token::COMMA); - p.emit_err(Span::new(start, start), SyntaxError::TS1005); + p.emit_err(Span::new_with_checked(start, start), SyntaxError::TS1005); None }; @@ -1297,7 +1309,7 @@ pub fn try_parse_ts_index_signature<'a, P: Parser<'a>>( let type_ann = parse_ts_type_ann(p, /* eat_colon */ false, type_ann_start)?; id.span = p.span(ident_start); - id.type_ann = Some(type_ann); + id.type_ann = Some(Box::new(type_ann)); expect!(p, &P::Token::RBRACKET); diff --git a/crates/swc_ecma_lexer/src/input.rs b/crates/swc_ecma_lexer/src/input.rs index 8056f1580575..963d7ab331cf 100644 --- a/crates/swc_ecma_lexer/src/input.rs +++ b/crates/swc_ecma_lexer/src/input.rs @@ -321,7 +321,7 @@ impl<'a, I: Tokens> crate::common::parser::buffer::Buffer<'a> for Buffer { iter: lexer, cur: None, - prev_span: Span::new(start_pos, start_pos), + prev_span: Span::new_with_checked(start_pos, start_pos), next: None, } } diff --git a/crates/swc_ecma_lexer/src/lexer/jsx.rs b/crates/swc_ecma_lexer/src/lexer/jsx.rs index 14985989b3eb..590522446152 100644 --- a/crates/swc_ecma_lexer/src/lexer/jsx.rs +++ b/crates/swc_ecma_lexer/src/lexer/jsx.rs @@ -22,7 +22,7 @@ impl Lexer<'_> { match cur { '<' if self.had_line_break_before_last() && self.is_str("<<<<<< ") => { - let span = Span::new(cur_pos, cur_pos + BytePos(7)); + let span = Span::new_with_checked(cur_pos, cur_pos + BytePos(7)); self.emit_error_span(span, SyntaxError::TS1185); self.skip_line_comment(6); diff --git a/crates/swc_ecma_lexer/src/lexer/number.rs b/crates/swc_ecma_lexer/src/lexer/number.rs index 26f5b1bdef7c..dbdc8a584082 100644 --- a/crates/swc_ecma_lexer/src/lexer/number.rs +++ b/crates/swc_ecma_lexer/src/lexer/number.rs @@ -27,7 +27,13 @@ mod tests { fn num(s: &'static str) -> (f64, Atom) { lex(s, |l| { - l.read_number(s.starts_with('.')).unwrap().left().unwrap() + if s.starts_with('.') { + l.read_number::().unwrap().left().unwrap() + } else if s.starts_with('0') { + l.read_number::().unwrap().left().unwrap() + } else { + l.read_number::().unwrap().left().unwrap() + } }) } @@ -180,7 +186,7 @@ mod tests { assert_eq!( lex( "10000000000000000000000000000000000000000000000000000n", - |l| l.read_number(false).unwrap().right().unwrap() + |l| l.read_number::().unwrap().right().unwrap() ), ( Box::new( diff --git a/crates/swc_ecma_lexer/src/lexer/state.rs b/crates/swc_ecma_lexer/src/lexer/state.rs index 2cea52a59c91..817473c7f6c5 100644 --- a/crates/swc_ecma_lexer/src/lexer/state.rs +++ b/crates/swc_ecma_lexer/src/lexer/state.rs @@ -822,7 +822,7 @@ impl Lexer<'_> { } if had_line_break_before_last && self.is_str("<<<<<< ") { - let span = Span::new(cur_pos, cur_pos + BytePos(7)); + let span = Span::new_with_checked(cur_pos, cur_pos + BytePos(7)); self.emit_error_span(span, SyntaxError::TS1185); self.skip_line_comment(6); diff --git a/crates/swc_ecma_lexer/src/lexer/table.rs b/crates/swc_ecma_lexer/src/lexer/table.rs index 105478b1e189..bdd1fe3a8bdf 100644 --- a/crates/swc_ecma_lexer/src/lexer/table.rs +++ b/crates/swc_ecma_lexer/src/lexer/table.rs @@ -64,7 +64,7 @@ const ERR: ByteHandler = Some(|lexer| { const IDN: ByteHandler = Some(|lexer| lexer.read_ident_unknown().map(Some)); const L_A: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "abstract" => Some(Token::Word(Word::Ident(IdentLike::Known( KnownIdent::Abstract, )))), @@ -88,7 +88,7 @@ const L_A: ByteHandler = Some(|lexer| { }); const L_B: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "break" => Some(Token::Word(Word::Keyword(Keyword::Break))), "boolean" => Some(Token::Word(Word::Ident(IdentLike::Known( KnownIdent::Boolean, @@ -101,7 +101,7 @@ const L_B: ByteHandler = Some(|lexer| { }); const L_C: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "case" => Some(Token::Word(Word::Keyword(Keyword::Case))), "catch" => Some(Token::Word(Word::Keyword(Keyword::Catch))), "class" => Some(Token::Word(Word::Keyword(Keyword::Class))), @@ -112,7 +112,7 @@ const L_C: ByteHandler = Some(|lexer| { }); const L_D: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "debugger" => Some(Token::Word(Word::Keyword(Keyword::Debugger))), "default" => Some(Token::Word(Word::Keyword(Keyword::Default_))), "delete" => Some(Token::Word(Word::Keyword(Keyword::Delete))), @@ -125,7 +125,7 @@ const L_D: ByteHandler = Some(|lexer| { }); const L_E: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "else" => Some(Token::Word(Word::Keyword(Keyword::Else))), "enum" => Some(Token::Word(Word::Ident(IdentLike::Known(KnownIdent::Enum)))), "export" => Some(Token::Word(Word::Keyword(Keyword::Export))), @@ -135,7 +135,7 @@ const L_E: ByteHandler = Some(|lexer| { }); const L_F: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "false" => Some(Token::Word(Word::False)), "finally" => Some(Token::Word(Word::Keyword(Keyword::Finally))), "for" => Some(Token::Word(Word::Keyword(Keyword::For))), @@ -146,7 +146,7 @@ const L_F: ByteHandler = Some(|lexer| { }); const L_G: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "global" => Some(Token::Word(Word::Ident(IdentLike::Known( KnownIdent::Global, )))), @@ -158,7 +158,7 @@ const L_G: ByteHandler = Some(|lexer| { const L_H: ByteHandler = IDN; const L_I: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "if" => Some(Token::Word(Word::Keyword(Keyword::If))), "import" => Some(Token::Word(Word::Keyword(Keyword::Import))), "in" => Some(Token::Word(Word::Keyword(Keyword::In))), @@ -183,7 +183,7 @@ const L_I: ByteHandler = Some(|lexer| { const L_J: ByteHandler = IDN; const L_K: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "keyof" => Some(Token::Word(Word::Ident(IdentLike::Known( KnownIdent::Keyof, )))), @@ -192,21 +192,21 @@ const L_K: ByteHandler = Some(|lexer| { }); const L_L: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "let" => Some(Token::Word(Word::Keyword(Keyword::Let))), _ => None, }) }); const L_M: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "meta" => Some(Token::Word(Word::Ident(IdentLike::Known(KnownIdent::Meta)))), _ => None, }) }); const L_N: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "new" => Some(Token::Word(Word::Keyword(Keyword::New))), "null" => Some(Token::Word(Word::Null)), "number" => Some(Token::Word(Word::Ident(IdentLike::Known( @@ -223,7 +223,7 @@ const L_N: ByteHandler = Some(|lexer| { }); const L_O: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "of" => Some(Token::Word(Word::Ident(IdentLike::Known(KnownIdent::Of)))), "object" => Some(Token::Word(Word::Ident(IdentLike::Known( KnownIdent::Object, @@ -233,7 +233,7 @@ const L_O: ByteHandler = Some(|lexer| { }); const L_P: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "public" => Some(Token::Word(Word::Ident(IdentLike::Known( KnownIdent::Public, )))), @@ -253,7 +253,7 @@ const L_P: ByteHandler = Some(|lexer| { const L_Q: ByteHandler = IDN; const L_R: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "return" => Some(Token::Word(Word::Keyword(Keyword::Return))), "readonly" => Some(Token::Word(Word::Ident(IdentLike::Known( KnownIdent::Readonly, @@ -266,7 +266,7 @@ const L_R: ByteHandler = Some(|lexer| { }); const L_S: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "super" => Some(Token::Word(Word::Keyword(Keyword::Super))), "static" => Some(Token::Word(Word::Ident(IdentLike::Known( KnownIdent::Static, @@ -287,7 +287,7 @@ const L_S: ByteHandler = Some(|lexer| { }); const L_T: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "this" => Some(Token::Word(Word::Keyword(Keyword::This))), "throw" => Some(Token::Word(Word::Keyword(Keyword::Throw))), "true" => Some(Token::Word(Word::True)), @@ -302,7 +302,7 @@ const L_T: ByteHandler = Some(|lexer| { }); const L_U: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "using" => Some(Token::Word(Word::Ident(IdentLike::Known( KnownIdent::Using, )))), @@ -320,7 +320,7 @@ const L_U: ByteHandler = Some(|lexer| { }); const L_V: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "var" => Some(Token::Word(Word::Keyword(Keyword::Var))), "void" => Some(Token::Word(Word::Keyword(Keyword::Void))), _ => None, @@ -328,7 +328,7 @@ const L_V: ByteHandler = Some(|lexer| { }); const L_W: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "while" => Some(Token::Word(Word::Keyword(Keyword::While))), "with" => Some(Token::Word(Word::Keyword(Keyword::With))), _ => None, @@ -338,7 +338,7 @@ const L_W: ByteHandler = Some(|lexer| { const L_X: ByteHandler = IDN; const L_Y: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "yield" => Some(Token::Word(Word::Keyword(Keyword::Yield))), _ => None, }) @@ -351,8 +351,9 @@ const ZER: ByteHandler = Some(|lexer| lexer.read_token_zero().map(Some)); /// Numbers const DIG: ByteHandler = Some(|lexer| { + debug_assert!(lexer.cur().is_some_and(|cur| cur != '0')); lexer - .read_number(false) + .read_number::() .map(|v| match v { Either::Left((value, raw)) => Token::Num { value, raw }, Either::Right((value, raw)) => Token::BigInt { value, raw }, diff --git a/crates/swc_ecma_lexer/src/lexer/tests.rs b/crates/swc_ecma_lexer/src/lexer/tests.rs index e3923724285a..a2df8f425bd5 100644 --- a/crates/swc_ecma_lexer/src/lexer/tests.rs +++ b/crates/swc_ecma_lexer/src/lexer/tests.rs @@ -46,7 +46,7 @@ trait SpanRange: Sized { } impl SpanRange for usize { fn into_span(self) -> Span { - Span::new( + Span::new_with_checked( // +1 as bytepos starts at 1 BytePos((self + 1) as _), // +1 as bytepos starts at 1 @@ -62,7 +62,7 @@ impl SpanRange for Span { } impl SpanRange for Range { fn into_span(self) -> Span { - Span::new( + Span::new_with_checked( // +1 as bytepos starts at 1 BytePos((self.start + 1) as _), // +1 as bytepos starts at 1 diff --git a/crates/swc_ecma_lexer/src/token.rs b/crates/swc_ecma_lexer/src/token.rs index 088379510edb..4db04ea06518 100644 --- a/crates/swc_ecma_lexer/src/token.rs +++ b/crates/swc_ecma_lexer/src/token.rs @@ -780,7 +780,7 @@ impl<'a, I: Tokens> crate::common::lexer::token::TokenFactory<'a, } #[inline(always)] - fn take_unknown_ident_ref<'b>(&'b self, _: &'b mut Self::Buffer) -> &'b Atom { + fn take_unknown_ident_ref<'b>(&'b self, _: &'b Self::Buffer) -> &'b Atom { match self { Self::Word(Word::Ident(IdentLike::Other(ref ident))) => ident, _ => unreachable!(), diff --git a/crates/swc_ecma_lexer/src/utils.rs b/crates/swc_ecma_lexer/src/utils.rs index 8ac48300412e..a914b36342ba 100644 --- a/crates/swc_ecma_lexer/src/utils.rs +++ b/crates/swc_ecma_lexer/src/utils.rs @@ -6,7 +6,9 @@ use crate::token::*; impl Context { pub fn is_reserved(self, word: &Word) -> bool { match *word { - Word::Keyword(Keyword::Let) => self.contains(Context::Strict), + Word::Keyword(Keyword::Let) | Word::Ident(IdentLike::Known(known_ident!("static"))) => { + self.contains(Context::Strict) + } Word::Keyword(Keyword::Await) => { self.contains(Context::InAsync) || self.contains(Context::InStaticBlock) diff --git a/crates/swc_ecma_parser/src/lexer/table.rs b/crates/swc_ecma_parser/src/lexer/table.rs index 03222c4aa7df..75e689e09cd8 100644 --- a/crates/swc_ecma_parser/src/lexer/table.rs +++ b/crates/swc_ecma_parser/src/lexer/table.rs @@ -63,7 +63,7 @@ const ERR: ByteHandler = Some(|lexer| { const IDN: ByteHandler = Some(|lexer| lexer.read_ident_unknown().map(Some)); const L_A: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "abstract" => Some(Token::Abstract), "as" => Some(Token::As), "await" => Some(Token::Await), @@ -77,7 +77,7 @@ const L_A: ByteHandler = Some(|lexer| { }); const L_B: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "break" => Some(Token::Break), "boolean" => Some(Token::Boolean), "bigint" => Some(Token::Bigint), @@ -86,7 +86,7 @@ const L_B: ByteHandler = Some(|lexer| { }); const L_C: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "case" => Some(Token::Case), "catch" => Some(Token::Catch), "class" => Some(Token::Class), @@ -97,7 +97,7 @@ const L_C: ByteHandler = Some(|lexer| { }); const L_D: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "debugger" => Some(Token::Debugger), "default" => Some(Token::Default), "delete" => Some(Token::Delete), @@ -108,7 +108,7 @@ const L_D: ByteHandler = Some(|lexer| { }); const L_E: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "else" => Some(Token::Else), "enum" => Some(Token::Enum), "export" => Some(Token::Export), @@ -118,7 +118,7 @@ const L_E: ByteHandler = Some(|lexer| { }); const L_F: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "false" => Some(Token::False), "finally" => Some(Token::Finally), "for" => Some(Token::For), @@ -129,7 +129,7 @@ const L_F: ByteHandler = Some(|lexer| { }); const L_G: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "global" => Some(Token::Global), "get" => Some(Token::Get), _ => None, @@ -139,7 +139,7 @@ const L_G: ByteHandler = Some(|lexer| { const L_H: ByteHandler = IDN; const L_I: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "if" => Some(Token::If), "import" => Some(Token::Import), "in" => Some(Token::In), @@ -156,28 +156,28 @@ const L_I: ByteHandler = Some(|lexer| { const L_J: ByteHandler = IDN; const L_K: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "keyof" => Some(Token::Keyof), _ => None, }) }); const L_L: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "let" => Some(Token::Let), _ => None, }) }); const L_M: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "meta" => Some(Token::Meta), _ => None, }) }); const L_N: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "new" => Some(Token::New), "null" => Some(Token::Null), "number" => Some(Token::Number), @@ -188,7 +188,7 @@ const L_N: ByteHandler = Some(|lexer| { }); const L_O: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "of" => Some(Token::Of), "object" => Some(Token::Object), "out" => Some(Token::Out), @@ -198,7 +198,7 @@ const L_O: ByteHandler = Some(|lexer| { }); const L_P: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "public" => Some(Token::Public), "package" => Some(Token::Package), "protected" => Some(Token::Protected), @@ -210,7 +210,7 @@ const L_P: ByteHandler = Some(|lexer| { const L_Q: ByteHandler = IDN; const L_R: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "return" => Some(Token::Return), "readonly" => Some(Token::Readonly), "require" => Some(Token::Require), @@ -219,7 +219,7 @@ const L_R: ByteHandler = Some(|lexer| { }); const L_S: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "super" => Some(Token::Super), "static" => Some(Token::Static), "switch" => Some(Token::Switch), @@ -232,7 +232,7 @@ const L_S: ByteHandler = Some(|lexer| { }); const L_T: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "this" => Some(Token::This), "throw" => Some(Token::Throw), "true" => Some(Token::True), @@ -245,7 +245,7 @@ const L_T: ByteHandler = Some(|lexer| { }); const L_U: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "using" => Some(Token::Using), "unique" => Some(Token::Unique), "undefined" => Some(Token::Undefined), @@ -255,7 +255,7 @@ const L_U: ByteHandler = Some(|lexer| { }); const L_V: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "var" => Some(Token::Var), "void" => Some(Token::Void), _ => None, @@ -263,7 +263,7 @@ const L_V: ByteHandler = Some(|lexer| { }); const L_W: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "while" => Some(Token::While), "with" => Some(Token::With), _ => None, @@ -273,7 +273,7 @@ const L_W: ByteHandler = Some(|lexer| { const L_X: ByteHandler = IDN; const L_Y: ByteHandler = Some(|lexer| { - lexer.read_word_with(&|s| match s { + lexer.read_keyword_with(&|s| match s { "yield" => Some(Token::Yield), _ => None, }) @@ -286,8 +286,9 @@ const ZER: ByteHandler = Some(|lexer| lexer.read_token_zero().map(Some)); /// Numbers const DIG: ByteHandler = Some(|lexer| { + debug_assert!(lexer.cur().is_some_and(|cur| cur != '0')); lexer - .read_number(false) + .read_number::() .map(|v| match v { Either::Left((value, raw)) => { lexer.state.set_token_value(TokenValue::Num { value, raw }); diff --git a/crates/swc_ecma_parser/src/lexer/token.rs b/crates/swc_ecma_parser/src/lexer/token.rs index b37a6aec9992..b01ac65d4223 100644 --- a/crates/swc_ecma_parser/src/lexer/token.rs +++ b/crates/swc_ecma_parser/src/lexer/token.rs @@ -787,7 +787,7 @@ impl<'a, I: Tokens> swc_ecma_lexer::common::lexer::token::TokenFactory<'a, Token } #[inline(always)] - fn take_unknown_ident_ref<'b>(&'b self, buffer: &'b mut Self::Buffer) -> &'b Atom { + fn take_unknown_ident_ref<'b>(&'b self, buffer: &'b Self::Buffer) -> &'b Atom { buffer.expect_word_token_value_ref() } @@ -904,7 +904,7 @@ impl std::fmt::Debug for Token { impl Token { pub(crate) fn is_reserved(&self, ctx: Context) -> bool { match self { - Token::Let => ctx.contains(Context::Strict), + Token::Let | Token::Static => ctx.contains(Context::Strict), Token::Await => { ctx.contains(Context::InAsync) || ctx.contains(Context::InStaticBlock) diff --git a/crates/swc_ecma_parser/src/parser/expr.rs b/crates/swc_ecma_parser/src/parser/expr.rs index f6438dda4f1d..663c09b8e2ea 100644 --- a/crates/swc_ecma_parser/src/parser/expr.rs +++ b/crates/swc_ecma_parser/src/parser/expr.rs @@ -83,7 +83,7 @@ impl Parser { }; let arg = self.parse_unary_expr()?; - let span = Span::new(start, arg.span_hi()); + let span = Span::new_with_checked(start, arg.span_hi()); self.check_assign_target(&arg, false); return Ok(UpdateExpr { @@ -125,7 +125,7 @@ impl Parser { Err(err) => { self.emit_error(err); Invalid { - span: Span::new(arg_start, arg_start), + span: Span::new_with_checked(arg_start, arg_start), } .into() } @@ -138,7 +138,7 @@ impl Parser { } return Ok(UnaryExpr { - span: Span::new(start, arg.span_hi()), + span: Span::new_with_checked(start, arg.span_hi()), op, arg, } @@ -192,7 +192,9 @@ impl Parser { .unwrap_or(false); if let Some(tok) = self.input.cur() { match *tok { - Token::This => return parse_this_expr(self, start), + Token::This => { + return parse_this_expr(self, start).map(|expr| Box::new(Expr::This(expr))) + } Token::Async => { if let Some(res) = try_parse_async_start(self, can_be_arrow) { return res; diff --git a/crates/swc_ecma_parser/src/parser/input.rs b/crates/swc_ecma_parser/src/parser/input.rs index 4895ff0086e8..b516054bbf18 100644 --- a/crates/swc_ecma_parser/src/parser/input.rs +++ b/crates/swc_ecma_parser/src/parser/input.rs @@ -194,7 +194,7 @@ impl<'a, I: Tokens> swc_ecma_lexer::common::parser::buffer::Buffer<'a> for Buffe Buffer { iter: lexer, cur: None, - prev_span: Span::new(start_pos, start_pos), + prev_span: Span::new_with_checked(start_pos, start_pos), next: None, } } diff --git a/crates/swc_ecma_parser/src/parser/jsx/mod.rs b/crates/swc_ecma_parser/src/parser/jsx/mod.rs index 74b5ebed719f..a41ecca1f7a6 100644 --- a/crates/swc_ecma_parser/src/parser/jsx/mod.rs +++ b/crates/swc_ecma_parser/src/parser/jsx/mod.rs @@ -66,7 +66,7 @@ impl Parser { self.input_mut().scan_jsx_identifier(); let name: IdentName = self.parse_jsx_ident()?.into(); JSXAttrName::JSXNamespacedName(JSXNamespacedName { - span: Span::new(start, name.span.hi), + span: Span::new_with_checked(start, name.span.hi), ns, name, }) @@ -132,9 +132,9 @@ impl Parser { } let span = if in_expr_context { - Span::new(start, self.last_pos()) + Span::new_with_checked(start, self.last_pos()) } else { - Span::new(start, self.cur_pos()) + Span::new_with_checked(start, self.cur_pos()) }; Ok(JSXClosingElement { span, @@ -157,7 +157,7 @@ impl Parser { } else { self.input_mut().scan_jsx_token(true); } - let span = Span::new(start, self.cur_pos()); + let span = Span::new_with_checked(start, self.cur_pos()); Ok(JSXClosingFragment { span }) } @@ -222,7 +222,7 @@ impl Parser { self.input_mut().scan_jsx_identifier(); let name = self.parse_jsx_ident()?; Ok(JSXAttrName::JSXNamespacedName(JSXNamespacedName { - span: Span::new(start, name.span.hi), + span: Span::new_with_checked(start, name.span.hi), ns: attr_name.into(), name: name.into(), })) @@ -332,7 +332,7 @@ impl Parser { // <>xxxxxx p.input_mut().scan_jsx_token(true); let opening = JSXOpeningFragment { - span: Span::new( + span: Span::new_with_checked( start, p.input.get_cur().map(|cur| cur.span.lo).unwrap_or(start), ), @@ -340,9 +340,9 @@ impl Parser { let children = p.parse_jsx_children(); let closing = p.parse_jsx_closing_fragment(in_expr_context)?; let span = if in_expr_context { - Span::new(start, p.last_pos()) + Span::new_with_checked(start, p.last_pos()) } else { - Span::new(start, p.cur_pos()) + Span::new_with_checked(start, p.cur_pos()) }; Ok(either::Either::Left(JSXFragment { span, @@ -363,7 +363,7 @@ impl Parser { if p.input_mut().cur().is_some_and(|cur| cur == &Token::Gt) { // xxxxx p.input_mut().scan_jsx_token(true); - let span = Span::new( + let span = Span::new_with_checked( start, p.input.get_cur().map(|cur| cur.span.lo).unwrap_or(start), ); @@ -377,9 +377,9 @@ impl Parser { let children = p.parse_jsx_children(); let closing = p.parse_jsx_closing_element(in_expr_context, &opening.name)?; let span = if in_expr_context { - Span::new(start, p.last_pos()) + Span::new_with_checked(start, p.last_pos()) } else { - Span::new(start, p.cur_pos()) + Span::new_with_checked(start, p.cur_pos()) }; Ok(either::Either::Right(JSXElement { span, @@ -403,9 +403,9 @@ impl Parser { p.input_mut().scan_jsx_token(true); } let span = if in_expr_context { - Span::new(start, p.last_pos()) + p.span(start) } else { - Span::new(start, p.cur_pos()) + Span::new_with_checked(start, p.cur_pos()) }; Ok(either::Either::Right(JSXElement { span, diff --git a/crates/swc_ecma_parser/src/parser/tpl.rs b/crates/swc_ecma_parser/src/parser/tpl.rs index b61561712778..84f6d555a71f 100644 --- a/crates/swc_ecma_parser/src/parser/tpl.rs +++ b/crates/swc_ecma_parser/src/parser/tpl.rs @@ -37,7 +37,7 @@ impl Parser { }; let pos = self.input.prev_span().hi; debug_assert!(start <= pos); - let span = Span::new(start, pos); + let span = Span::new_with_checked(start, pos); Ok(Tpl { span, exprs: vec![], @@ -47,7 +47,10 @@ impl Parser { // `____` // `start.0 + 1` means skip the first backtick // `pos.0 - 1` means skip the last backtick - Span::new(BytePos::from_u32(start.0 + 1), BytePos::from_u32(pos.0 - 1)) + Span::new_with_checked( + BytePos::from_u32(start.0 + 1), + BytePos::from_u32(pos.0 - 1), + ) }, tail: true, raw, @@ -83,7 +86,8 @@ impl Parser { // `start.0 + 1` means skip the first backtick // `pos.0 - 2` means skip "${" debug_assert!(start.0 <= pos.0 - 3); - let span = Span::new(BytePos::from_u32(start.0 + 1), BytePos::from_u32(pos.0 - 2)); + let span = + Span::new_with_checked(BytePos::from_u32(start.0 + 1), BytePos::from_u32(pos.0 - 2)); Ok(TplElement { span, raw, @@ -145,7 +149,7 @@ impl Parser { debug_assert!(start.0 <= pos.0 - 2); // case: ___${ // `pos.0 - 2` means skip '${' - let span = Span::new(start, BytePos::from_u32(pos.0 - 2)); + let span = Span::new_with_checked(start, BytePos::from_u32(pos.0 - 2)); match cooked { Ok(cooked) => (raw, Some(cooked), false, span), Err(err) => { @@ -164,7 +168,7 @@ impl Parser { debug_assert!(start.0 < pos.0); // case: ____` // `pos.0 - 1` means skip '`' - let span = Span::new(start, BytePos::from_u32(pos.0 - 1)); + let span = Span::new_with_checked(start, BytePos::from_u32(pos.0 - 1)); match cooked { Ok(cooked) => (raw, Some(cooked), true, span), Err(err) => { @@ -257,7 +261,7 @@ impl Parser { }; let pos = self.input.prev_span().hi; debug_assert!(start.0 <= pos.0); - let span = Span::new(start, pos); + let span = Span::new_with_checked(start, pos); Ok(TsTplLitType { span, types: vec![], @@ -267,7 +271,10 @@ impl Parser { // `____` // `start.0 + 1` means skip the first backtick // `pos.0 - 1` means skip the last backtick - Span::new(BytePos::from_u32(start.0 + 1), BytePos::from_u32(pos.0 - 1)) + Span::new_with_checked( + BytePos::from_u32(start.0 + 1), + BytePos::from_u32(pos.0 - 1), + ) }, tail: true, raw, diff --git a/crates/swc_ecma_parser/tests/comments.rs b/crates/swc_ecma_parser/tests/comments.rs index 82ca8216c799..e70a91fe4b00 100644 --- a/crates/swc_ecma_parser/tests/comments.rs +++ b/crates/swc_ecma_parser/tests/comments.rs @@ -90,7 +90,7 @@ impl Visit for CommentPrinter<'_> { swc_common::errors::Level::Note, "Leading (lo)", ) - .span_note(Span::new(n.lo, n.lo), &c.text) + .span_note(Span::new_with_checked(n.lo, n.lo), &c.text) .emit(); } }); @@ -102,7 +102,7 @@ impl Visit for CommentPrinter<'_> { swc_common::errors::Level::Note, "Trailing (lo)", ) - .span_note(Span::new(n.lo, n.lo), &c.text) + .span_note(Span::new_with_checked(n.lo, n.lo), &c.text) .emit(); } }); @@ -114,7 +114,10 @@ impl Visit for CommentPrinter<'_> { swc_common::errors::Level::Note, "Leading (hi)", ) - .span_note(Span::new(n.hi - BytePos(1), n.hi - BytePos(1)), &c.text) + .span_note( + Span::new_with_checked(n.hi - BytePos(1), n.hi - BytePos(1)), + &c.text, + ) .emit(); } }); @@ -126,7 +129,7 @@ impl Visit for CommentPrinter<'_> { swc_common::errors::Level::Note, "Trailing (hi)", ) - .span_note(Span::new(n.hi, n.hi), &c.text) + .span_note(Span::new_with_checked(n.hi, n.hi), &c.text) .emit(); } });