swc_ecma_lexer/lexer/
state.rs

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/// State of lexer.
34///
35/// Ported from babylon.
36#[derive(Clone)]
37pub struct State {
38    pub is_expr_allowed: bool,
39    pub next_regexp: Option<BytePos>,
40    /// if line break exists between previous token and new token?
41    pub had_line_break: bool,
42    /// if line break exists before last?
43    pub had_line_break_before_last: bool,
44    /// TODO: Remove this field.
45    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    /// Returns true if following `LBrace` token is `block statement` according
72    /// to  `ctx`, `prev`, `is_expr_allowed`.
73    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                // `{ a: {} }`
87                //     ^ ^
88                Some(TokenContext::BraceExpr) => return false,
89                _ => {}
90            };
91        }
92
93        //  function a() {
94        //      return { a: "" };
95        //  }
96        //  function a() {
97        //      return
98        //      {
99        //          function b(){}
100        //      };
101        //  }
102        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            // If previous token was `{`
114            // https://github.com/swc-project/swc/issues/3241#issuecomment-1029584460
115            // <Blah blah={function (): void {}} />
116            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    /// `is_expr_allowed`: previous value.
133    /// `start`: start of newly produced token.
134    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            // TODO: Verify
151            if context.len() == 1 {
152                return true;
153            } else {
154                let out = context.pop().unwrap();
155                // let a = function(){}
156                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                // ${} in template
167                if out == TokenContext::TplQuasi {
168                    match context.current() {
169                        Some(TokenContext::Tpl) => return false,
170                        _ => return true,
171                    }
172                }
173                // expression cannot follow expression
174                !out.is_expr()
175            }
176        } else if next.is_keyword_fn() {
177            // This is required to lex
178            // `x = function(){}/42/i`
179            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            // `function`/`class` keyword is object prop
199            //
200            // ```JavaScript
201            // { function: expr, class: expr }
202            // ```
203            context.pop(); // Remove FnExpr or ClassExpr
204            true
205        } else if next.is_known_ident_of()
206            && context.current() == Some(TokenContext::ParenStmt { is_for_loop: true })
207        {
208            // for (a of b) {}
209
210            // e.g. for (a of _) => true
211            !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(); // do not consider JSX expr -> JSX open tag ->... anymore
242            context.push(TokenContext::JSXClosingTag); // reconsider as closing tag context
243            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 we are in template, ` terminates template.
266            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); // treat as beginning of JSX expression
275            context.push(TokenContext::JSXOpeningTag); // start opening tag context
276            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        // noop
744    }
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        // skip spaces before getting next character, if we are allowed to.
768        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            // End of input.
781            self.consume_pending_comments();
782            return Ok(Token::Eof);
783        }
784
785        // println!(
786        //     "\tContext: ({:?}) {:?}",
787        //     self.input.cur().unwrap(),
788        //     self.state.context.0
789        // );
790
791        self.state.start = *start;
792
793        if self.syntax.jsx()
794            && !self.ctx.contains(Context::InPropertyName)
795            && !self.ctx.contains(Context::InType)
796        {
797            //jsx
798            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                            // Safety: cur() is Some('>')
814                            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                        // Safety: cur() is Some('<')
832                        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            // Attach span to token.
881            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/// The algorithm used to determine whether a regexp can appear at a
951/// given point in the program is loosely based on sweet.js' approach.
952/// See https://github.com/mozilla/sweet.js/wiki/design
953#[derive(Debug, Clone, Copy, PartialEq, Eq)]
954pub enum TokenContext {
955    BraceStmt,
956    BraceExpr,
957    TplQuasi,
958    ParenStmt {
959        /// Is this `for` loop?
960        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/// lex `s` within module context.
1022#[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/// Returns `(tokens, recovered_errors)`. `(tokens)` may contain an error token
1044/// if the lexer fails to recover from it.
1045#[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}