1use std::mem::take;
2
3use smallvec::{smallvec, SmallVec};
4use swc_common::{BytePos, Span};
5use swc_ecma_ast::EsVersion;
6use tracing::trace;
7
8use super::{Context, Input, Lexer, LexerTrait};
9use crate::{
10 common::{
11 input::Tokens,
12 lexer::{
13 char::CharExt,
14 comments_buffer::{BufferedCommentKind, CommentsBufferTrait},
15 state::{
16 State as StateTrait, TokenKind as TokenKindTrait, TokenType as TokenTypeTrait,
17 },
18 },
19 syntax::SyntaxFlags,
20 },
21 error::{Error, SyntaxError},
22 token::{BinOpToken, Keyword, Token, TokenAndSpan, TokenKind, WordKind},
23 *,
24};
25
26bitflags::bitflags! {
27 #[derive(Debug, Default, Clone, Copy)]
28 pub struct TokenFlags: u8 {
29 const UNICODE = 1 << 0;
30 }
31}
32
33#[derive(Clone)]
37pub struct State {
38 pub is_expr_allowed: bool,
39 pub next_regexp: Option<BytePos>,
40 pub had_line_break: bool,
42 pub had_line_break_before_last: bool,
44 is_first: bool,
46 pub start: BytePos,
47 pub prev_hi: BytePos,
48 pub tpl_start: BytePos,
49
50 context: TokenContexts,
51 syntax: SyntaxFlags,
52
53 token_type: Option<TokenType>,
54}
55
56impl State {
57 pub(super) fn update(&mut self, start: BytePos, next: TokenKind) {
58 if cfg!(feature = "debug") {
59 tracing::trace!(
60 "updating state: next={:?}, had_line_break={} ",
61 next,
62 self.had_line_break()
63 );
64 }
65 let prev = self.token_type();
66 self.set_token_type(next.into());
67 let is_expr_allowed_on_next = self.is_expr_allowed_on_next(prev, start, next);
68 self.set_is_expr_allowed(is_expr_allowed_on_next);
69 }
70
71 fn is_brace_block(
74 token_contexts: &TokenContexts,
75 prev: Option<TokenType>,
76 had_line_break: bool,
77 is_expr_allowed: bool,
78 ) -> bool {
79 let Some(prev) = prev else {
80 return true;
81 };
82
83 if prev.is_colon() {
84 match token_contexts.current() {
85 Some(TokenContext::BraceStmt) => return true,
86 Some(TokenContext::BraceExpr) => return false,
89 _ => {}
90 };
91 }
92
93 if prev.is_keyword_return() || prev.is_keyword_yield() {
103 had_line_break
104 } else if prev.is_rparen()
105 || prev.is_semi()
106 || prev.is_keyword_else()
107 || prev.is_lt()
108 || prev.is_gt()
109 || prev.is_arrow()
110 {
111 true
112 } else if prev.is_lbrace() {
113 let c = token_contexts.current();
117 if c == Some(TokenContext::BraceExpr) {
118 let len = token_contexts.len();
119 if let Some(TokenContext::JSXOpeningTag) = token_contexts.0.get(len - 2) {
120 return true;
121 }
122 }
123 c == Some(TokenContext::BraceStmt)
124 } else {
125 if had_line_break && prev.is_other_and_before_expr_is_false() {
126 return true;
127 }
128 !is_expr_allowed
129 }
130 }
131
132 fn is_expr_allowed_on_next(
135 &mut self,
136 prev: Option<TokenType>,
137 start: BytePos,
138 next: TokenKind,
139 ) -> bool {
140 let is_expr_allowed = self.is_expr_allowed();
141 let had_line_break = self.had_line_break();
142 let had_line_break_before_last = self.had_line_break_before_last();
143 let is_next_keyword = next.is_keyword();
144 let syntax = self.syntax();
145 let context = self.mut_token_contexts();
146
147 if is_next_keyword && prev.is_some_and(|prev| prev.is_dot()) {
148 false
149 } else if next.is_rparen() || next.is_rbrace() {
150 if context.len() == 1 {
152 return true;
153 } else {
154 let out = context.pop().unwrap();
155 if out == TokenContext::BraceStmt
157 && matches!(
158 context.current(),
159 Some(TokenContext::FnExpr | TokenContext::ClassExpr)
160 )
161 {
162 context.pop();
163 return false;
164 }
165
166 if out == TokenContext::TplQuasi {
168 match context.current() {
169 Some(TokenContext::Tpl) => return false,
170 _ => return true,
171 }
172 }
173 !out.is_expr()
175 }
176 } else if next.is_keyword_fn() {
177 if is_expr_allowed
180 && !Self::is_brace_block(context, prev, had_line_break, is_expr_allowed)
181 {
182 context.push(TokenContext::FnExpr);
183 }
184 false
185 } else if next.is_keyword_class() {
186 if is_expr_allowed
187 && !Self::is_brace_block(context, prev, had_line_break, is_expr_allowed)
188 {
189 context.push(TokenContext::ClassExpr);
190 }
191 false
192 } else if next.is_colon()
193 && matches!(
194 context.current(),
195 Some(TokenContext::FnExpr | TokenContext::ClassExpr)
196 )
197 {
198 context.pop(); true
205 } else if next.is_known_ident_of()
206 && context.current() == Some(TokenContext::ParenStmt { is_for_loop: true })
207 {
208 !prev
212 .expect("context.current() if ParenStmt, so prev token cannot be None")
213 .before_expr()
214 } else if next.is_ident() {
215 let Some(prev) = prev else {
216 return false;
217 };
218 had_line_break_before_last
219 && (prev.is_keyword_var() || prev.is_keyword_let() || prev.is_keyword_const())
220 } else if next.is_lbrace() {
221 let cur = context.current();
222 if syntax.jsx() && cur == Some(TokenContext::JSXOpeningTag) {
223 context.push(TokenContext::BraceExpr)
224 } else if syntax.jsx() && cur == Some(TokenContext::JSXExpr) {
225 context.push(TokenContext::TplQuasi);
226 } else {
227 let next_ctxt =
228 if Self::is_brace_block(context, prev, had_line_break, is_expr_allowed) {
229 TokenContext::BraceStmt
230 } else {
231 TokenContext::BraceExpr
232 };
233 context.push(next_ctxt);
234 }
235 true
236 } else if next.is_slash()
237 && syntax.jsx()
238 && prev.is_some_and(|prev| prev.is_jsx_tag_start())
239 {
240 context.pop();
241 context.pop(); context.push(TokenContext::JSXClosingTag); false
244 } else if next.is_dollar_lbrace() {
245 context.push(TokenContext::TplQuasi);
246 true
247 } else if next.is_lparen() {
248 let c = match prev {
249 Some(prev) => {
250 if prev.is_keyword_if() || prev.is_keyword_while() || prev.is_keyword_with() {
251 TokenContext::ParenStmt { is_for_loop: false }
252 } else if prev.is_keyword_for() {
253 TokenContext::ParenStmt { is_for_loop: true }
254 } else {
255 TokenContext::ParenExpr
256 }
257 }
258 None => TokenContext::ParenExpr,
259 };
260 context.push(c);
261 true
262 } else if next.is_plus_plus() || next.is_minus_minus() {
263 is_expr_allowed
264 } else if next.is_back_quote() {
265 if let Some(TokenContext::Tpl) = context.current() {
267 context.pop();
268 } else {
269 context.push(TokenContext::Tpl);
270 self.tpl_start = start;
271 }
272 false
273 } else if next.is_jsx_tag_start() {
274 context.push(TokenContext::JSXExpr); context.push(TokenContext::JSXOpeningTag); false
277 } else if next.is_jsx_tag_end() {
278 let out = context.pop();
279 if (out == Some(TokenContext::JSXOpeningTag)
280 && prev.is_some_and(|prev| prev.is_slash()))
281 || out == Some(TokenContext::JSXClosingTag)
282 {
283 context.pop();
284 context.current() == Some(TokenContext::JSXExpr)
285 } else {
286 true
287 }
288 } else {
289 next.before_expr()
290 }
291 }
292}
293
294impl common::lexer::state::State for State {
295 type TokenKind = crate::token::TokenKind;
296 type TokenType = self::TokenType;
297
298 #[inline(always)]
299 fn is_expr_allowed(&self) -> bool {
300 self.is_expr_allowed
301 }
302
303 #[inline(always)]
304 fn set_is_expr_allowed(&mut self, is_expr_allowed: bool) {
305 self.is_expr_allowed = is_expr_allowed;
306 }
307
308 #[inline(always)]
309 fn set_next_regexp(&mut self, start: Option<BytePos>) {
310 self.next_regexp = start;
311 }
312
313 #[inline(always)]
314 fn had_line_break(&self) -> bool {
315 self.had_line_break
316 }
317
318 #[inline(always)]
319 fn mark_had_line_break(&mut self) {
320 self.had_line_break = true;
321 }
322
323 #[inline(always)]
324 fn had_line_break_before_last(&self) -> bool {
325 self.had_line_break_before_last
326 }
327
328 #[inline(always)]
329 fn token_contexts(&self) -> &crate::TokenContexts {
330 &self.context
331 }
332
333 #[inline(always)]
334 fn mut_token_contexts(&mut self) -> &mut crate::TokenContexts {
335 &mut self.context
336 }
337
338 #[inline(always)]
339 fn set_token_type(&mut self, token_type: Self::TokenType) {
340 self.token_type = Some(token_type);
341 }
342
343 #[inline(always)]
344 fn token_type(&self) -> Option<Self::TokenType> {
345 self.token_type
346 }
347
348 #[inline(always)]
349 fn syntax(&self) -> SyntaxFlags {
350 self.syntax
351 }
352
353 #[inline(always)]
354 fn prev_hi(&self) -> BytePos {
355 self.prev_hi
356 }
357
358 #[inline(always)]
359 fn start(&self) -> BytePos {
360 self.start
361 }
362}
363
364#[derive(Debug, Copy, Clone, PartialEq, Eq)]
365pub enum TokenType {
366 Template,
367 Dot,
368 Colon,
369 LBrace,
370 RParen,
371 Semi,
372 BinOp(BinOpToken),
373 Keyword(Keyword),
374 JSXName,
375 JSXText,
376 JSXTagStart,
377 JSXTagEnd,
378 Arrow,
379 Other {
380 before_expr: bool,
381 can_have_trailing_comment: bool,
382 },
383}
384
385impl TokenType {
386 #[inline]
387 pub const fn before_expr(self) -> bool {
388 match self {
389 TokenType::JSXName
390 | TokenType::JSXTagStart
391 | TokenType::JSXTagEnd
392 | TokenType::Template
393 | TokenType::Dot
394 | TokenType::RParen => false,
395
396 TokenType::JSXText
397 | TokenType::Colon
398 | TokenType::LBrace
399 | TokenType::Semi
400 | TokenType::Arrow => true,
401
402 TokenType::BinOp(b) => b.before_expr(),
403 TokenType::Keyword(k) => k.before_expr(),
404 TokenType::Other { before_expr, .. } => before_expr,
405 }
406 }
407}
408
409impl From<TokenKind> for TokenType {
410 #[inline]
411 fn from(t: TokenKind) -> Self {
412 match t {
413 TokenKind::Template => TokenType::Template,
414 TokenKind::Dot => TokenType::Dot,
415 TokenKind::Colon => TokenType::Colon,
416 TokenKind::LBrace => TokenType::LBrace,
417 TokenKind::RParen => TokenType::RParen,
418 TokenKind::Semi => TokenType::Semi,
419 TokenKind::JSXTagEnd => TokenType::JSXTagEnd,
420 TokenKind::JSXTagStart => TokenType::JSXTagStart,
421 TokenKind::JSXText => TokenType::JSXText,
422 TokenKind::JSXName => TokenType::JSXName,
423 TokenKind::BinOp(op) => TokenType::BinOp(op),
424 TokenKind::Arrow => TokenType::Arrow,
425
426 TokenKind::Word(WordKind::Keyword(k)) => TokenType::Keyword(k),
427 _ => TokenType::Other {
428 before_expr: t.before_expr(),
429 can_have_trailing_comment: matches!(
430 t,
431 TokenKind::Num
432 | TokenKind::Str
433 | TokenKind::Word(WordKind::Ident(..))
434 | TokenKind::DollarLBrace
435 | TokenKind::Regex
436 | TokenKind::BigInt
437 | TokenKind::JSXText
438 | TokenKind::RBrace
439 ),
440 },
441 }
442 }
443}
444
445impl crate::common::lexer::state::TokenKind for TokenType {
446 #[inline(always)]
447 fn is_dot(self) -> bool {
448 self == Self::Dot
449 }
450
451 #[inline(always)]
452 fn is_bin_op(self) -> bool {
453 matches!(self, Self::BinOp(_))
454 }
455
456 #[inline(always)]
457 fn is_semi(self) -> bool {
458 self == Self::Semi
459 }
460
461 #[inline(always)]
462 fn is_template(self) -> bool {
463 self == Self::Template
464 }
465
466 #[inline(always)]
467 fn is_keyword(self) -> bool {
468 matches!(self, Self::Keyword(_))
469 }
470
471 #[inline(always)]
472 fn is_colon(self) -> bool {
473 self == Self::Colon
474 }
475
476 #[inline(always)]
477 fn is_lbrace(self) -> bool {
478 self == Self::LBrace
479 }
480
481 #[inline(always)]
482 fn is_rbrace(self) -> bool {
483 unreachable!("RBrace is not a token type")
484 }
485
486 #[inline(always)]
487 fn is_lparen(self) -> bool {
488 unreachable!("LParen is not a token type")
489 }
490
491 #[inline(always)]
492 fn is_rparen(self) -> bool {
493 self == Self::RParen
494 }
495
496 #[inline(always)]
497 fn is_keyword_fn(self) -> bool {
498 self == Self::Keyword(Keyword::Function)
499 }
500
501 #[inline(always)]
502 fn is_keyword_return(self) -> bool {
503 self == Self::Keyword(Keyword::Return)
504 }
505
506 #[inline(always)]
507 fn is_keyword_yield(self) -> bool {
508 self == Self::Keyword(Keyword::Yield)
509 }
510
511 #[inline(always)]
512 fn is_keyword_else(self) -> bool {
513 self == Self::Keyword(Keyword::Else)
514 }
515
516 #[inline(always)]
517 fn is_keyword_class(self) -> bool {
518 self == Self::Keyword(Keyword::Class)
519 }
520
521 #[inline(always)]
522 fn is_keyword_let(self) -> bool {
523 self == Self::Keyword(Keyword::Let)
524 }
525
526 #[inline(always)]
527 fn is_keyword_var(self) -> bool {
528 self == Self::Keyword(Keyword::Var)
529 }
530
531 #[inline(always)]
532 fn is_keyword_const(self) -> bool {
533 self == Self::Keyword(Keyword::Const)
534 }
535
536 #[inline(always)]
537 fn is_keyword_if(self) -> bool {
538 self == Self::Keyword(Keyword::If)
539 }
540
541 #[inline(always)]
542 fn is_keyword_while(self) -> bool {
543 self == Self::Keyword(Keyword::While)
544 }
545
546 #[inline(always)]
547 fn is_keyword_for(self) -> bool {
548 self == Self::Keyword(Keyword::For)
549 }
550
551 #[inline(always)]
552 fn is_keyword_with(self) -> bool {
553 self == Self::Keyword(Keyword::With)
554 }
555
556 #[inline(always)]
557 fn is_lt(self) -> bool {
558 self == Self::BinOp(BinOpToken::Lt)
559 }
560
561 #[inline(always)]
562 fn is_gt(self) -> bool {
563 self == Self::BinOp(BinOpToken::Gt)
564 }
565
566 #[inline(always)]
567 fn is_arrow(self) -> bool {
568 self == Self::Arrow
569 }
570
571 #[inline(always)]
572 fn is_ident(self) -> bool {
573 unreachable!()
574 }
575
576 #[inline(always)]
577 fn is_known_ident_of(self) -> bool {
578 unreachable!()
579 }
580
581 #[inline(always)]
582 fn is_slash(self) -> bool {
583 self == Self::BinOp(BinOpToken::Div)
584 }
585
586 #[inline(always)]
587 fn is_dollar_lbrace(self) -> bool {
588 unreachable!()
589 }
590
591 #[inline(always)]
592 fn is_plus_plus(self) -> bool {
593 unreachable!()
594 }
595
596 #[inline(always)]
597 fn is_minus_minus(self) -> bool {
598 unreachable!()
599 }
600
601 #[inline(always)]
602 fn is_back_quote(self) -> bool {
603 unreachable!()
604 }
605
606 #[inline(always)]
607 fn before_expr(self) -> bool {
608 self.before_expr()
609 }
610
611 #[inline(always)]
612 fn is_jsx_tag_start(self) -> bool {
613 self == Self::JSXTagStart
614 }
615
616 #[inline(always)]
617 fn is_jsx_tag_end(self) -> bool {
618 self == Self::JSXTagEnd
619 }
620}
621
622impl crate::common::lexer::state::TokenType for TokenType {
623 #[inline(always)]
624 fn is_other_and_before_expr_is_false(self) -> bool {
625 match self {
626 TokenType::Other { before_expr, .. } => !before_expr,
627 _ => false,
628 }
629 }
630
631 #[inline(always)]
632 fn is_other_and_can_have_trailing_comment(self) -> bool {
633 match self {
634 TokenType::Other {
635 can_have_trailing_comment,
636 ..
637 } => can_have_trailing_comment,
638 _ => false,
639 }
640 }
641}
642
643impl Tokens<TokenAndSpan> for Lexer<'_> {
644 type Checkpoint = Self;
645
646 #[inline]
647 fn set_ctx(&mut self, ctx: Context) {
648 if ctx.contains(Context::Module) && !self.module_errors.borrow().is_empty() {
649 let mut module_errors = self.module_errors.borrow_mut();
650 self.errors.borrow_mut().append(&mut *module_errors);
651 }
652 self.ctx = ctx
653 }
654
655 #[inline]
656 fn ctx(&self) -> Context {
657 self.ctx
658 }
659
660 #[inline]
661 fn ctx_mut(&mut self) -> &mut Context {
662 &mut self.ctx
663 }
664
665 fn checkpoint_save(&self) -> Self::Checkpoint {
666 self.clone()
667 }
668
669 fn checkpoint_load(&mut self, checkpoint: Self::Checkpoint) {
670 *self = checkpoint;
671 }
672
673 #[inline]
674 fn syntax(&self) -> SyntaxFlags {
675 self.syntax
676 }
677
678 #[inline]
679 fn target(&self) -> EsVersion {
680 self.target
681 }
682
683 #[inline]
684 fn start_pos(&self) -> BytePos {
685 self.start_pos
686 }
687
688 #[inline]
689 fn set_expr_allowed(&mut self, allow: bool) {
690 self.state.is_expr_allowed = allow;
691 }
692
693 #[inline]
694 fn set_next_regexp(&mut self, start: Option<BytePos>) {
695 self.state.next_regexp = start;
696 }
697
698 #[inline]
699 fn token_context(&self) -> &TokenContexts {
700 &self.state.context
701 }
702
703 #[inline]
704 fn token_context_mut(&mut self) -> &mut TokenContexts {
705 &mut self.state.context
706 }
707
708 #[inline]
709 fn set_token_context(&mut self, c: TokenContexts) {
710 self.state.context = c;
711 }
712
713 #[inline]
714 fn add_error(&mut self, error: Error) {
715 self.errors.borrow_mut().push(error);
716 }
717
718 #[inline]
719 fn add_module_mode_error(&mut self, error: Error) {
720 if self.ctx.contains(Context::Module) {
721 self.add_error(error);
722 return;
723 }
724 self.module_errors.borrow_mut().push(error);
725 }
726
727 #[inline]
728 fn take_errors(&mut self) -> Vec<Error> {
729 take(&mut self.errors.borrow_mut())
730 }
731
732 #[inline]
733 fn take_script_module_errors(&mut self) -> Vec<Error> {
734 take(&mut self.module_errors.borrow_mut())
735 }
736
737 #[inline]
738 fn end_pos(&self) -> BytePos {
739 self.input.end_pos()
740 }
741
742 fn update_token_flags(&mut self, _: impl FnOnce(&mut lexer::TokenFlags)) {
743 }
745
746 fn token_flags(&self) -> lexer::TokenFlags {
747 Default::default()
748 }
749}
750
751impl Lexer<'_> {
752 fn next_token(&mut self, start: &mut BytePos) -> Result<Token, Error> {
753 if let Some(next_regexp) = self.state.next_regexp {
754 *start = next_regexp;
755 return self.read_regexp(next_regexp);
756 }
757
758 if self.state.is_first {
759 if let Some(shebang) = self.read_shebang()? {
760 return Ok(Token::Shebang(shebang));
761 }
762 }
763
764 self.state.had_line_break = self.state.is_first;
765 self.state.is_first = false;
766
767 let can_skip_space = !self
769 .state
770 .context
771 .current()
772 .map(|t| t.preserve_space())
773 .unwrap_or_default();
774 if can_skip_space {
775 self.skip_space::<true>();
776 *start = self.input.cur_pos();
777 };
778
779 if self.input.last_pos() == self.input.end_pos() {
780 self.consume_pending_comments();
782 return Ok(Token::Eof);
783 }
784
785 self.state.start = *start;
792
793 if self.syntax.jsx()
794 && !self.ctx.contains(Context::InPropertyName)
795 && !self.ctx.contains(Context::InType)
796 {
797 if self.state.context.current() == Some(TokenContext::JSXExpr) {
799 return self.read_jsx_token();
800 }
801
802 let c = self.cur();
803 if let Some(c) = c {
804 if self.state.context.current() == Some(TokenContext::JSXOpeningTag)
805 || self.state.context.current() == Some(TokenContext::JSXClosingTag)
806 {
807 if c.is_ident_start() {
808 return self.read_jsx_word();
809 }
810
811 if c == '>' {
812 unsafe {
813 self.input.bump();
815 }
816 return Ok(Token::JSXTagEnd);
817 }
818
819 if (c == '\'' || c == '"')
820 && self.state.context.current() == Some(TokenContext::JSXOpeningTag)
821 {
822 return self.read_jsx_str(c);
823 }
824 }
825
826 if c == '<' && self.state.is_expr_allowed && self.input.peek() != Some('!') {
827 let had_line_break_before_last = self.had_line_break_before_last();
828 let cur_pos = self.input.cur_pos();
829
830 unsafe {
831 self.input.bump();
833 }
834
835 if had_line_break_before_last && self.is_str("<<<<<< ") {
836 let span = Span::new_with_checked(cur_pos, cur_pos + BytePos(7));
837
838 self.emit_error_span(span, SyntaxError::TS1185);
839 self.skip_line_comment(6);
840 self.skip_space::<true>();
841 return self.read_token();
842 }
843
844 return Ok(Token::JSXTagStart);
845 }
846 }
847 }
848
849 if let Some(TokenContext::Tpl) = self.state.context.current() {
850 let start = self.state.tpl_start;
851 return self.read_tmpl_token(start);
852 }
853
854 self.read_token()
855 }
856}
857
858impl Iterator for Lexer<'_> {
859 type Item = TokenAndSpan;
860
861 fn next(&mut self) -> Option<Self::Item> {
862 let mut start = self.cur_pos();
863
864 let res = self.next_token(&mut start);
865
866 let token = match res.map_err(Token::Error) {
867 Ok(t) => t,
868 Err(e) => e,
869 };
870
871 let span = self.span(start);
872 if !matches!(token, Token::Eof) {
873 if let Some(comments) = self.comments_buffer.as_mut() {
874 comments.pending_to_comment(BufferedCommentKind::Leading, start);
875 }
876
877 self.state.update(start, token.kind());
878 self.state.prev_hi = self.last_pos();
879 self.state.had_line_break_before_last = self.had_line_break_before_last();
880 Some(TokenAndSpan {
882 token,
883 had_line_break: self.had_line_break_before_last(),
884 span,
885 })
886 } else {
887 None
888 }
889 }
890}
891
892impl State {
893 pub fn new(syntax: SyntaxFlags, start_pos: BytePos) -> Self {
894 let context = TokenContexts(smallvec![TokenContext::BraceStmt]);
895
896 State {
897 is_expr_allowed: true,
898 next_regexp: None,
899 had_line_break: false,
900 had_line_break_before_last: false,
901 is_first: true,
902 start: BytePos(0),
903 prev_hi: start_pos,
904 tpl_start: BytePos::DUMMY,
905 context,
906 syntax,
907 token_type: None,
908 }
909 }
910}
911
912#[derive(Clone, Default)]
913pub struct TokenContexts(pub SmallVec<[TokenContext; 128]>);
914
915impl TokenContexts {
916 #[inline]
917 pub fn len(&self) -> usize {
918 self.0.len()
919 }
920
921 #[inline]
922 pub fn is_empty(&self) -> bool {
923 self.0.is_empty()
924 }
925
926 #[inline]
927 pub fn pop(&mut self) -> Option<TokenContext> {
928 let opt = self.0.pop();
929 if cfg!(feature = "debug") {
930 trace!("context.pop({:?}): {:?}", opt, self.0);
931 }
932 opt
933 }
934
935 #[inline]
936 pub fn current(&self) -> Option<TokenContext> {
937 self.0.last().cloned()
938 }
939
940 #[inline]
941 pub fn push(&mut self, t: TokenContext) {
942 self.0.push(t);
943
944 if cfg!(feature = "debug") {
945 trace!("context.push({:?}): {:?}", t, self.0);
946 }
947 }
948}
949
950#[derive(Debug, Clone, Copy, PartialEq, Eq)]
954pub enum TokenContext {
955 BraceStmt,
956 BraceExpr,
957 TplQuasi,
958 ParenStmt {
959 is_for_loop: bool,
961 },
962 ParenExpr,
963 Tpl,
964 FnExpr,
965 ClassExpr,
966 JSXOpeningTag,
967 JSXClosingTag,
968 JSXExpr,
969}
970
971impl TokenContext {
972 pub const fn is_expr(&self) -> bool {
973 matches!(
974 self,
975 Self::BraceExpr
976 | Self::TplQuasi
977 | Self::ParenExpr
978 | Self::Tpl
979 | Self::FnExpr
980 | Self::ClassExpr
981 | Self::JSXExpr
982 )
983 }
984
985 pub const fn preserve_space(&self) -> bool {
986 match self {
987 Self::Tpl | Self::JSXExpr => true,
988 _ => false,
989 }
990 }
991}
992
993#[cfg(test)]
994pub(crate) fn with_lexer<F, Ret>(
995 syntax: Syntax,
996 target: EsVersion,
997 s: &str,
998 f: F,
999) -> Result<Ret, ::testing::StdErr>
1000where
1001 F: FnOnce(&mut Lexer<'_>) -> Result<Ret, ()>,
1002{
1003 crate::with_test_sess(s, |_, fm| {
1004 let mut l = Lexer::new(syntax, target, fm, None);
1005 let res = f(&mut l);
1006
1007 #[cfg(debug_assertions)]
1008 let c = TokenContexts(smallvec![TokenContext::BraceStmt]);
1009 #[cfg(debug_assertions)]
1010 debug_assert_eq!(l.state.context.0, c.0);
1011
1012 res
1013 })
1014}
1015
1016#[cfg(test)]
1017pub(crate) fn lex(syntax: Syntax, s: &'static str) -> Vec<TokenAndSpan> {
1018 with_lexer(syntax, Default::default(), s, |l| Ok(l.collect())).unwrap()
1019}
1020
1021#[cfg(test)]
1023pub(crate) fn lex_module_errors(syntax: Syntax, s: &'static str) -> Vec<Error> {
1024 with_lexer(syntax, Default::default(), s, |l| {
1025 l.ctx.insert(Context::Module);
1026 l.ctx.insert(Context::Strict);
1027
1028 let _: Vec<_> = l.collect();
1029
1030 Ok(l.take_errors())
1031 })
1032 .unwrap()
1033}
1034
1035#[cfg(test)]
1036pub(crate) fn lex_tokens(syntax: Syntax, s: &'static str) -> Vec<Token> {
1037 with_lexer(syntax, Default::default(), s, |l| {
1038 Ok(l.map(|ts| ts.token).collect())
1039 })
1040 .unwrap()
1041}
1042
1043#[cfg(test)]
1046pub(crate) fn lex_errors(syntax: Syntax, s: &'static str) -> (Vec<Token>, Vec<Error>) {
1047 with_lexer(syntax, EsVersion::Es2020, s, |l| {
1048 let tokens = l.map(|ts| ts.token).collect();
1049 let errors = l.take_errors();
1050 Ok((tokens, errors))
1051 })
1052 .unwrap()
1053}