Skip to main content

submilli_engine/
parser.rs

1use crate::compiler_error::{CompileError, CompilerFailure, CompilerStage};
2use crate::{
3    ArrayLiteralElement, ArrowBody, Ast, BinOp, Binding, CatchClause, ClassMember, Diagnostic,
4    EnumInitializer, EnumMember, ExportedDecl, Expr, ExprId, ExprKind, FileId, Ident, ImportKind,
5    ImportSpecifier, ObjectLiteralField, ObjectLiteralMember, ObjectPatternField, ParamDecl,
6    PostfixOp, Severity, Span, Stmt, StmtId, StmtKind, SwitchCase, SwitchDefault, Token, TokenKind,
7    TypeAnnotation, TypeAnnotationField, TypeAnnotationKind, UnOp,
8};
9
10const MAX_ERRORS: usize = 20;
11// Counts recursive grammar entries, not delimiters: expression precedence and
12// mixed statement/type nesting share this budget on a 2 MiB worker stack.
13const MAX_PARSE_DEPTH: usize = 128;
14
15/// Names a type declaration may not take, as in TypeScript.
16const BUILT_IN_TYPE_NAMES: &[&str] = &[
17    "any",
18    "bigint",
19    "boolean",
20    "never",
21    "number",
22    "object",
23    "string",
24    "symbol",
25    "undefined",
26    "unknown",
27    "void",
28];
29
30/// Where a type is being parsed. Only one production depends on it: `(T)`, spelled
31/// identically as a grouped type and as v1's rejected bare parameter list, is decided by
32/// whether a `=>` follows the matching `)`. In an arrow's return annotation that `=>` is
33/// the *body's*, so the tie-break has no signal there and `(T)` groups — which loses
34/// nothing, since a bare parameter list is rejected outright in every position.
35#[derive(Clone, Copy, PartialEq)]
36enum TypePos {
37    Anywhere,
38    ArrowReturn,
39}
40
41/// Compatibility adapter for tools that consume parser diagnostics.
42pub fn parse(source: &str, tokens: Vec<Token>, file: FileId) -> (Ast, Vec<Diagnostic>) {
43    match parse_checked(source, tokens, file) {
44        Ok(result) => result,
45        Err(error) => (Ast::new(), error.into_diagnostics(file)),
46    }
47}
48
49pub fn parse_checked(
50    source: &str,
51    tokens: Vec<Token>,
52    file: FileId,
53) -> Result<(Ast, Vec<Diagnostic>), CompileError> {
54    crate::source::SourceError::check_source_len(source.len())
55        .map_err(|error| error.into_compiler_failure(CompilerStage::Parse))?;
56    validate_token_stream(source, &tokens, file).map_err(|message| CompilerFailure::Internal {
57        stage: CompilerStage::Parse,
58        span: None,
59        message: message.into(),
60    })?;
61    let eof = tokens
62        .last()
63        .cloned()
64        .unwrap_or_else(|| Token::new(TokenKind::Eof, Span::at(file)));
65    let mut p = Parser {
66        source,
67        tokens,
68        split_type_tokens: Vec::new(),
69        file,
70        pos: 0,
71        ast: Ast::new(),
72        diagnostics: Vec::new(),
73        block_depth: 0,
74        class_member_body_depth: 0,
75        function_expression_body_depth: 0,
76        recursion_depth: 0,
77        recursion_limit_span: None,
78        resume_at_next_member: false,
79        last_as_type_end: None,
80        trailing_declarators: Vec::new(),
81        eof,
82        fatal: None,
83    };
84    p.parse_program();
85    if p.fatal.is_none() {
86        p.reject_use_strict_with_non_simple_params();
87    }
88    if let Some(span) = p.recursion_limit_span {
89        p.fatal = Some(CompilerFailure::Limit {
90            stage: CompilerStage::Parse,
91            span: Some(span),
92            message: "parser recursion limit exceeded".into(),
93            help: vec!["simplify nested syntax or split it into separate declarations".into()],
94        });
95    }
96    if let Some(fatal) = p.fatal {
97        return Err(CompileError {
98            diagnostics: p.diagnostics,
99            fatal: Some(fatal),
100        });
101    }
102    p.ast
103        .validate_source(source, file)
104        .map_err(|error| CompileError {
105            diagnostics: p.diagnostics.clone(),
106            fatal: Some(error.into_compiler_failure(CompilerStage::Parse)),
107        })?;
108    crate::tree_height::check_syntax(&p.ast).map_err(|fatal| CompileError {
109        diagnostics: p.diagnostics.clone(),
110        fatal: Some(fatal),
111    })?;
112    Ok((p.ast, p.diagnostics))
113}
114
115fn validate_token_stream(source: &str, tokens: &[Token], file: FileId) -> Result<(), &'static str> {
116    if !matches!(tokens.last().map(|token| &token.kind), Some(TokenKind::Eof)) {
117        return Err("token stream must end with EOF");
118    }
119    let mut previous = 0;
120    for (index, token) in tokens.iter().enumerate() {
121        let span = token.span;
122        if span.file != file
123            || span.start < previous
124            || span.start > span.end
125            || source.get(span.start as usize..span.end as usize).is_none()
126        {
127            return Err("token span is outside its source or out of order");
128        }
129        if matches!(token.kind, TokenKind::Eof) && index != tokens.len() - 1 {
130            return Err("tokens follow EOF");
131        }
132        if let Some(doc) = &token.leading_doc {
133            let span = doc.span;
134            if span.file != file
135                || span.start > span.end
136                || span.end > token.span.start
137                || source.get(span.start as usize..span.end as usize) != Some(doc.text.as_str())
138                || !doc.text.starts_with("/**")
139                || !doc.text.ends_with("*/")
140            {
141                return Err("documentation metadata does not match its source");
142            }
143        }
144        previous = span.start;
145    }
146    Ok(())
147}
148
149pub(crate) struct Parser<'a> {
150    source: &'a str,
151    tokens: Vec<Token>,
152    split_type_tokens: Vec<(usize, Token)>,
153    file: FileId,
154    pos: usize,
155    ast: Ast,
156    diagnostics: Vec<Diagnostic>,
157    /// Zero at top level; used to reject `import` inside nested blocks.
158    block_depth: u32,
159    /// Nonzero while parsing a class method or constructor body; gates `this`/`super`.
160    class_member_body_depth: u32,
161    function_expression_body_depth: u32,
162    recursion_depth: usize,
163    recursion_limit_span: Option<Span>,
164    /// Set by a class member that reported its error after reading its whole
165    /// body (a method missing its return type): the class loop takes it and
166    /// continues at the next member instead of abandoning the class.
167    resume_at_next_member: bool,
168    /// End offset of the type in the most recent `x as T`. A statement ending there
169    /// may end at a line break, since the type stops at one (see
170    /// [`Parser::at_statement_end`]).
171    last_as_type_end: Option<u32>,
172    /// See [`Parser::take_trailing_declarators`].
173    trailing_declarators: Vec<StmtId>,
174    eof: Token,
175    fatal: Option<CompilerFailure>,
176}
177
178impl<'a> Parser<'a> {
179    fn span(&self, start: u32, end: u32) -> Span {
180        Span {
181            file: self.file,
182            start,
183            end,
184        }
185    }
186
187    fn type_name(&mut self, span: Span) -> Option<Ident> {
188        match span.text(self.source, self.file) {
189            Ok(name) => Some(Ident {
190                name: name.to_string(),
191                span,
192            }),
193            Err(error) => {
194                self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
195                None
196            }
197        }
198    }
199
200    fn parse_program(&mut self) {
201        while !self.is_at_eof() {
202            if self.fatal.is_some() || self.error_count() >= MAX_ERRORS {
203                break;
204            }
205            if matches!(self.peek().kind, TokenKind::Semicolon) {
206                self.advance();
207                continue;
208            }
209            let pos_before = self.pos;
210            if let Some(id) = self.parse_statement() {
211                self.ast.top_level.push(id);
212                let trailing = self.take_trailing_declarators();
213                self.ast.top_level.extend(trailing);
214            } else {
215                if self.fatal.is_some() {
216                    break;
217                }
218                // A failed statement that moved and stopped at the start of a
219                // top-level item leaves that item to be parsed, for example after a
220                // class whose last method body took its `}`. Recovering would skip
221                // `class`, `type`, `interface`, `enum` or `import`.
222                if self.pos != pos_before && self.peek_starts_top_level_item() {
223                    continue;
224                }
225                self.recover();
226                if self.pos != pos_before && matches!(self.peek().kind, TokenKind::RightBrace) {
227                    self.advance();
228                    continue;
229                }
230                // Forward-progress guarantee: advance if recover didn't move.
231                if self.pos == pos_before && !self.is_at_eof() {
232                    self.advance();
233                }
234            }
235        }
236    }
237
238    fn parse_statement(&mut self) -> Option<StmtId> {
239        self.trailing_declarators.clear();
240        self.with_recursion_limit(Self::parse_statement_inner)
241    }
242
243    fn parse_statement_inner(&mut self) -> Option<StmtId> {
244        self.reject_async_modifier();
245        match self.peek().kind {
246            TokenKind::Let => self.parse_let_or_const(false),
247            TokenKind::Const => self.parse_let_or_const(true),
248            TokenKind::Function => self.parse_function_decl(),
249            TokenKind::Class => self.parse_class_decl(),
250            TokenKind::Interface => self.parse_interface_decl(),
251            TokenKind::Enum => self.parse_enum_decl(),
252            TokenKind::Identifier if self.at_type_alias_head() => self.parse_type_alias_decl(),
253            TokenKind::Export => self.parse_export(),
254            TokenKind::Import => self.parse_import_decl(),
255            TokenKind::If => self.parse_if(),
256            TokenKind::While => self.parse_while(),
257            TokenKind::Do => self.parse_do_while(),
258            TokenKind::For => self.parse_for(),
259            TokenKind::Switch => self.parse_switch(),
260            TokenKind::Break => self.parse_break(),
261            TokenKind::Continue => self.parse_continue(),
262            TokenKind::Return => self.parse_return(),
263            TokenKind::Throw => self.parse_throw(),
264            TokenKind::Try => self.parse_try(),
265            TokenKind::LeftBrace => self.parse_block(),
266            _ => self.parse_expression_statement(),
267        }
268    }
269
270    fn parse_expression_statement(&mut self) -> Option<StmtId> {
271        let expr_id = self.parse_expression()?;
272        let expr_span = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.span;
273        if !self.at_statement_end() {
274            let what = if matches!(
275                parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.kind,
276                ExprKind::Assign { .. }
277            ) {
278                "assignment"
279            } else {
280                "expression"
281            };
282            self.error_at_peek(format!("expected `;` after {what}"));
283            return None;
284        }
285        let end = self.finish_statement();
286        let span = self.span(expr_span.start, end);
287        let kind = self.statement_kind_for(expr_id)?;
288        parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
289    }
290
291    /// An assignment in statement position becomes an assignment statement;
292    /// only one used as a value stays an `ExprKind::Assign`.
293    fn statement_kind_for(&mut self, expr_id: ExprId) -> Option<StmtKind> {
294        let ExprKind::Assign {
295            target,
296            op,
297            op_span,
298            value,
299        } = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?
300            .kind
301            .clone()
302        else {
303            return Some(StmtKind::Expr(expr_id));
304        };
305        Some(
306            match (
307                parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?
308                    .kind
309                    .clone(),
310                op,
311            ) {
312                (ExprKind::Identifier(target), None) => StmtKind::Assign { target, value },
313                (ExprKind::Identifier(target), Some(op)) => StmtKind::CompoundAssign {
314                    target,
315                    op,
316                    op_span,
317                    value,
318                },
319                (ExprKind::FieldAccess { receiver, name }, None) => StmtKind::AssignField {
320                    receiver,
321                    field_name: name,
322                    value,
323                },
324                (ExprKind::FieldAccess { receiver, name }, Some(op)) => {
325                    StmtKind::CompoundAssignField {
326                        receiver,
327                        field_name: name,
328                        op,
329                        op_span,
330                        value,
331                    }
332                }
333                (ExprKind::IndexAccess { receiver, index }, None) => StmtKind::AssignIndex {
334                    receiver,
335                    index,
336                    value,
337                },
338                (ExprKind::IndexAccess { receiver, index }, Some(op)) => {
339                    StmtKind::CompoundAssignIndex {
340                        receiver,
341                        index,
342                        op,
343                        op_span,
344                        value,
345                    }
346                }
347                _ => return self.invariant_failure("invalid assignment target"),
348            },
349        )
350    }
351
352    /// Parses `let`/`const` with one or more comma-separated declarators. Each
353    /// declarator becomes its own statement, in source order; the first is
354    /// returned and the rest wait in `trailing_declarators` for the caller to
355    /// place after it.
356    fn parse_let_or_const(&mut self, is_const: bool) -> Option<StmtId> {
357        let mut doc = self.take_leading_doc();
358        let kw = self.advance();
359
360        let mut declarators = Vec::new();
361        let mut start = kw.span.start;
362        loop {
363            let Some(kind) = self.parse_declarator(is_const, doc.take()) else {
364                self.discard_detached_patterns(&declarators);
365                return None;
366            };
367            if !matches!(self.peek().kind, TokenKind::Comma) {
368                if !self.at_statement_end() {
369                    self.error_at_peek("expected `;` after declaration");
370                    self.discard_detached_patterns(&declarators);
371                    return None;
372                }
373                let end = self.finish_statement();
374                declarators.push(self.push_declarator(kind, start, end)?);
375                break;
376            }
377            let end = self.prev_token_end();
378            declarators.push(self.push_declarator(kind, start, end)?);
379            self.advance();
380            start = self.peek().span.start;
381        }
382
383        let first = declarators.remove(0);
384        self.trailing_declarators = declarators;
385        Some(first)
386    }
387
388    fn push_declarator(&mut self, kind: StmtKind, start: u32, end: u32) -> Option<StmtId> {
389        let span = self.span(start, end);
390        parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
391    }
392
393    /// The declarators after the first of the declaration `parse_statement` just
394    /// returned. Every caller of `parse_statement` takes them, so none can
395    /// attach to a later statement.
396    fn take_trailing_declarators(&mut self) -> Vec<StmtId> {
397        std::mem::take(&mut self.trailing_declarators)
398    }
399
400    /// One declarator: a name or pattern, an optional annotation, and its initializer.
401    fn parse_declarator(
402        &mut self,
403        is_const: bool,
404        doc: Option<crate::DocComment>,
405    ) -> Option<StmtKind> {
406        let binding = match self.peek().kind {
407            TokenKind::LeftBrace | TokenKind::LeftBracket => Some(self.parse_binding()?),
408            _ => None,
409        };
410
411        let name = if binding.is_none() {
412            let name_tok =
413                self.expect_binding_identifier("expected identifier after `let`/`const`")?;
414            Some(self.ident_from_token(&name_tok))
415        } else {
416            None
417        };
418
419        let ty = if matches!(self.peek().kind, TokenKind::Colon) {
420            self.advance();
421            Some(self.parse_type_annotation()?)
422        } else {
423            None
424        };
425
426        let implicit =
427            !is_const && matches!(self.peek().kind, TokenKind::Semicolon | TokenKind::Comma);
428        let value = if let (true, Some(name)) = (implicit, &name) {
429            // `let x: T;` starts as `undefined`; the typechecker requires `T` to allow it.
430            let value = parse_arena_result(
431                self.ast.try_push_expr(Expr {
432                    kind: ExprKind::Identifier(Ident {
433                        name: "undefined".to_string(),
434                        span: name.span,
435                    }),
436                    span: name.span,
437                }),
438                &mut self.fatal,
439            )?;
440            self.ast.implicit_initializers.insert(value);
441            self.ast.synthetic_undefined.insert(value);
442            value
443        } else {
444            if !matches!(self.peek().kind, TokenKind::Equals) {
445                let (msg, help) = if is_const {
446                    (
447                        "`const` declaration requires an initializer",
448                        vec!["const x: T = expr;".to_string()],
449                    )
450                } else {
451                    (
452                        "`let` declaration requires an initializer",
453                        vec![
454                            "let x: T = expr;".to_string(),
455                            "or declare one that starts empty: `let x: T | undefined;`".to_string(),
456                        ],
457                    )
458                };
459                self.error_at_peek_with_help(msg, help);
460                return None;
461            }
462            self.advance();
463            self.parse_expression()?
464        };
465
466        Some(match (binding, name) {
467            (Some(binding), _) => {
468                if is_const {
469                    StmtKind::ConstPattern {
470                        binding,
471                        ty,
472                        value,
473                        doc,
474                    }
475                } else {
476                    StmtKind::LetPattern {
477                        binding,
478                        ty,
479                        value,
480                        doc,
481                    }
482                }
483            }
484            (None, Some(name)) => {
485                if is_const {
486                    StmtKind::Const {
487                        name,
488                        ty,
489                        value,
490                        doc,
491                    }
492                } else {
493                    StmtKind::Let {
494                        name,
495                        ty,
496                        value,
497                        doc,
498                    }
499                }
500            }
501            (None, None) => return self.invariant_failure("either binding or name must be Some"),
502        })
503    }
504
505    // One level only; nested patterns are rejected.
506    fn parse_binding(&mut self) -> Option<Binding> {
507        match self.peek().kind {
508            TokenKind::LeftBrace => self.parse_object_binding(),
509            TokenKind::LeftBracket => self.parse_array_binding(),
510            _ => {
511                self.error_at_peek("expected `{` or `[` to start a destructuring pattern");
512                None
513            }
514        }
515    }
516
517    fn parse_object_binding(&mut self) -> Option<Binding> {
518        let open = self.advance();
519        let mut fields: Vec<ObjectPatternField> = Vec::new();
520        let mut rest: Option<Ident> = None;
521
522        if matches!(self.peek().kind, TokenKind::RightBrace) {
523            self.error_at_peek("empty object destructuring pattern");
524            return None;
525        }
526
527        loop {
528            if matches!(self.peek().kind, TokenKind::DotDotDot) {
529                let dots = self.advance();
530                let name_tok = self.expect_binding_identifier("expected identifier after `...`")?;
531                let name = self.ident_from_token(&name_tok);
532                if !matches!(self.peek().kind, TokenKind::RightBrace) {
533                    self.error_at_peek_with_help(
534                        "rest element must be the last element in a destructuring pattern",
535                        vec!["move `...` after every other binding".to_string()],
536                    );
537                    return None;
538                }
539                rest = Some(Ident {
540                    name: name.name,
541                    span: self.span(dots.span.start, name.span.end),
542                });
543                break;
544            }
545
546            let source_tok = self.expect_property_name("expected field name in object pattern")?;
547            let source = self.ident_from_token(&source_tok);
548            let field_span_start = source.span.start;
549
550            let (local, field_end) = if matches!(self.peek().kind, TokenKind::Colon) {
551                self.advance();
552                match self.peek().kind {
553                    TokenKind::LeftBrace | TokenKind::LeftBracket => {
554                        self.error_at_peek_with_help(
555                                "nested destructuring is not supported",
556                                vec![
557                                    "destructure once and access nested fields explicitly: const { a } = obj; const inner = a.b;"
558                                        .to_string(),
559                                ],
560                            );
561                        return None;
562                    }
563                    _ => {
564                        let local_tok = self.expect_binding_identifier(
565                            "expected identifier after `:` in pattern",
566                        )?;
567                        let local = self.ident_from_token(&local_tok);
568                        let end = local.span.end;
569                        (local, end)
570                    }
571                }
572            } else {
573                if !matches!(source_tok.kind, TokenKind::Identifier) {
574                    self.error_at_peek("expected `:` after keyword field name in object pattern");
575                    return None;
576                }
577                self.reject_strict_mode_reserved_word(&source_tok);
578                self.reject_eval_or_arguments(source_tok.span);
579                let end = source.span.end;
580                (source.clone(), end)
581            };
582
583            let default = if matches!(self.peek().kind, TokenKind::Equals) {
584                self.advance();
585                Some(self.parse_expression()?)
586            } else {
587                None
588            };
589            let field_end = match default {
590                Some(value) => {
591                    parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?
592                        .span
593                        .end
594                }
595                None => field_end,
596            };
597
598            fields.push(ObjectPatternField {
599                source,
600                local,
601                default,
602                span: self.span(field_span_start, field_end),
603            });
604
605            match self.peek().kind {
606                TokenKind::Comma => {
607                    self.advance();
608                    if matches!(self.peek().kind, TokenKind::RightBrace) {
609                        break;
610                    }
611                }
612                TokenKind::RightBrace => break,
613                _ => {
614                    self.error_at_peek("expected `,` or `}` in object pattern");
615                    return None;
616                }
617            }
618        }
619
620        if !matches!(self.peek().kind, TokenKind::RightBrace) {
621            self.error_at_peek("expected `}` to close object pattern");
622            return None;
623        }
624        let close = self.advance();
625
626        Some(Binding::Object {
627            fields,
628            rest,
629            span: self.span(open.span.start, close.span.end),
630        })
631    }
632
633    fn parse_array_binding(&mut self) -> Option<Binding> {
634        let open = self.advance();
635        let mut elems: Vec<Option<Ident>> = Vec::new();
636        let mut defaults = Vec::new();
637        let mut rest: Option<Ident> = None;
638
639        if matches!(self.peek().kind, TokenKind::RightBracket) {
640            self.error_at_peek("empty array destructuring pattern");
641            return None;
642        }
643
644        loop {
645            match self.peek().kind {
646                TokenKind::Comma => {
647                    elems.push(None);
648                    defaults.push(None);
649                    self.advance();
650                    if matches!(self.peek().kind, TokenKind::RightBracket) {
651                        break;
652                    }
653                    continue;
654                }
655                TokenKind::RightBracket => break,
656                TokenKind::DotDotDot => {
657                    let dots = self.advance();
658                    let name_tok =
659                        self.expect_binding_identifier("expected identifier after `...`")?;
660                    let name = self.ident_from_token(&name_tok);
661                    if !matches!(self.peek().kind, TokenKind::RightBracket) {
662                        self.error_at_peek_with_help(
663                            "rest element must be the last element in a destructuring pattern",
664                            vec!["move `...` after every other binding".to_string()],
665                        );
666                        return None;
667                    }
668                    rest = Some(Ident {
669                        name: name.name,
670                        span: self.span(dots.span.start, name.span.end),
671                    });
672                    break;
673                }
674                TokenKind::LeftBrace | TokenKind::LeftBracket => {
675                    self.error_at_peek_with_help(
676                        "nested destructuring is not supported",
677                        vec![
678                            "destructure once and access nested elements explicitly: const [a] = arr; const inner = a[0];"
679                                .to_string(),
680                        ],
681                    );
682                    return None;
683                }
684                _ => {
685                    let name_tok =
686                        self.expect_binding_identifier("expected identifier in array pattern")?;
687                    let name = self.ident_from_token(&name_tok);
688
689                    let default = if matches!(self.peek().kind, TokenKind::Equals) {
690                        self.advance();
691                        Some(self.parse_expression()?)
692                    } else {
693                        None
694                    };
695                    defaults.push(default);
696                    elems.push(Some(name));
697
698                    match self.peek().kind {
699                        TokenKind::Comma => {
700                            self.advance();
701                            if matches!(self.peek().kind, TokenKind::RightBracket) {
702                                break;
703                            }
704                        }
705                        TokenKind::RightBracket => break,
706                        _ => {
707                            self.error_at_peek("expected `,` or `]` in array pattern");
708                            return None;
709                        }
710                    }
711                }
712            }
713        }
714
715        if !matches!(self.peek().kind, TokenKind::RightBracket) {
716            self.error_at_peek("expected `]` to close array pattern");
717            return None;
718        }
719        let close = self.advance();
720
721        Some(Binding::Array {
722            elems,
723            defaults,
724            rest,
725            span: self.span(open.span.start, close.span.end),
726        })
727    }
728
729    fn parse_function_decl(&mut self) -> Option<StmtId> {
730        let doc = self.take_leading_doc();
731        let kw = self.advance();
732
733        let name_tok = self.expect_binding_identifier("expected function name")?;
734        let name = self.ident_from_token(&name_tok);
735        let is_main = self.block_depth == 0 && name.name == "main";
736        let decl = self.parse_function_decl_after_name(doc, kw, name);
737        if decl.is_none() && is_main {
738            self.ast.unparsed_main = true;
739        }
740        decl
741    }
742
743    fn parse_function_decl_after_name(
744        &mut self,
745        doc: Option<crate::DocComment>,
746        kw: Token,
747        name: Ident,
748    ) -> Option<StmtId> {
749        let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
750            self.parse_generic_param_list()?
751        } else {
752            Vec::new()
753        };
754
755        if !matches!(self.peek().kind, TokenKind::LeftParen) {
756            self.error_at_peek("expected `(` after function name");
757            return None;
758        }
759        self.advance();
760
761        let params = self.parse_param_list(false)?;
762        self.advance(); // `)` — parse_param_list left us on it
763
764        if !matches!(self.peek().kind, TokenKind::Colon) {
765            self.error_at_peek_with_help(
766                "expected `:` and return type",
767                vec![format!(
768                    "write the return type: `function {}(…): T {{ … }}`, with `void` when it returns nothing",
769                    name.name
770                )],
771            );
772            // Read the body anyway, so its own errors are reported and recovery
773            // does not resume inside it and misread its closing `}`.
774            if matches!(self.peek().kind, TokenKind::LeftBrace) {
775                let _ = self.parse_outer_this_boundary(Self::parse_block);
776            }
777            return None;
778        }
779        self.advance();
780        let (return_type, type_predicate) =
781            self.parse_predicate_or_return_type(TypePos::Anywhere)?;
782
783        // A declaration nested in a method has no receiver of its own, and
784        // must not see the method's.
785        let body = self.parse_outer_this_boundary(Self::parse_block)?;
786        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
787            .span
788            .end;
789
790        parse_arena_result(
791            self.ast.try_push_stmt(Stmt {
792                kind: StmtKind::Function {
793                    name,
794                    generics,
795                    params,
796                    return_type,
797                    type_predicate,
798                    body,
799                    doc,
800                },
801                span: self.span(kw.span.start, body_end),
802            }),
803            &mut self.fatal,
804        )
805    }
806
807    fn parse_class_decl(&mut self) -> Option<StmtId> {
808        let doc = self.take_leading_doc();
809        let kw = self.advance();
810        // Classes are collected from the module's top level only; a nested one
811        // would otherwise be dropped unchecked.
812        if self.block_depth > 0 {
813            self.error_at_with_help(
814                kw.span,
815                "a `class` must be declared at the top level of the module",
816                vec!["move this `class` outside any block or function body".to_string()],
817            );
818        }
819        let name = self.parse_type_decl_name("a", "class")?;
820
821        let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
822            self.parse_generic_param_list()?
823        } else {
824            Vec::new()
825        };
826
827        let extends = if matches!(self.peek().kind, TokenKind::Extends) {
828            self.advance();
829            Some(self.parse_type_annotation()?)
830        } else {
831            None
832        };
833
834        let mut implements: Vec<crate::TypeAnnotation> = Vec::new();
835        if matches!(self.peek().kind, TokenKind::Implements) {
836            self.advance();
837            loop {
838                implements.push(self.parse_type_annotation()?);
839                if matches!(self.peek().kind, TokenKind::Comma) {
840                    self.advance();
841                    continue;
842                }
843                break;
844            }
845        }
846
847        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
848            self.error_at_peek("expected `{` after class header");
849            return None;
850        }
851        self.advance();
852
853        let mut members: Vec<crate::ClassMember> = Vec::new();
854        let mut member_failed = false;
855        // After a failed member, a body missing its own `}` may have taken the
856        // class's: a declaration or import there is the next top-level item, not a
857        // member, so the class stops and reports the missing `}`.
858        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
859            && !(member_failed && self.peek_starts_top_level_item())
860        {
861            // Stray `;` between members is accepted (TS allows it).
862            if matches!(self.peek().kind, TokenKind::Semicolon) {
863                self.advance();
864                continue;
865            }
866            match self.parse_class_member() {
867                Some(member) => members.push(member),
868                None if std::mem::take(&mut self.resume_at_next_member) => member_failed = true,
869                None => return None,
870            }
871        }
872
873        if !matches!(self.peek().kind, TokenKind::RightBrace) {
874            self.error_at_peek("expected `}` to close class");
875            return None;
876        }
877        let close = self.advance();
878        if member_failed {
879            return None;
880        }
881
882        parse_arena_result(
883            self.ast.try_push_stmt(Stmt {
884                kind: StmtKind::ClassDecl {
885                    name,
886                    generics,
887                    extends,
888                    implements,
889                    members,
890                    doc,
891                },
892                span: self.span(kw.span.start, close.span.end),
893            }),
894            &mut self.fatal,
895        )
896    }
897
898    fn parse_class_member(&mut self) -> Option<crate::ClassMember> {
899        let doc = self.take_leading_doc();
900        let member_start = self.peek().span.start;
901
902        // Excluded modifiers — reject before anything else, naming the fix.
903        if self.peek_identifier_text_is("protected") {
904            let tok = self.advance();
905            self.error_at_with_help(
906                tok.span,
907                "`protected` is not supported",
908                vec!["use `private` or `public`".to_string()],
909            );
910            return None;
911        }
912        if self.peek_identifier_text_is("abstract") {
913            let tok = self.advance();
914            self.error_at_with_help(
915                tok.span,
916                "abstract classes are not supported",
917                vec!["provide a concrete implementation instead of `abstract`".to_string()],
918            );
919            return None;
920        }
921
922        let modifiers = self.parse_class_modifiers();
923        self.reject_async_member_modifier();
924
925        // Getter/setter: `get`/`set` <name> ( … ). A member literally named `get`/`set`
926        // (followed directly by `(`) is an ordinary method and stays accepted.
927        for (kw, kind) in [
928            ("get", crate::AccessorKind::Get),
929            ("set", crate::AccessorKind::Set),
930        ] {
931            if self.peek_identifier_text_is(kw)
932                && is_property_name(&self.peek_at(1).kind)
933                && matches!(self.peek_at(2).kind, TokenKind::LeftParen)
934            {
935                if let Some(span) = modifiers.readonly {
936                    self.reject_readonly_modifier(span);
937                }
938                if let Some(span) = modifiers.static_span {
939                    self.error_at_with_help(
940                        span,
941                        "static accessors are not supported",
942                        vec!["use a static method: `static name(): T { … }`".to_string()],
943                    );
944                    return None;
945                }
946                return self.parse_accessor(member_start, modifiers, kind, doc);
947            }
948        }
949
950        // Constructor.
951        if self.peek_identifier_text_is("constructor")
952            && matches!(self.peek_at(1).kind, TokenKind::LeftParen)
953        {
954            if let Some(span) = modifiers.static_span {
955                self.error_at_with_help(
956                    span,
957                    "a constructor cannot be `static`",
958                    vec![
959                        "remove `static` — the constructor already belongs to the class, not \
960                         instances"
961                            .to_string(),
962                    ],
963                );
964                return None;
965            }
966            if let Some(span) = modifiers.readonly {
967                self.error_at_with_help(
968                    span,
969                    "a constructor cannot be `readonly`",
970                    vec!["remove `readonly`; it applies to fields".to_string()],
971                );
972                return None;
973            }
974            return self.parse_constructor(member_start, modifiers.visibility, doc);
975        }
976
977        let name = self.expect_property_ident("expected class member name")?;
978        let optional_span = if matches!(self.peek().kind, TokenKind::Question) {
979            Some(self.advance().span)
980        } else {
981            None
982        };
983        let optional = optional_span.is_some();
984
985        // Method.
986        if matches!(self.peek().kind, TokenKind::LessThan | TokenKind::LeftParen) {
987            if let Some(span) = modifiers.readonly {
988                self.reject_readonly_modifier(span);
989            }
990            let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
991                self.parse_generic_param_list()?
992            } else {
993                Vec::new()
994            };
995            if !matches!(self.peek().kind, TokenKind::LeftParen) {
996                self.error_at_peek("expected `(` to start the method parameter list");
997                return None;
998            }
999            self.advance();
1000            let params = self.parse_class_member_params(false)?;
1001            self.advance(); // `)` — parse_param_list left us on it
1002            if !matches!(self.peek().kind, TokenKind::Colon) {
1003                self.error_at_peek_with_help(
1004                    "expected `:` and return type",
1005                    vec![format!(
1006                        "write the return type: `{}(…): T {{ … }}`, with `void` when it returns nothing",
1007                        name.name
1008                    )],
1009                );
1010                // As for a function: read the body so recovery resumes after it.
1011                if matches!(self.peek().kind, TokenKind::LeftBrace)
1012                    && self.parse_class_member_body().is_some()
1013                {
1014                    self.resume_at_next_member = true;
1015                }
1016                return None;
1017            }
1018            self.advance();
1019            let return_type = self.parse_type_annotation()?;
1020            if self.at_inserted_semicolon() && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
1021            {
1022                self.advance();
1023            }
1024            if optional
1025                && matches!(
1026                    self.peek().kind,
1027                    TokenKind::Semicolon | TokenKind::RightBrace
1028                )
1029            {
1030                if !generics.is_empty() {
1031                    self.error_at(
1032                        name.span,
1033                        "generic optional method declarations are not supported",
1034                    );
1035                    return None;
1036                }
1037                let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
1038                    self.advance().span.end
1039                } else {
1040                    return_type.span.end
1041                };
1042                let ty = self.method_declaration_type(
1043                    params,
1044                    return_type,
1045                    self.span(name.span.start, end),
1046                )?;
1047                return Some(crate::ClassMember::Field {
1048                    name,
1049                    modifiers,
1050                    optional: true,
1051                    method_signature: true,
1052                    ty,
1053                    initializer: None,
1054                    span: self.span(member_start, end),
1055                    doc,
1056                });
1057            }
1058            let body = self.parse_class_member_body()?;
1059            let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1060                .span
1061                .end;
1062            return Some(crate::ClassMember::Method {
1063                name,
1064                optional,
1065                modifiers,
1066                generics,
1067                params,
1068                return_type,
1069                body,
1070                span: self.span(member_start, body_end),
1071                doc,
1072            });
1073        }
1074
1075        // Field.
1076        if let Some(span) = optional_span
1077            && modifiers.static_span.is_some()
1078        {
1079            self.error_at_with_help(
1080                span,
1081                "a static field cannot be optional",
1082                vec!["a static field must be initialized; give it a value with `= …`".to_string()],
1083            );
1084            return None;
1085        }
1086        if !matches!(self.peek().kind, TokenKind::Colon) {
1087            if matches!(self.peek().kind, TokenKind::Equals) {
1088                self.error_at_peek_with_help(
1089                    "class fields require a type annotation",
1090                    vec!["add `: T` before the initializer, e.g. `name: string = …`".to_string()],
1091                );
1092            } else {
1093                self.error_at_peek("expected `:` and a type for the class field");
1094            }
1095            return None;
1096        }
1097        self.advance();
1098        let ty = self.parse_type_annotation()?;
1099        let initializer = if matches!(self.peek().kind, TokenKind::Equals) {
1100            self.advance();
1101            // A field initializer may read `this` (the typechecker binds it); gate
1102            // it like a member body so `this.x` parses.
1103            self.class_member_body_depth += 1;
1104            let init = self.parse_expression();
1105            self.class_member_body_depth -= 1;
1106            Some(init?)
1107        } else {
1108            None
1109        };
1110        if !matches!(self.peek().kind, TokenKind::Semicolon) {
1111            self.error_at_peek("expected `;` after class field");
1112            return None;
1113        }
1114        let semi = self.advance();
1115        Some(crate::ClassMember::Field {
1116            name,
1117            modifiers,
1118            optional,
1119            method_signature: false,
1120            ty,
1121            initializer,
1122            span: self.span(member_start, semi.span.end),
1123            doc,
1124        })
1125    }
1126
1127    fn parse_constructor(
1128        &mut self,
1129        member_start: u32,
1130        visibility: crate::Visibility,
1131        doc: Option<crate::DocComment>,
1132    ) -> Option<crate::ClassMember> {
1133        self.advance(); // `constructor`
1134        self.advance(); // `(` — guaranteed by the caller
1135        let params = self.parse_class_member_params(true)?;
1136        self.advance(); // `)` — parse_param_list left us on it
1137        if matches!(self.peek().kind, TokenKind::Colon) {
1138            let colon = self.advance();
1139            let _ = self.parse_type_annotation();
1140            self.error_at_with_help(
1141                colon.span,
1142                "a constructor cannot declare a return type",
1143                vec![
1144                    "drop the `: T` — a constructor always returns the class instance".to_string(),
1145                ],
1146            );
1147        }
1148        let body = self.parse_class_member_body()?;
1149        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1150            .span
1151            .end;
1152        Some(crate::ClassMember::Constructor {
1153            visibility,
1154            params,
1155            body,
1156            span: self.span(member_start, body_end),
1157            doc,
1158        })
1159    }
1160
1161    /// `get name(): T { … }` / `set name(v: T) { … }`. The caller has confirmed the
1162    /// `get`/`set` keyword, a property name, and a `(` follow.
1163    fn parse_accessor(
1164        &mut self,
1165        member_start: u32,
1166        modifiers: crate::ClassModifiers,
1167        kind: crate::AccessorKind,
1168        doc: Option<crate::DocComment>,
1169    ) -> Option<crate::ClassMember> {
1170        self.advance(); // `get` / `set`
1171        let name = self.expect_property_ident("expected accessor name")?;
1172        self.advance(); // `(` — guaranteed by the caller
1173        let mut params = self.parse_class_member_params(false)?;
1174        self.advance(); // `)`
1175        let param = match kind {
1176            crate::AccessorKind::Get => {
1177                if !params.is_empty() {
1178                    self.error_at(name.span, "a getter cannot declare parameters");
1179                }
1180                None
1181            }
1182            crate::AccessorKind::Set => {
1183                if params.len() != 1 {
1184                    self.error_at(name.span, "a setter must declare exactly one parameter");
1185                    return None;
1186                }
1187                let param = params.remove(0);
1188                // An assignment always passes the value, so a setter's parameter
1189                // can't be omitted or collect several.
1190                if param.rest {
1191                    self.error_at(
1192                        param.name.span,
1193                        "a setter's parameter can't be a rest parameter",
1194                    );
1195                } else if param.default.is_some() {
1196                    self.error_at(name.span, "a setter's parameter can't have a default value");
1197                } else if param.optional {
1198                    self.error_at(param.name.span, "a setter's parameter can't be optional");
1199                }
1200                Some(Box::new(param))
1201            }
1202        };
1203        let return_type = if matches!(self.peek().kind, TokenKind::Colon) {
1204            self.advance();
1205            Some(self.parse_type_annotation()?)
1206        } else {
1207            // A getter's type is its return type, which, as for a method, is
1208            // written rather than inferred. The body still parses, so the
1209            // rest of the class is checked.
1210            if kind == crate::AccessorKind::Get {
1211                self.error_at_peek_with_help(
1212                    "expected `:` and return type",
1213                    vec!["get name(): T { … }".to_string()],
1214                );
1215            }
1216            None
1217        };
1218        let body = self.parse_class_member_body()?;
1219        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1220            .span
1221            .end;
1222        Some(crate::ClassMember::Accessor {
1223            name,
1224            modifiers,
1225            kind,
1226            param,
1227            return_type,
1228            body,
1229            span: self.span(member_start, body_end),
1230            doc,
1231        })
1232    }
1233
1234    /// `public`/`private`/`static`/`readonly`. Duplicate or conflicting modifiers are
1235    /// diagnosed but parsing continues so the member shape is still recovered.
1236    /// Visibility must precede `static` and `readonly` (the TypeScript order);
1237    /// `readonly` is accepted on either side of `static` — no semantic difference
1238    /// under our subset.
1239    fn parse_class_modifiers(&mut self) -> crate::ClassModifiers {
1240        let mut visibility = crate::Visibility::Public;
1241        let mut visibility_span: Option<Span> = None;
1242        let mut readonly: Option<Span> = None;
1243        let mut static_span: Option<Span> = None;
1244        loop {
1245            if self.peek_word_is_class_modifier("public")
1246                || self.peek_word_is_class_modifier("private")
1247            {
1248                let (kind, word) = if self.peek_identifier_text_is("private") {
1249                    (crate::Visibility::Private, "private")
1250                } else {
1251                    (crate::Visibility::Public, "public")
1252                };
1253                let tok = self.advance();
1254                if visibility_span.is_some() {
1255                    self.error_at_with_help(
1256                        tok.span,
1257                        "a class member may have at most one visibility modifier",
1258                        vec!["keep a single `public` or `private`".to_string()],
1259                    );
1260                    continue;
1261                }
1262                // TypeScript reports one misplaced modifier per member; `static` is
1263                // named first when both precede.
1264                let preceding = static_span
1265                    .map(|_| "static")
1266                    .or(readonly.map(|_| "readonly"));
1267                if let Some(preceding) = preceding {
1268                    self.error_at_with_help(
1269                        tok.span,
1270                        format!("`{word}` must come before `{preceding}`"),
1271                        vec![format!("write `{word} {preceding} <name>`")],
1272                    );
1273                }
1274                visibility = kind;
1275                visibility_span = Some(tok.span);
1276                continue;
1277            }
1278            if self.peek_word_is_class_modifier("static") {
1279                let tok = self.advance();
1280                if static_span.is_some() {
1281                    self.error_at(tok.span, "duplicate `static` modifier");
1282                } else {
1283                    static_span = Some(tok.span);
1284                }
1285                continue;
1286            }
1287            if self.peek_word_is_class_modifier("readonly") {
1288                let tok = self.advance();
1289                if readonly.is_some() {
1290                    self.error_at(tok.span, "duplicate `readonly` modifier");
1291                } else {
1292                    readonly = Some(tok.span);
1293                }
1294                continue;
1295            }
1296            break;
1297        }
1298        crate::ClassModifiers {
1299            visibility,
1300            visibility_span,
1301            readonly,
1302            static_span,
1303        }
1304    }
1305
1306    /// A contextual modifier keyword counts as a modifier only when another member token
1307    /// (the real name, or a further modifier) follows — otherwise the word is the member
1308    /// name itself (e.g. a field named `private`). Mirrors `eat_readonly_property_modifier`.
1309    /// As in TypeScript, only `static` may be followed by a line break; in a class body
1310    /// ASI already ends the member there, but a constructor parameter list has no ASI.
1311    fn peek_word_is_class_modifier(&self, word: &str) -> bool {
1312        if !self.peek_identifier_text_is(word) {
1313            return false;
1314        }
1315        if word != "static" && self.line_break_after_peek() {
1316            return false;
1317        }
1318        let next = &self.peek_at(1).kind;
1319        is_property_name(next) || matches!(next, TokenKind::StringLiteral(_))
1320    }
1321
1322    fn parse_class_member_params(
1323        &mut self,
1324        allow_param_properties: bool,
1325    ) -> Option<Vec<ParamDecl>> {
1326        let saved = self.class_member_body_depth;
1327        self.class_member_body_depth = 1;
1328        let params = self.parse_param_list(allow_param_properties);
1329        self.class_member_body_depth = saved;
1330        params
1331    }
1332
1333    fn parse_class_member_body(&mut self) -> Option<StmtId> {
1334        self.class_member_body_depth += 1;
1335        let body = self.parse_block();
1336        self.class_member_body_depth -= 1;
1337        body
1338    }
1339
1340    fn parse_interface_decl(&mut self) -> Option<StmtId> {
1341        let doc = self.take_leading_doc();
1342        let kw = self.advance();
1343        let name = self.parse_type_decl_name("an", "interface")?;
1344
1345        let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1346            self.parse_generic_param_list()?
1347        } else {
1348            Vec::new()
1349        };
1350
1351        let mut extends = Vec::new();
1352        if matches!(self.peek().kind, TokenKind::Extends) {
1353            self.advance();
1354            loop {
1355                extends.push(self.parse_type_annotation()?);
1356                if !matches!(self.peek().kind, TokenKind::Comma) {
1357                    break;
1358                }
1359                self.advance();
1360            }
1361        }
1362        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1363            self.error_at_peek("expected `{` after interface name");
1364            return None;
1365        }
1366        self.advance();
1367
1368        let mut members: Vec<crate::InterfaceMember> = Vec::new();
1369        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
1370            && !self.peek_starts_top_level_item()
1371        {
1372            // Call signatures `(params): ret;` are stored under the sentinel name `@call`.
1373            // The `@` prefix is not a valid identifier start, so collisions with user methods
1374            // are impossible.
1375            let member_doc = self.take_leading_doc();
1376            if matches!(self.peek().kind, TokenKind::LeftParen) {
1377                let open = self.advance();
1378                let params = self.parse_param_list(false)?;
1379                self.advance();
1380                if !matches!(self.peek().kind, TokenKind::Colon) {
1381                    self.error_at_peek("expected `:` and return type");
1382                    return None;
1383                }
1384                self.advance();
1385                let return_type = self.parse_type_annotation()?;
1386                let end = self.finish_interface_member(return_type.span.end)?;
1387                let span = self.span(open.span.start, end);
1388                let sentinel = Ident {
1389                    name: "@call".to_string(),
1390                    span: open.span,
1391                };
1392                members.push(crate::InterfaceMember::Method {
1393                    name: sentinel,
1394                    optional: false,
1395                    generics: Vec::new(),
1396                    params,
1397                    return_type,
1398                    span,
1399                    doc: member_doc,
1400                });
1401                continue;
1402            }
1403            if matches!(self.peek().kind, TokenKind::LessThan) {
1404                self.error_at_peek("generic call signatures are not yet supported");
1405                return None;
1406            }
1407            let member_start = self.peek().span.start;
1408            let readonly = self.eat_readonly_property_modifier();
1409            if self.peek_is_parameterless_index_signature() {
1410                self.skip_parameterless_index_signature(member_start)?;
1411                self.finish_interface_member(self.prev_token_end())?;
1412                continue;
1413            }
1414            if matches!(self.peek().kind, TokenKind::LeftBracket) {
1415                let signature = self.parse_index_signature(readonly)?;
1416                self.finish_interface_member(signature.span.end)?;
1417                members.push(crate::InterfaceMember::IndexSignature(signature));
1418                continue;
1419            }
1420            let member_name = self.expect_property_ident("expected interface member name")?;
1421            let optional = matches!(self.peek().kind, TokenKind::Question);
1422            if optional {
1423                self.advance();
1424            }
1425            if matches!(self.peek().kind, TokenKind::Colon) {
1426                self.advance();
1427                let ty = self.parse_type_annotation()?;
1428                let end = self.finish_interface_member(ty.span.end)?;
1429                let span = self.span(member_name.span.start, end);
1430                members.push(crate::InterfaceMember::Property {
1431                    name: member_name,
1432                    ty,
1433                    optional,
1434                    readonly,
1435                    span,
1436                    doc: member_doc,
1437                });
1438                continue;
1439            }
1440            if readonly {
1441                self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
1442            }
1443            let m_generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1444                self.parse_generic_param_list()?
1445            } else {
1446                Vec::new()
1447            };
1448            if !matches!(self.peek().kind, TokenKind::LeftParen) {
1449                self.error_at_peek(
1450                    "expected `(` to start a method signature or `:` to start a property",
1451                );
1452                return None;
1453            }
1454            self.advance();
1455            let params = self.parse_param_list(false)?;
1456            self.advance();
1457            if !matches!(self.peek().kind, TokenKind::Colon) {
1458                self.error_at_peek("expected `:` and return type");
1459                return None;
1460            }
1461            self.advance();
1462            let return_type = self.parse_type_annotation()?;
1463            let end = self.finish_interface_member(return_type.span.end)?;
1464            let span = self.span(member_name.span.start, end);
1465            if optional {
1466                if !m_generics.is_empty() {
1467                    self.error_at(
1468                        member_name.span,
1469                        "generic optional method declarations are not supported",
1470                    );
1471                    return None;
1472                }
1473                let ty = self.method_declaration_type(params, return_type, span)?;
1474                members.push(crate::InterfaceMember::Property {
1475                    name: member_name,
1476                    ty,
1477                    optional: true,
1478                    readonly,
1479                    span,
1480                    doc: member_doc,
1481                });
1482            } else {
1483                members.push(crate::InterfaceMember::Method {
1484                    name: member_name,
1485                    optional,
1486                    generics: m_generics,
1487                    params,
1488                    return_type,
1489                    span,
1490                    doc: member_doc,
1491                });
1492            }
1493        }
1494
1495        if !matches!(self.peek().kind, TokenKind::RightBrace) {
1496            self.error_at_peek("expected `}` to close interface");
1497            return None;
1498        }
1499        let close = self.advance();
1500
1501        parse_arena_result(
1502            self.ast.try_push_stmt(Stmt {
1503                kind: StmtKind::InterfaceDecl {
1504                    name,
1505                    generics,
1506                    extends,
1507                    members,
1508                    doc,
1509                },
1510                span: self.span(kw.span.start, close.span.end),
1511            }),
1512            &mut self.fatal,
1513        )
1514    }
1515
1516    fn method_declaration_type(
1517        &mut self,
1518        params: Vec<ParamDecl>,
1519        return_type: TypeAnnotation,
1520        span: Span,
1521    ) -> Option<TypeAnnotation> {
1522        let mut fields = Vec::new();
1523        for param in params {
1524            if param.pattern.is_some() || param.default.is_some() {
1525                self.error_at(
1526                    param.name.span,
1527                    "a method declaration cannot contain a parameter pattern or default value",
1528                );
1529                return None;
1530            }
1531            let Some(ty) = param.ty else {
1532                return self.invariant_failure("method parameter annotation was not retained");
1533            };
1534            fields.push(TypeAnnotationField {
1535                name: param.name,
1536                ty,
1537                optional: param.optional,
1538                readonly: false,
1539                rest: param.rest,
1540                method: false,
1541            });
1542        }
1543        Some(TypeAnnotation {
1544            kind: TypeAnnotationKind::Function {
1545                params: fields,
1546                return_type: Box::new(return_type),
1547            },
1548            span,
1549        })
1550    }
1551
1552    fn finish_interface_member(&mut self, body_end: u32) -> Option<u32> {
1553        self.finish_type_member(body_end, "expected `;`, `,`, or `}` after interface member")
1554    }
1555
1556    /// Consume the separator after an interface or type-literal member and return
1557    /// where the member ends. `;` and `,` are interchangeable and the last member's
1558    /// is optional. A line break also ends a member, as in TypeScript: ASI inserts
1559    /// `;` there, but keeps some tokens, such as `[` and `(`, attached. `body_end` is
1560    /// where the member ends when it carries no separator of its own.
1561    fn finish_type_member(&mut self, body_end: u32, message: &str) -> Option<u32> {
1562        match self.peek().kind {
1563            TokenKind::Semicolon | TokenKind::Comma => Some(self.advance().span.end),
1564            TokenKind::RightBrace => Some(body_end),
1565            _ if self.line_break_before_peek() => Some(body_end),
1566            _ => {
1567                self.error_at_peek(message);
1568                None
1569            }
1570        }
1571    }
1572
1573    fn parse_type_alias_decl(&mut self) -> Option<StmtId> {
1574        let doc = self.take_leading_doc();
1575        let kw = self.advance();
1576        let name = self.parse_type_decl_name("a", "type alias")?;
1577
1578        let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1579            self.parse_generic_param_list()?
1580        } else {
1581            Vec::new()
1582        };
1583
1584        if !matches!(self.peek().kind, TokenKind::Equals) {
1585            self.error_at_peek("expected `=` after type alias name");
1586            return None;
1587        }
1588        self.advance();
1589
1590        let ty = self.parse_type_annotation()?;
1591
1592        // ASI keeps a `[` on the next line attached, but the type already ended at
1593        // the line break (see `parse_type_array_inner`), so the break ends the alias.
1594        let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
1595            self.advance().span.end
1596        } else if self.line_break_before_peek() {
1597            self.prev_token_end()
1598        } else {
1599            self.error_at_peek("expected `;` after type alias body");
1600            return None;
1601        };
1602
1603        parse_arena_result(
1604            self.ast.try_push_stmt(Stmt {
1605                kind: StmtKind::TypeAliasDecl {
1606                    name,
1607                    generics,
1608                    ty,
1609                    doc,
1610                },
1611                span: self.span(kw.span.start, end),
1612            }),
1613            &mut self.fatal,
1614        )
1615    }
1616
1617    // Parser enforces kind-coherence: an enum cannot mix numeric and string initializers.
1618    fn parse_enum_decl(&mut self) -> Option<StmtId> {
1619        #[derive(Copy, Clone)]
1620        enum Kind {
1621            Number,
1622            String,
1623        }
1624        let doc = self.take_leading_doc();
1625        let kw = self.advance();
1626        let name = self.parse_type_decl_name("an", "enum")?;
1627
1628        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1629            self.error_at_peek("expected `{` after enum name");
1630            return None;
1631        }
1632        self.advance();
1633
1634        let mut members: Vec<EnumMember> = Vec::new();
1635        let mut first_explicit: Option<(Kind, Span)> = None;
1636        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
1637            // ASI inserts `;` after `,` on a newline — tolerate it so multi-line enums parse.
1638            if matches!(self.peek().kind, TokenKind::Semicolon) {
1639                self.advance();
1640                continue;
1641            }
1642            let member_doc = self.take_leading_doc();
1643            let member_name_tok = self.expect_identifier_name("expected enum member name")?;
1644            let member_name = self.ident_from_token(&member_name_tok);
1645
1646            let value = if matches!(self.peek().kind, TokenKind::Equals) {
1647                self.advance();
1648                let init = self.parse_enum_initializer()?;
1649                let (this_kind, this_span) = match &init {
1650                    EnumInitializer::Number { span, .. } => (Kind::Number, *span),
1651                    EnumInitializer::String { span, .. } => (Kind::String, *span),
1652                };
1653                match first_explicit {
1654                    None => first_explicit = Some((this_kind, this_span)),
1655                    Some((prev, _)) => {
1656                        let mismatched = matches!(
1657                            (prev, this_kind),
1658                            (Kind::Number, Kind::String) | (Kind::String, Kind::Number)
1659                        );
1660                        if mismatched {
1661                            self.error_at(
1662                                this_span,
1663                                "mixed numeric and string enum members are not allowed",
1664                            );
1665                        }
1666                    }
1667                }
1668                Some(init)
1669            } else {
1670                None
1671            };
1672
1673            let end_span = value
1674                .as_ref()
1675                .map_or(member_name.span, super::ast::EnumInitializer::span);
1676            let member_span = self.span(member_name.span.start, end_span.end);
1677            members.push(EnumMember {
1678                name: member_name,
1679                value,
1680                span: member_span,
1681                doc: member_doc,
1682            });
1683
1684            match self.peek().kind {
1685                // ASI supplies the `;` before a closing `}` and after a newline, so an
1686                // enum body sees one wherever a comma was omitted.
1687                TokenKind::Comma | TokenKind::Semicolon => {
1688                    self.advance();
1689                }
1690                TokenKind::RightBrace | TokenKind::Eof => {}
1691                _ => {
1692                    self.error_at_peek("expected `,` or `}` after enum member");
1693                    return None;
1694                }
1695            }
1696        }
1697
1698        if !matches!(self.peek().kind, TokenKind::RightBrace) {
1699            self.error_at_peek("expected `}` to close enum");
1700            return None;
1701        }
1702        let close = self.advance();
1703
1704        parse_arena_result(
1705            self.ast.try_push_stmt(Stmt {
1706                kind: StmtKind::EnumDecl { name, members, doc },
1707                span: self.span(kw.span.start, close.span.end),
1708            }),
1709            &mut self.fatal,
1710        )
1711    }
1712
1713    /// `export` on a top-level declaration. Two forms (multi-file.md §3):
1714    /// Form 1 marks a declaration (`export function …`) — the declaration is
1715    /// parsed normally and recorded in `exported_decls`; Form 2 is a re-export
1716    /// (`export { a, b as c } from "./util";` / `export { x };`) parsed into an
1717    /// `ExportFrom` node. `export` is top-level only; inside a function body it
1718    /// is a parse error. `export default` stays rejected.
1719    fn parse_export(&mut self) -> Option<StmtId> {
1720        let export_tok = self.advance();
1721        let export_span = export_tok.span;
1722
1723        let nested = self.block_depth > 0;
1724        if nested {
1725            self.error_at_with_help(
1726                export_span,
1727                "`export` statements must appear at the top of the file",
1728                vec!["move this `export` to the top level of the module".to_string()],
1729            );
1730            // Fall through to parse the declaration / list anyway so recovery
1731            // stays in sync, but return None below so it isn't recorded.
1732        }
1733
1734        // `export type { … }` — TS's type-only re-export, accepted as a no-op
1735        // synonym: exports carry both value and type spaces here.
1736        if self.peek_identifier_text_is("type")
1737            && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
1738        {
1739            self.advance();
1740        }
1741
1742        self.reject_async_modifier();
1743        let declares = matches!(
1744            self.peek().kind,
1745            TokenKind::Function
1746                | TokenKind::Class
1747                | TokenKind::Const
1748                | TokenKind::Let
1749                | TokenKind::Interface
1750                | TokenKind::Enum
1751        ) || self.at_type_alias_head();
1752        if declares {
1753            // Carry any doc comment that attached to `export` onto the
1754            // declaration token so the declaration parser still sees it.
1755            if export_tok.leading_doc.is_some() && self.peek().leading_doc.is_none() {
1756                if let Some(token) = self.tokens.get_mut(self.pos) {
1757                    token.leading_doc = export_tok.leading_doc;
1758                } else {
1759                    return self.invariant_failure("export cursor is outside token stream");
1760                }
1761            }
1762            let id = self.parse_statement()?;
1763            if nested {
1764                let detached: Vec<StmtId> = std::iter::once(id)
1765                    .chain(self.take_trailing_declarators())
1766                    .collect();
1767                self.discard_detached_patterns(&detached);
1768                return None;
1769            }
1770            // The caller places the trailing declarators; each is exported too.
1771            for stmt in std::iter::once(id).chain(self.trailing_declarators.clone()) {
1772                self.ast
1773                    .exported_decls
1774                    .push(ExportedDecl { stmt, export_span });
1775            }
1776            return Some(id);
1777        }
1778
1779        match self.peek().kind {
1780            TokenKind::LeftBrace => {
1781                // A doc comment attaches to the `export` keyword, which we've
1782                // already consumed — carry it onto the re-export node.
1783                let doc = export_tok
1784                    .leading_doc
1785                    .as_ref()
1786                    .and_then(|raw| self.parse_doc(raw))
1787                    .or_else(|| self.take_leading_doc());
1788                self.parse_export_from(export_span, nested, doc)
1789            }
1790            TokenKind::Default => {
1791                self.error_at_peek_with_help(
1792                    "`export default` is not supported",
1793                    vec![
1794                        "default exports are out of scope; drop `default` — a named \
1795                         `export` marks the declaration visible to sibling modules"
1796                            .to_string(),
1797                    ],
1798                );
1799                None
1800            }
1801            _ => {
1802                self.error_at_peek_with_help(
1803                    "expected a declaration after `export`",
1804                    vec![
1805                        "`export` may precede `function`, `class`, `const`, `let`, `type`, \
1806                         `interface`, or `enum`, or take the form `export { … }`"
1807                            .to_string(),
1808                    ],
1809                );
1810                None
1811            }
1812        }
1813    }
1814
1815    /// Parse Form 2 re-export: `export { a, b as c } from "./util";` or the
1816    /// bare `export { x };`. The leading `export` has already been consumed.
1817    fn parse_export_from(
1818        &mut self,
1819        export_span: Span,
1820        nested: bool,
1821        doc: Option<crate::DocComment>,
1822    ) -> Option<StmtId> {
1823        let (specs, open_span) = self.parse_specifier_list(SpecifierList::Export)?;
1824        if specs.is_empty() {
1825            self.error_at_with_help(
1826                open_span,
1827                "empty export specifier list",
1828                vec!["name what you re-export: `export { name } from \"./util\";`".to_string()],
1829            );
1830            return None;
1831        }
1832
1833        let source = if self.peek_identifier_text_is("from") {
1834            self.advance();
1835            let module_tok = self.peek().clone();
1836            let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1837                s.clone()
1838            } else {
1839                self.error_at_peek("expected module specifier string after `from`");
1840                return None;
1841            };
1842            self.advance();
1843            Some((module, module_tok.span))
1844        } else {
1845            None
1846        };
1847
1848        if !matches!(self.peek().kind, TokenKind::Semicolon) {
1849            self.error_at_peek("expected `;` after export statement");
1850            return None;
1851        }
1852        let semi = self.advance();
1853
1854        if nested {
1855            return None;
1856        }
1857
1858        parse_arena_result(
1859            self.ast.try_push_stmt(Stmt {
1860                kind: StmtKind::ExportFrom { specs, source, doc },
1861                span: self.span(export_span.start, semi.span.end),
1862            }),
1863            &mut self.fatal,
1864        )
1865    }
1866
1867    fn parse_import_decl(&mut self) -> Option<StmtId> {
1868        let doc = self.take_leading_doc();
1869        let kw = self.advance();
1870        let kw_span = kw.span;
1871
1872        let nested = self.block_depth > 0;
1873        if nested {
1874            self.error_at_with_help(
1875                kw_span,
1876                "`import` statements must appear at the top of the file",
1877                vec!["move this `import` outside any block / function body".to_string()],
1878            );
1879            // Continue parsing to consume the whole import so recovery isn't confused.
1880        }
1881
1882        let kind = self.parse_import_kind()?;
1883
1884        if !self.peek_identifier_text_is("from") {
1885            self.error_at_peek("expected `from` after import specifier list");
1886            return None;
1887        }
1888        self.advance();
1889
1890        let module_tok = self.peek().clone();
1891        let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1892            s.clone()
1893        } else {
1894            self.error_at_peek("expected module specifier string after `from`");
1895            return None;
1896        };
1897        let module_span = module_tok.span;
1898        self.advance();
1899
1900        if !matches!(self.peek().kind, TokenKind::Semicolon) {
1901            self.error_at_peek("expected `;` after import statement");
1902            return None;
1903        }
1904        let semi = self.advance();
1905
1906        if nested {
1907            return None;
1908        }
1909
1910        parse_arena_result(
1911            self.ast.try_push_stmt(Stmt {
1912                kind: StmtKind::Import {
1913                    module,
1914                    module_span,
1915                    kind,
1916                    doc,
1917                },
1918                span: self.span(kw_span.start, semi.span.end),
1919            }),
1920            &mut self.fatal,
1921        )
1922    }
1923
1924    fn parse_import_kind(&mut self) -> Option<ImportKind> {
1925        // `import type …` — TS's type-only import, accepted as a no-op synonym:
1926        // imports carry both value and type spaces here. `import type from "m"`
1927        // stays a namespace import named `type`, so `type` is a modifier only
1928        // when what follows can't be a complete import clause without it.
1929        if self.peek_identifier_text_is("type")
1930            && match self.peek_at(1).kind {
1931                TokenKind::LeftBrace | TokenKind::Star => true,
1932                TokenKind::Identifier => {
1933                    !(self.peek_at_is_word(1, "from")
1934                        && matches!(self.peek_at(2).kind, TokenKind::StringLiteral(_)))
1935                }
1936                _ => false,
1937            }
1938        {
1939            self.advance();
1940        }
1941
1942        match self.peek().kind {
1943            TokenKind::LeftBrace => self.parse_named_imports(),
1944            TokenKind::Identifier => {
1945                let local_tok = self.advance();
1946                let local_name = self.ident_from_token(&local_tok);
1947                if matches!(self.peek().kind, TokenKind::Comma) {
1948                    self.error_at_peek_with_help(
1949                        "combining default and named imports is not supported",
1950                        vec!["use two separate `import` statements".to_string()],
1951                    );
1952                    return None;
1953                }
1954                Some(ImportKind::Namespace { local_name })
1955            }
1956            // `import * as ns from "pkg"` — forgiveness synonym for `import ns from "pkg"`.
1957            // Both are identical since we have no default exports.
1958            TokenKind::Star => {
1959                self.advance();
1960                if !self.peek_identifier_text_is("as") {
1961                    self.error_at_peek_with_help(
1962                        "expected `as <name>` after `import *`",
1963                        vec![
1964                            "name the namespace binding: \
1965                             `import * as ns from \"pkg\";` (or use the \
1966                             short form: `import ns from \"pkg\";`)"
1967                                .to_string(),
1968                        ],
1969                    );
1970                    return None;
1971                }
1972                self.advance();
1973                let local_tok = self.expect_identifier("expected namespace name after `as`")?;
1974                let local_name = self.ident_from_token(&local_tok);
1975                Some(ImportKind::Namespace { local_name })
1976            }
1977            TokenKind::StringLiteral(_) => {
1978                self.error_at_peek_with_help(
1979                    "side-effect-only imports are not supported",
1980                    vec![
1981                        "name what you import: `import { name } from \"pkg\";` \
1982                         or `import ns from \"pkg\";`"
1983                            .to_string(),
1984                    ],
1985                );
1986                None
1987            }
1988            _ => {
1989                self.error_at_peek("expected `{` or namespace name after `import`");
1990                None
1991            }
1992        }
1993    }
1994
1995    fn parse_named_imports(&mut self) -> Option<ImportKind> {
1996        let (specs, open_span) = self.parse_specifier_list(SpecifierList::Import)?;
1997        if specs.is_empty() {
1998            self.error_at_with_help(
1999                open_span,
2000                "empty import specifier list",
2001                vec!["name what you import: `import { name } from \"pkg\";`".to_string()],
2002            );
2003            return None;
2004        }
2005        Some(ImportKind::Named(specs))
2006    }
2007
2008    /// Parse a `{ a, b as c }` specifier list shared by `import` and re-`export`.
2009    /// `list` says whether the specifiers bind local names (an import) and names the
2010    /// construct in diagnostics. Returns the specifiers (possibly
2011    /// empty — callers reject empty with construct-specific help) and the span of
2012    /// the opening brace.
2013    fn parse_specifier_list(
2014        &mut self,
2015        list: SpecifierList,
2016    ) -> Option<(Vec<ImportSpecifier>, Span)> {
2017        let what = list.noun();
2018        let open = self.advance();
2019        let mut specs: Vec<ImportSpecifier> = Vec::new();
2020        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
2021            // Inline type-only modifier (`{ type X }`), accepted as a no-op
2022            // synonym. `{ type as t }` instead names the binding `type`.
2023            if self.peek_identifier_text_is("type")
2024                && matches!(self.peek_at(1).kind, TokenKind::Identifier)
2025                && !self.peek_at_is_word(1, "as")
2026            {
2027                self.advance();
2028            }
2029            let imported_tok =
2030                self.expect_identifier_name(&format!("expected {what} specifier name"))?;
2031            let imported_name = self.ident_from_token(&imported_tok);
2032
2033            let binds_local = list == SpecifierList::Import;
2034            let local_name = if self.peek_identifier_text_is("as") {
2035                self.advance();
2036                let local_tok = self.expect_identifier_name("expected local name after `as`")?;
2037                if binds_local {
2038                    self.reject_strict_mode_reserved_word(&local_tok);
2039                }
2040                self.ident_from_token(&local_tok)
2041            } else {
2042                if binds_local {
2043                    self.reject_strict_mode_reserved_word(&imported_tok);
2044                }
2045                imported_name.clone()
2046            };
2047
2048            specs.push(ImportSpecifier {
2049                imported_name,
2050                local_name,
2051            });
2052
2053            match self.peek().kind {
2054                TokenKind::Comma => {
2055                    self.advance();
2056                }
2057                TokenKind::RightBrace | TokenKind::Eof => {}
2058                _ => {
2059                    self.error_at_peek(format!("expected `,` or `}}` after {what} specifier"));
2060                    return None;
2061                }
2062            }
2063        }
2064
2065        if !matches!(self.peek().kind, TokenKind::RightBrace) {
2066            self.error_at_peek(format!("expected `}}` to close {what} specifier list"));
2067            return None;
2068        }
2069        self.advance();
2070
2071        Some((specs, open.span))
2072    }
2073
2074    fn parse_enum_initializer(&mut self) -> Option<EnumInitializer> {
2075        let neg = if matches!(self.peek().kind, TokenKind::Minus) {
2076            Some(self.advance())
2077        } else {
2078            None
2079        };
2080        let tok = self.advance();
2081        match &tok.kind {
2082            TokenKind::NumberLiteral(n) => {
2083                let value = if neg.is_some() { -n } else { *n };
2084                // tsc constant-folds enum initializers, which turns `-0` into `0`.
2085                let value = if value == 0.0 { 0.0 } else { value };
2086                let start = neg.as_ref().map_or(tok.span.start, |t| t.span.start);
2087                Some(EnumInitializer::Number {
2088                    value,
2089                    span: self.span(start, tok.span.end),
2090                })
2091            }
2092            TokenKind::StringLiteral(s) => {
2093                if let Some(neg_tok) = &neg {
2094                    self.error_at(
2095                        self.span(neg_tok.span.start, tok.span.end),
2096                        "cannot negate a string enum initializer",
2097                    );
2098                    return None;
2099                }
2100                Some(EnumInitializer::String {
2101                    value: s.clone(),
2102                    span: tok.span,
2103                })
2104            }
2105            _ => {
2106                let span = neg.map_or(tok.span, |t| self.span(t.span.start, tok.span.end));
2107                self.error_at(
2108                    span,
2109                    "expected a number or string literal for enum initializer",
2110                );
2111                None
2112            }
2113        }
2114    }
2115
2116    /// Whether the token at `index` is `operator` (`keyof`, `readonly`) used as a
2117    /// prefix type operator.
2118    ///
2119    /// Both are contextual keywords — valid as ordinary identifiers in TypeScript and
2120    /// Submilli — so they are only operators when a type follows. A bare type name is
2121    /// never followed by another type name, so requiring one separates `keyof T` from
2122    /// a type literally named `keyof`.
2123    fn is_contextual_type_operator(&self, index: usize, operator: &str) -> bool {
2124        let Some(tok) = self.tokens.get(index) else {
2125            return false;
2126        };
2127        if !self.token_is_word(tok, operator) {
2128            return false;
2129        }
2130        matches!(
2131            self.tokens.get(index + 1).map(|t| &t.kind),
2132            Some(
2133                TokenKind::Identifier
2134                    | TokenKind::Void
2135                    | TokenKind::LeftBrace
2136                    | TokenKind::LeftParen
2137                    | TokenKind::LeftBracket
2138                    | TokenKind::StringLiteral(_)
2139                    | TokenKind::NumberLiteral(_)
2140                    | TokenKind::BooleanLiteral(_)
2141                    | TokenKind::NullLiteral
2142            )
2143        )
2144    }
2145
2146    /// The lexer already emits shifts as separate `>` tokens. Only `>=`
2147    /// needs splitting in a type context; keep `=` at the cursor for the caller.
2148    fn consume_type_close(&mut self) -> Option<Token> {
2149        if matches!(self.peek().kind, TokenKind::GreaterThan) {
2150            return Some(self.advance());
2151        }
2152        let original = self.peek().clone();
2153        if !matches!(original.kind, TokenKind::GreaterEquals) {
2154            return self.invariant_failure("type closing token must start with `>`");
2155        }
2156        let middle = original.span.start + 1;
2157        let close = Token::new(
2158            TokenKind::GreaterThan,
2159            self.span(original.span.start, middle),
2160        );
2161        let remaining = Token::new(TokenKind::Equals, self.span(middle, original.span.end));
2162        self.split_type_tokens.push((self.pos, original));
2163        *self.tokens.get_mut(self.pos)? = remaining;
2164        Some(close)
2165    }
2166
2167    fn restore_type_tokens(&mut self, checkpoint: usize) {
2168        for (index, original) in self.split_type_tokens.drain(checkpoint..).rev() {
2169            // Splitting replaces tokens in place; the token vector never shrinks.
2170            self.tokens[index] = original;
2171        }
2172    }
2173
2174    fn parse_type_argument_list(&mut self) -> Option<(Vec<TypeAnnotation>, u32)> {
2175        let lt = self.advance();
2176        if matches!(
2177            self.peek().kind,
2178            TokenKind::GreaterThan | TokenKind::GreaterEquals
2179        ) {
2180            self.error_at(lt.span, "empty generic argument list `<>` is not allowed");
2181            return None;
2182        }
2183        let mut args = Vec::new();
2184        loop {
2185            args.push(self.parse_type_annotation()?);
2186            match self.peek().kind {
2187                TokenKind::Comma => {
2188                    self.advance();
2189                    if matches!(
2190                        self.peek().kind,
2191                        TokenKind::GreaterThan | TokenKind::GreaterEquals
2192                    ) {
2193                        let gt = self.consume_type_close()?;
2194                        return Some((args, gt.span.end));
2195                    }
2196                }
2197                TokenKind::GreaterThan | TokenKind::GreaterEquals => {
2198                    let gt = self.consume_type_close()?;
2199                    return Some((args, gt.span.end));
2200                }
2201                _ => {
2202                    self.error_at(
2203                        lt.span,
2204                        "expected `,` or `>` to close generic argument list",
2205                    );
2206                    return None;
2207                }
2208            }
2209        }
2210    }
2211
2212    /// The name a `class`, `interface`, `type` or `enum` declares, `noun` naming
2213    /// which. A built-in type's name is reported, as in TypeScript: `class number {}`
2214    /// would read as the built-in in some positions and the class in others.
2215    fn parse_type_decl_name(&mut self, article: &str, noun: &str) -> Option<Ident> {
2216        let name_tok = self.expect_identifier(&format!("expected {noun} name"))?;
2217        let name = self.ident_from_token(&name_tok);
2218        if BUILT_IN_TYPE_NAMES.contains(&name.name.as_str()) {
2219            self.error_at_with_help(
2220                name.span,
2221                format!(
2222                    "`{}` is a built-in type and can't be used as {article} {noun} name",
2223                    name.name
2224                ),
2225                vec![format!("rename the {noun}")],
2226            );
2227        }
2228        Some(name)
2229    }
2230
2231    fn parse_generic_param_list(&mut self) -> Option<Vec<crate::TypeParameterAnnotation>> {
2232        let lt = self.advance();
2233        if matches!(
2234            self.peek().kind,
2235            TokenKind::GreaterThan | TokenKind::GreaterEquals
2236        ) {
2237            self.error_at(lt.span, "empty generic parameter list `<>` is not allowed");
2238            return None;
2239        }
2240        let mut generics = Vec::new();
2241        loop {
2242            let name_tok = self.expect_identifier("expected generic parameter name")?;
2243            let name = self.ident_from_token(&name_tok);
2244            let constraint = if matches!(self.peek().kind, TokenKind::Extends) {
2245                self.advance();
2246                Some(self.parse_type_annotation()?)
2247            } else {
2248                None
2249            };
2250            if matches!(self.peek().kind, TokenKind::Equals) {
2251                let equals = self.advance();
2252                self.error_at_with_help(equals.span, "generic type parameter defaults are not supported", vec!["remove the default and supply a type argument explicitly when inference cannot determine it".into()]);
2253                self.parse_type_annotation()?;
2254            }
2255            // A duplicate is reported and dropped, as `tsc` drops it: the first
2256            // parameter keeps the name, and the type arguments count against the
2257            // parameters that remain.
2258            if generics
2259                .iter()
2260                .any(|g: &crate::TypeParameterAnnotation| g.name == name.name)
2261            {
2262                self.error_at_with_help(
2263                    name.span,
2264                    format!("duplicate type parameter `{}`", name.name),
2265                    vec!["give each type parameter its own name".to_string()],
2266                );
2267            } else {
2268                generics.push(crate::TypeParameterAnnotation {
2269                    ident: name,
2270                    constraint,
2271                });
2272            }
2273            match self.peek().kind {
2274                TokenKind::Comma => {
2275                    self.advance();
2276                    if matches!(
2277                        self.peek().kind,
2278                        TokenKind::GreaterThan | TokenKind::GreaterEquals
2279                    ) {
2280                        self.consume_type_close()?;
2281                        return Some(generics);
2282                    }
2283                }
2284                TokenKind::GreaterThan | TokenKind::GreaterEquals => {
2285                    self.consume_type_close()?;
2286                    return Some(generics);
2287                }
2288                _ => {
2289                    self.error_at_peek("expected `,` or `>`");
2290                    return None;
2291                }
2292            }
2293        }
2294    }
2295
2296    fn parse_param_list(&mut self, allow_param_properties: bool) -> Option<Vec<ParamDecl>> {
2297        if matches!(self.peek().kind, TokenKind::RightParen) {
2298            return Some(Vec::new());
2299        }
2300        let mut params = Vec::new();
2301        loop {
2302            let param = self.parse_param(allow_param_properties)?;
2303            let is_rest = param.rest;
2304            params.push(param);
2305            match self.peek().kind {
2306                TokenKind::Comma => {
2307                    if is_rest {
2308                        let comma = self.advance();
2309                        self.error_at(comma.span, "rest parameter must be the last parameter");
2310                        return None;
2311                    }
2312                    self.advance();
2313                    if matches!(self.peek().kind, TokenKind::RightParen) {
2314                        break;
2315                    }
2316                }
2317                TokenKind::RightParen => break,
2318                _ => {
2319                    self.error_at_peek("expected `,` or `)`");
2320                    return None;
2321                }
2322            }
2323        }
2324        self.check_parameter_order(params.iter().map(ParamOrder::of_param));
2325        Some(params)
2326    }
2327
2328    fn parse_param(&mut self, allow_param_properties: bool) -> Option<ParamDecl> {
2329        // Parameter properties: `constructor(public x: T)` declares + assigns a
2330        // field. A modifier keyword counts only when a parameter name follows it
2331        // (so a param literally named `public`/`readonly` still parses).
2332        let modifiers = if self.peek_word_is_class_modifier("public")
2333            || self.peek_word_is_class_modifier("private")
2334            || self.peek_word_is_class_modifier("readonly")
2335        {
2336            let span = self.peek().span;
2337            let parsed = self.parse_class_modifiers();
2338            if allow_param_properties {
2339                Some(parsed)
2340            } else {
2341                self.error_at_with_help(
2342                    span,
2343                    "parameter properties are only allowed in a constructor",
2344                    vec![
2345                        "move the `public`/`private`/`readonly` modifier to a constructor parameter"
2346                            .to_string(),
2347                    ],
2348                );
2349                None
2350            }
2351        } else {
2352            None
2353        };
2354
2355        let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
2356            self.advance();
2357            true
2358        } else {
2359            false
2360        };
2361        if modifiers.is_some() && rest {
2362            self.error_at_peek("a parameter property cannot be a rest parameter");
2363            return None;
2364        }
2365
2366        // Placeholder `name` is the empty identifier at the pattern's span;
2367        // the pre-infer lowering pass replaces it with a fresh `#pattern_p_N`.
2368        let (name, pattern) = match self.peek().kind {
2369            TokenKind::LeftBrace | TokenKind::LeftBracket => {
2370                if rest {
2371                    self.error_at_peek("rest parameter cannot be destructured");
2372                    return None;
2373                }
2374                if modifiers.is_some() {
2375                    self.error_at_peek("a parameter property cannot be destructured");
2376                    return None;
2377                }
2378                let binding = self.parse_binding()?;
2379                let placeholder = Ident {
2380                    name: String::new(),
2381                    span: binding.span(),
2382                };
2383                (placeholder, Some(binding))
2384            }
2385            _ => {
2386                let name_tok = self.expect_binding_identifier(if rest {
2387                    "expected identifier after `...`"
2388                } else {
2389                    "expected parameter name"
2390                })?;
2391                let name = self.ident_from_token(&name_tok);
2392                (name, None)
2393            }
2394        };
2395
2396        let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
2397        let ty = if matches!(self.peek().kind, TokenKind::Colon) {
2398            self.advance();
2399            Some(self.parse_type_annotation()?)
2400        } else if !rest && matches!(self.peek().kind, TokenKind::Equals) {
2401            None
2402        } else {
2403            if rest {
2404                self.error_at_peek_with_help(
2405                    "rest parameter requires a type annotation",
2406                    vec!["function name(...args: T[]): R { … }".to_string()],
2407                );
2408            } else {
2409                self.error_at_peek_with_help(
2410                    "parameter requires a type annotation",
2411                    vec!["add a type (`x: T`) or a default initializer (`x = value`)".to_string()],
2412                );
2413            }
2414            return None;
2415        };
2416        let default = self.parse_parameter_default(rest, optional)?;
2417        Some(ParamDecl {
2418            name,
2419            ty,
2420            default,
2421            optional,
2422            pattern,
2423            rest,
2424            modifiers,
2425        })
2426    }
2427
2428    fn parse_parameter_optional(&mut self, rest: bool, pattern: bool) -> Option<bool> {
2429        if !matches!(self.peek().kind, TokenKind::Question) {
2430            return Some(false);
2431        }
2432        let question = self.advance();
2433        if rest || pattern {
2434            self.error_at(
2435                question.span,
2436                if rest {
2437                    "a rest parameter cannot be optional"
2438                } else {
2439                    "a destructured parameter cannot be optional; use a default value"
2440                },
2441            );
2442            return None;
2443        }
2444        Some(true)
2445    }
2446
2447    /// The `= value` after a parameter, if any. An optional parameter's default is
2448    /// reported but still parsed, so the rest of the list is checked too.
2449    fn parse_parameter_default(&mut self, rest: bool, optional: bool) -> Option<Option<ExprId>> {
2450        if !matches!(self.peek().kind, TokenKind::Equals) {
2451            return Some(None);
2452        }
2453        if rest {
2454            self.error_rest_parameter_default();
2455            return None;
2456        }
2457        if optional {
2458            self.error_at_peek_with_help(
2459                "an optional parameter cannot have a default value",
2460                vec!["drop the `?`: a parameter with a default is already optional".to_string()],
2461            );
2462        }
2463        self.advance();
2464        Some(Some(self.parse_expression()?))
2465    }
2466
2467    /// Rest's default is the empty array, so an explicit one is rejected.
2468    fn error_rest_parameter_default(&mut self) {
2469        self.error_at_peek_with_help(
2470            "rest parameter cannot have a default value",
2471            vec!["omit the `= …`; an unspecified rest defaults to `[]`".to_string()],
2472        );
2473    }
2474
2475    /// A caller cannot skip an optional argument and still pass a later one, so a
2476    /// required parameter after an optional one is reported. Only the first is, and
2477    /// parsing continues.
2478    fn check_parameter_order<'p>(&mut self, params: impl IntoIterator<Item = ParamOrder<'p>>) {
2479        let mut follows_optional = false;
2480        for param in params {
2481            if param.optional {
2482                follows_optional = true;
2483                continue;
2484            }
2485            if !follows_optional || !param.required {
2486                continue;
2487            }
2488            let label = match param.name {
2489                Some(name) => format!("parameter `{name}`"),
2490                None => "destructured parameter".to_string(),
2491            };
2492            self.error_at_with_help(
2493                param.span,
2494                format!("required {label} cannot follow an optional parameter"),
2495                vec!["make it optional too (`x?: T`), give it a default (`x: T = …`), or move it before the optional parameters".to_string()],
2496            );
2497            return;
2498        }
2499    }
2500
2501    /// Drop a `;` that ASI inserted directly before `next`, where the grammar requires
2502    /// `next` and so no statement can have ended. ASI is a token-stream pre-pass with no
2503    /// parser feedback, so it cannot see that a `}` ends a `do` body rather than a
2504    /// statement, or that the `{` after a signature's return annotation opens its body.
2505    fn eat_asi_semicolon_before(&mut self, next: TokenKind) {
2506        if matches!(self.peek().kind, TokenKind::Semicolon) && self.peek_at(1).kind == next {
2507            self.advance();
2508        }
2509    }
2510
2511    fn parse_block(&mut self) -> Option<StmtId> {
2512        self.eat_asi_semicolon_before(TokenKind::LeftBrace);
2513        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
2514            self.error_at_peek("expected `{`");
2515            return None;
2516        }
2517        let open = self.advance();
2518        self.block_depth += 1;
2519
2520        let mut stmts = Vec::new();
2521        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
2522            if matches!(self.peek().kind, TokenKind::Semicolon) {
2523                self.advance();
2524                continue;
2525            }
2526            let pos_before = self.pos;
2527            if let Some(id) = self.parse_statement() {
2528                stmts.push(id);
2529                stmts.extend(self.take_trailing_declarators());
2530            } else {
2531                self.recover();
2532                if self.pos == pos_before && !self.is_at_eof() {
2533                    self.advance();
2534                }
2535            }
2536        }
2537
2538        self.block_depth -= 1;
2539        if !matches!(self.peek().kind, TokenKind::RightBrace) {
2540            self.error_at_peek("expected `}`");
2541            return None;
2542        }
2543        let close = self.advance();
2544
2545        let span = self.span(open.span.start, close.span.end);
2546        parse_arena_result(
2547            self.ast.try_push_stmt(Stmt {
2548                kind: StmtKind::Block(stmts),
2549                span,
2550            }),
2551            &mut self.fatal,
2552        )
2553    }
2554
2555    // Control-flow bodies accept either a braced block or a single statement;
2556    // `if (c) return x;` is identical to `if (c) { return x; }` under our type
2557    // system (forgiveness principle). Downstream passes assume a `Block`, so a
2558    // braceless statement is wrapped in one.
2559    fn parse_control_body(&mut self) -> Option<StmtId> {
2560        if matches!(self.peek().kind, TokenKind::LeftBrace) {
2561            return self.parse_block();
2562        }
2563        // A binding here would go out of scope as soon as it was made. TypeScript
2564        // or JavaScript rejects each of these, so it is reported and then parsed
2565        // as usual. Only a declaration is reported: a `let` followed by
2566        // anything else already fails to parse.
2567        let keyword = match self.peek().kind {
2568            TokenKind::Let => Some(("a", "let")),
2569            TokenKind::Const => Some(("a", "const")),
2570            TokenKind::Function => Some(("a", "function")),
2571            // A nested `class` is reported wherever it is: see `parse_class_decl`.
2572            TokenKind::Interface => Some(("an", "interface")),
2573            TokenKind::Enum => Some(("an", "enum")),
2574            _ if self.peek_identifier_text_is("type") => Some(("a", "type")),
2575            _ => None,
2576        };
2577        // After `let`/`const`, a bracket or brace starts a destructuring
2578        // pattern; after `type`, which is also an ordinary identifier, it is an
2579        // index or nothing a declaration can be.
2580        let declares = match self.peek_at(1).kind {
2581            TokenKind::Identifier => true,
2582            TokenKind::LeftBracket | TokenKind::LeftBrace => !matches!(keyword, Some((_, "type"))),
2583            _ => false,
2584        };
2585        if let Some((article, keyword)) = keyword
2586            && declares
2587        {
2588            self.error_at_peek_with_help(
2589                format!(
2590                    "{article} `{keyword}` declaration can't be the body of a statement without braces"
2591                ),
2592                vec![format!("wrap it in braces: `{{ {keyword} … }}`")],
2593            );
2594        }
2595        self.block_depth += 1;
2596        let stmt = self.parse_statement();
2597        self.block_depth -= 1;
2598        let stmt = stmt?;
2599        let mut stmts = vec![stmt];
2600        stmts.extend(self.take_trailing_declarators());
2601        let first = parse_arena_result(self.ast.try_stmt(stmt), &mut self.fatal)?.span;
2602        let last_id = stmts.last().copied().unwrap_or(stmt);
2603        let last = parse_arena_result(self.ast.try_stmt(last_id), &mut self.fatal)?.span;
2604        let span = self.span(first.start, last.end);
2605        parse_arena_result(
2606            self.ast.try_push_stmt(Stmt {
2607                kind: StmtKind::Block(stmts),
2608                span,
2609            }),
2610            &mut self.fatal,
2611        )
2612    }
2613
2614    /// A loop body may also be a lone `;`: `while (advance());` does all its work in
2615    /// the header. Branches do not accept it; see `parse_branch_body`.
2616    fn parse_loop_body(&mut self) -> Option<StmtId> {
2617        if !matches!(self.peek().kind, TokenKind::Semicolon) {
2618            return self.parse_control_body();
2619        }
2620        self.empty_body_at_semicolon()
2621    }
2622
2623    /// An `if` or `else` body. Unlike a loop, a branch cannot be a lone `;`: `if (c);`
2624    /// silently detaches the block meant to follow it. The `;` is reported, then
2625    /// consumed as an empty body so an `else` after it doesn't cascade into a second error.
2626    fn parse_branch_body(&mut self, keyword: &str) -> Option<StmtId> {
2627        if !matches!(self.peek().kind, TokenKind::Semicolon) {
2628            return self.parse_control_body();
2629        }
2630        self.error_at_peek_with_help(
2631            format!("`{keyword}` has an empty body"),
2632            vec![format!("remove the `;` after `{keyword}`")],
2633        );
2634        self.empty_body_at_semicolon()
2635    }
2636
2637    /// Consumes a lone `;` as an empty block spanning it.
2638    fn empty_body_at_semicolon(&mut self) -> Option<StmtId> {
2639        let semi = self.advance();
2640        parse_arena_result(
2641            self.ast.try_push_stmt(Stmt {
2642                kind: StmtKind::Block(vec![]),
2643                span: semi.span,
2644            }),
2645            &mut self.fatal,
2646        )
2647    }
2648
2649    fn parse_if(&mut self) -> Option<StmtId> {
2650        self.with_recursion_limit(Self::parse_if_inner)
2651    }
2652
2653    fn parse_if_inner(&mut self) -> Option<StmtId> {
2654        let kw = self.advance();
2655        let condition = self.parse_paren_condition()?;
2656        let then_block = self.parse_branch_body("if")?;
2657
2658        let else_block = if matches!(self.peek().kind, TokenKind::Else) {
2659            self.advance();
2660            if matches!(self.peek().kind, TokenKind::If) {
2661                Some(self.parse_if()?)
2662            } else {
2663                Some(self.parse_branch_body("else")?)
2664            }
2665        } else {
2666            None
2667        };
2668
2669        let end = match else_block {
2670            Some(id) => {
2671                parse_arena_result(self.ast.try_stmt(id), &mut self.fatal)?
2672                    .span
2673                    .end
2674            }
2675            None => {
2676                parse_arena_result(self.ast.try_stmt(then_block), &mut self.fatal)?
2677                    .span
2678                    .end
2679            }
2680        };
2681
2682        parse_arena_result(
2683            self.ast.try_push_stmt(Stmt {
2684                kind: StmtKind::If {
2685                    condition,
2686                    then_block,
2687                    else_block,
2688                },
2689                span: self.span(kw.span.start, end),
2690            }),
2691            &mut self.fatal,
2692        )
2693    }
2694
2695    fn parse_while(&mut self) -> Option<StmtId> {
2696        let kw = self.advance();
2697        let condition = self.parse_paren_condition()?;
2698        let body = self.parse_loop_body()?;
2699        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2700            .span
2701            .end;
2702
2703        parse_arena_result(
2704            self.ast.try_push_stmt(Stmt {
2705                kind: StmtKind::While { condition, body },
2706                span: self.span(kw.span.start, body_end),
2707            }),
2708            &mut self.fatal,
2709        )
2710    }
2711
2712    // Disambiguates C-style for, for-in and for-of via non-consuming lookahead.
2713    fn parse_for(&mut self) -> Option<StmtId> {
2714        let kw = self.advance();
2715        // `for await (…)`: there are no promises to wait for, so name the
2716        // rewrite and read the loop as a plain `for`.
2717        if self.peek_identifier_text_is("await")
2718            && matches!(self.peek_at(1).kind, TokenKind::LeftParen)
2719        {
2720            let tok = self.advance();
2721            self.error_at_with_help(
2722                tok.span,
2723                "`for await` is not supported",
2724                vec![
2725                    "calls return their values directly: remove `await` and write \
2726                     `for (const e of …)`"
2727                        .to_string(),
2728                ],
2729            );
2730        }
2731        if !matches!(self.peek().kind, TokenKind::LeftParen) {
2732            self.error_at_peek("expected `(` after `for`");
2733            return None;
2734        }
2735        self.advance();
2736
2737        if let Some(in_at) = self.scan_for_in_head() {
2738            return self.parse_for_in_tail(kw, in_at);
2739        }
2740        if self.scan_for_of_shape() {
2741            self.parse_for_of_tail(kw)
2742        } else {
2743            self.parse_c_for_tail(kw)
2744        }
2745    }
2746
2747    /// The `in` token after a name or pattern, optionally preceded by `let` or
2748    /// `const`. Recognize patterns too so their rejection names the loop syntax.
2749    fn scan_for_in_head(&self) -> Option<usize> {
2750        let binding_at = self.pos
2751            + usize::from(matches!(
2752                self.peek().kind,
2753                TokenKind::Let | TokenKind::Const
2754            ));
2755        let in_at = match self.tokens.get(binding_at)?.kind {
2756            TokenKind::Identifier => binding_at + 1,
2757            TokenKind::LeftBracket => self.scan_past_balanced(
2758                binding_at,
2759                |k| matches!(k, TokenKind::LeftBracket),
2760                |k| matches!(k, TokenKind::RightBracket),
2761            )?,
2762            TokenKind::LeftBrace => self.scan_past_balanced(
2763                binding_at,
2764                |k| matches!(k, TokenKind::LeftBrace),
2765                |k| matches!(k, TokenKind::RightBrace),
2766            )?,
2767            _ => return None,
2768        };
2769        matches!(self.tokens.get(in_at)?.kind, TokenKind::In).then_some(in_at)
2770    }
2771
2772    /// `for (k in obj)` iterates the keys of `obj` that `Object.keys` lists, and
2773    /// none when `obj` is nullish. It becomes a `for-of` over those keys, with
2774    /// `obj` held first so it runs once. A loop variable declared outside the
2775    /// loop receives each key from a fresh binding the loop declares.
2776    fn parse_for_in_tail(&mut self, kw: Token, in_at: usize) -> Option<StmtId> {
2777        let binding_kind = match self.peek().kind {
2778            TokenKind::Let => Some(crate::BindingKind::Let),
2779            TokenKind::Const => Some(crate::BindingKind::Const),
2780            _ => None,
2781        };
2782        if binding_kind.is_some() {
2783            self.advance();
2784        }
2785        let name = if matches!(
2786            self.peek().kind,
2787            TokenKind::LeftBracket | TokenKind::LeftBrace
2788        ) {
2789            self.error_at_peek_with_help(
2790                "the left-hand side of `for…in` cannot be a destructuring pattern",
2791                vec![
2792                    "use `for (const k in obj)` and read each value with `obj[k]`".to_string(),
2793                    "or use `for (const [k, v] of Object.entries(obj))` for key-value pairs"
2794                        .to_string(),
2795                ],
2796            );
2797            self.pos = in_at;
2798            None
2799        } else {
2800            let name_tok = self.advance();
2801            Some(self.ident_from_token(&name_tok))
2802        };
2803        self.advance();
2804
2805        let object = self.parse_expression()?;
2806        if !matches!(self.peek().kind, TokenKind::RightParen) {
2807            self.error_at_peek("expected `)` after `for…in` object");
2808            return None;
2809        }
2810        self.advance();
2811        let body = self.parse_loop_body()?;
2812        let body_span = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?.span;
2813        let loop_span = self.span(kw.span.start, body_span.end);
2814
2815        let Some(name) = name else {
2816            // The diagnostic prevents compilation. Consume the whole invalid loop
2817            // so recovery preserves sibling heads and enclosing braces, but do not
2818            // infer its body without the rejected bindings. Keep the original body
2819            // block in the arena so its destructuring declarations are lowered.
2820            return parse_arena_result(
2821                self.ast.try_push_stmt(Stmt {
2822                    kind: StmtKind::Block(Vec::new()),
2823                    span: loop_span,
2824                }),
2825                &mut self.fatal,
2826            );
2827        };
2828
2829        let held = self.hold_for_in_object(object)?;
2830        let keys = self.for_in_keys(&held)?;
2831        let (binding_kind, binding, body) = if let Some(kind) = binding_kind {
2832            (kind, name, body)
2833        } else {
2834            let (key, body) = self.copy_key_to_outer(name, body, body_span)?;
2835            (crate::BindingKind::Const, key, body)
2836        };
2837        let for_of = parse_arena_result(
2838            self.ast.try_push_stmt(Stmt {
2839                kind: StmtKind::ForOf {
2840                    binding_kind,
2841                    name: binding,
2842                    ty: None,
2843                    iter: keys,
2844                    body,
2845                },
2846                span: loop_span,
2847            }),
2848            &mut self.fatal,
2849        )?;
2850        parse_arena_result(
2851            self.ast.try_push_stmt(Stmt {
2852                kind: StmtKind::Block(vec![held.statement, for_of]),
2853                span: loop_span,
2854            }),
2855            &mut self.fatal,
2856        )
2857    }
2858
2859    /// For `for (k in obj)` with `k` declared outside the loop: the loop binds a
2860    /// fresh key, and its body starts by copying that key into `k`. Nested
2861    /// loops share the key's name: each copies before its body runs.
2862    fn copy_key_to_outer(
2863        &mut self,
2864        outer: Ident,
2865        body: StmtId,
2866        body_span: Span,
2867    ) -> Option<(Ident, StmtId)> {
2868        let span = outer.span;
2869        let key = Ident {
2870            name: FOR_IN_KEY.to_string(),
2871            span,
2872        };
2873        let key_expr = self.synthetic_expr(span, ExprKind::Identifier(key.clone()))?;
2874        let copy = parse_arena_result(
2875            self.ast.try_push_stmt(Stmt {
2876                kind: StmtKind::Assign {
2877                    target: outer,
2878                    value: key_expr,
2879                },
2880                span,
2881            }),
2882            &mut self.fatal,
2883        )?;
2884        let body = parse_arena_result(
2885            self.ast.try_push_stmt(Stmt {
2886                kind: StmtKind::Block(vec![copy, body]),
2887                span: body_span,
2888            }),
2889            &mut self.fatal,
2890        )?;
2891        Some((key, body))
2892    }
2893
2894    /// `let #for_in_object = object;`, so the guard and `Object.keys` read one
2895    /// evaluation. It is a `let`, never reassigned: a `const` alias of a
2896    /// type-parameter value breaks the guard's narrowing (SUB-2171). `#` cannot
2897    /// start a source identifier.
2898    fn hold_for_in_object(&mut self, object: ExprId) -> Option<HeldForInObject> {
2899        let span = parse_arena_result(self.ast.try_expr(object), &mut self.fatal)?.span;
2900        let name = Ident {
2901            name: FOR_IN_OBJECT.to_string(),
2902            span,
2903        };
2904        let statement = parse_arena_result(
2905            self.ast.try_push_stmt(Stmt {
2906                kind: StmtKind::Let {
2907                    name: name.clone(),
2908                    ty: None,
2909                    value: object,
2910                    doc: None,
2911                },
2912                span,
2913            }),
2914            &mut self.fatal,
2915        )?;
2916        Some(HeldForInObject { name, statement })
2917    }
2918
2919    /// `held === undefined || held === null ? [] : Object.keys(held)`, which
2920    /// puts `held`'s span on every synthesized node so a diagnostic about it
2921    /// points at the `for…in` object.
2922    fn for_in_keys(&mut self, held: &HeldForInObject) -> Option<ExprId> {
2923        let span = held.name.span;
2924        let undefined = self.synthetic_expr(
2925            span,
2926            ExprKind::Identifier(Ident {
2927                name: "undefined".to_string(),
2928                span,
2929            }),
2930        )?;
2931        self.ast.synthetic_undefined.insert(undefined);
2932        let null = self.synthetic_expr(span, ExprKind::Null)?;
2933        let is_undefined = self.held_equals(held, undefined)?;
2934        let is_null = self.held_equals(held, null)?;
2935        let cond = self.synthetic_expr(
2936            span,
2937            ExprKind::Binary {
2938                op: BinOp::Or,
2939                lhs: is_undefined,
2940                rhs: is_null,
2941            },
2942        )?;
2943        let none = self.synthetic_expr(span, ExprKind::ArrayLiteral { elements: vec![] })?;
2944        let namespace = self.synthetic_expr(
2945            span,
2946            ExprKind::Identifier(Ident {
2947                name: "Object".to_string(),
2948                span,
2949            }),
2950        )?;
2951        let callee = self.synthetic_expr(
2952            span,
2953            ExprKind::FieldAccess {
2954                receiver: namespace,
2955                name: Ident {
2956                    name: "keys".to_string(),
2957                    span,
2958                },
2959            },
2960        )?;
2961        let argument = self.synthetic_expr(span, ExprKind::Identifier(held.name.clone()))?;
2962        let keys = self.synthetic_expr(
2963            span,
2964            ExprKind::Call {
2965                callee,
2966                type_args: None,
2967                args: vec![argument],
2968            },
2969        )?;
2970        self.synthetic_expr(
2971            span,
2972            ExprKind::Ternary {
2973                cond,
2974                then_: none,
2975                else_: keys,
2976            },
2977        )
2978    }
2979
2980    fn held_equals(&mut self, held: &HeldForInObject, other: ExprId) -> Option<ExprId> {
2981        let object =
2982            self.synthetic_expr(held.name.span, ExprKind::Identifier(held.name.clone()))?;
2983        self.synthetic_expr(
2984            held.name.span,
2985            ExprKind::Binary {
2986                op: BinOp::Eq,
2987                lhs: object,
2988                rhs: other,
2989            },
2990        )
2991    }
2992
2993    fn synthetic_expr(&mut self, span: Span, kind: ExprKind) -> Option<ExprId> {
2994        parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
2995    }
2996
2997    // Counts bracket/angle depth to skip past optional type annotations.
2998    fn scan_for_of_shape(&self) -> bool {
2999        if !matches!(self.peek().kind, TokenKind::Let | TokenKind::Const) {
3000            return false;
3001        }
3002        // Object patterns are tentatively accepted here; `parse_for_of_tail` rejects them.
3003        let (start, mut depth) = match self.peek_at(1).kind {
3004            TokenKind::Identifier => match self.peek_at(2).kind {
3005                TokenKind::Identifier if self.peek_at_is_word(2, "of") => return true,
3006                TokenKind::Colon => (3, 0usize),
3007                _ => return false,
3008            },
3009            TokenKind::LeftBracket | TokenKind::LeftBrace => (2, 1usize),
3010            _ => return false,
3011        };
3012        let mut i = start;
3013        loop {
3014            match self.peek_at(i).kind {
3015                TokenKind::Eof => return false,
3016                TokenKind::LessThan
3017                | TokenKind::LeftParen
3018                | TokenKind::LeftBracket
3019                | TokenKind::LeftBrace => {
3020                    depth += 1;
3021                }
3022                TokenKind::GreaterThan
3023                | TokenKind::RightParen
3024                | TokenKind::RightBracket
3025                | TokenKind::RightBrace => {
3026                    if depth == 0 {
3027                        return false;
3028                    }
3029                    depth -= 1;
3030                }
3031                // A user type literally named `of` in the annotation would
3032                // misclassify here and error loudly downstream — acceptable.
3033                TokenKind::Identifier if depth == 0 && self.peek_at_is_word(i, "of") => {
3034                    return true;
3035                }
3036                TokenKind::Equals | TokenKind::Semicolon if depth == 0 => return false,
3037                _ => {}
3038            }
3039            i += 1;
3040        }
3041    }
3042
3043    // The trailing `;` of init-statement parsers doubles as the first `;` of the for header.
3044    fn parse_c_for_tail(&mut self, kw: Token) -> Option<StmtId> {
3045        let init = match self.peek().kind {
3046            TokenKind::Semicolon => {
3047                self.advance();
3048                Vec::new()
3049            }
3050            TokenKind::Let | TokenKind::Const => {
3051                self.trailing_declarators.clear();
3052                let is_const = matches!(self.peek().kind, TokenKind::Const);
3053                let mut init = vec![self.parse_let_or_const(is_const)?];
3054                init.extend(self.take_trailing_declarators());
3055                init
3056            }
3057            _ => self.parse_for_init_expressions()?,
3058        };
3059        let for_stmt = self.finish_c_for(kw, init.clone());
3060        if for_stmt.is_none() {
3061            self.discard_detached_patterns(&init);
3062        }
3063        for_stmt
3064    }
3065
3066    fn finish_c_for(&mut self, kw: Token, init: Vec<StmtId>) -> Option<StmtId> {
3067        let condition = if matches!(self.peek().kind, TokenKind::Semicolon) {
3068            None
3069        } else {
3070            Some(self.parse_expression()?)
3071        };
3072        if !matches!(self.peek().kind, TokenKind::Semicolon) {
3073            self.error_at_peek("expected `;` after `for` condition");
3074            return None;
3075        }
3076        self.advance();
3077
3078        let update = if matches!(self.peek().kind, TokenKind::RightParen) {
3079            None
3080        } else {
3081            Some(self.parse_for_update_stmt()?)
3082        };
3083        if !matches!(self.peek().kind, TokenKind::RightParen) {
3084            self.error_at_peek("expected `)` after `for` update");
3085            return None;
3086        }
3087        self.advance();
3088
3089        let body = self.parse_loop_body()?;
3090        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3091            .span
3092            .end;
3093        parse_arena_result(
3094            self.ast.try_push_stmt(Stmt {
3095                kind: StmtKind::For {
3096                    init,
3097                    condition,
3098                    update,
3099                    body,
3100                },
3101                span: self.span(kw.span.start, body_end),
3102            }),
3103            &mut self.fatal,
3104        )
3105    }
3106
3107    /// Pattern lowering expands a destructuring declaration only where a block, the
3108    /// program or a `for` head holds it, so one parsed into the arena whose
3109    /// statement then failed (a `for` loop, a later declarator, a nested `export`)
3110    /// would otherwise reach inference unlowered.
3111    fn discard_detached_patterns(&mut self, stmts: &[StmtId]) {
3112        for &id in stmts {
3113            let Some(stmt) = parse_arena_result(self.ast.try_stmt_mut(id), &mut self.fatal) else {
3114                return;
3115            };
3116            if matches!(
3117                stmt.kind,
3118                StmtKind::LetPattern { .. } | StmtKind::ConstPattern { .. }
3119            ) {
3120                stmt.kind = StmtKind::Block(Vec::new());
3121            }
3122        }
3123    }
3124
3125    /// `i = 0, j = n` in a `for` initializer runs each expression in turn, so each
3126    /// becomes its own statement, as each declarator of a `let` head does.
3127    fn parse_for_init_expressions(&mut self) -> Option<Vec<StmtId>> {
3128        let mut init = vec![self.parse_for_clause_expr()?];
3129        while matches!(self.peek().kind, TokenKind::Comma) {
3130            self.advance();
3131            init.push(self.parse_for_clause_expr()?);
3132        }
3133        if !self.at_statement_end() {
3134            let last = parse_arena_result(self.ast.try_stmt(*init.last()?), &mut self.fatal)?;
3135            let what = if matches!(last.kind, StmtKind::Expr(_)) {
3136                "expression"
3137            } else {
3138                "assignment"
3139            };
3140            self.error_at_peek(format!("expected `;` after {what}"));
3141            return None;
3142        }
3143        self.finish_statement();
3144        Some(init)
3145    }
3146
3147    /// `i++, j--` in a `for` update runs each expression in turn, so it becomes a
3148    /// block of their statements; it declares nothing, so the block scopes nothing.
3149    fn parse_for_update_stmt(&mut self) -> Option<StmtId> {
3150        let first = self.parse_for_clause_expr()?;
3151        if !matches!(self.peek().kind, TokenKind::Comma) {
3152            return Some(first);
3153        }
3154        let mut stmts = vec![first];
3155        while matches!(self.peek().kind, TokenKind::Comma) {
3156            self.advance();
3157            stmts.push(self.parse_for_clause_expr()?);
3158        }
3159        let start = parse_arena_result(self.ast.try_stmt(first), &mut self.fatal)?
3160            .span
3161            .start;
3162        let span = self.span(start, self.prev_token_end());
3163        parse_arena_result(
3164            self.ast.try_push_stmt(Stmt {
3165                kind: StmtKind::Block(stmts),
3166                span,
3167            }),
3168            &mut self.fatal,
3169        )
3170    }
3171
3172    fn parse_for_clause_expr(&mut self) -> Option<StmtId> {
3173        let expr_id = self.parse_expression()?;
3174        let span = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.span;
3175        let kind = self.statement_kind_for(expr_id)?;
3176        parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
3177    }
3178
3179    fn parse_for_of_tail(&mut self, kw: Token) -> Option<StmtId> {
3180        let binding_tok = self.advance();
3181        let binding_kind = match binding_tok.kind {
3182            TokenKind::Let => crate::BindingKind::Let,
3183            TokenKind::Const => crate::BindingKind::Const,
3184            _ => return self.invariant_failure("scan_for_of_shape gated this"),
3185        };
3186        let (name, binding) = match self.peek().kind {
3187            TokenKind::LeftBracket => {
3188                let b = self.parse_binding()?;
3189                (None, Some(b))
3190            }
3191            TokenKind::LeftBrace => {
3192                let b = self.parse_binding()?;
3193                self.error_at(
3194                    b.span(),
3195                    "object destructuring is not supported in `for-of`; use array destructuring or unpack inside the loop body",
3196                );
3197                return None;
3198            }
3199            _ => {
3200                let name_tok = self.expect_binding_identifier(
3201                    "expected identifier after `let`/`const` in `for-of`",
3202                )?;
3203                (Some(self.ident_from_token(&name_tok)), None)
3204            }
3205        };
3206        let ty = if matches!(self.peek().kind, TokenKind::Colon) {
3207            self.advance();
3208            Some(self.parse_type_annotation()?)
3209        } else {
3210            None
3211        };
3212        if !self.peek_identifier_text_is("of") {
3213            self.error_at_peek("expected `of` in `for-of`");
3214            return None;
3215        }
3216        self.advance();
3217
3218        let iter = self.parse_expression()?;
3219        if !matches!(self.peek().kind, TokenKind::RightParen) {
3220            self.error_at_peek("expected `)` after `for-of` iterable");
3221            return None;
3222        }
3223        self.advance();
3224
3225        let body = self.parse_loop_body()?;
3226        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3227            .span
3228            .end;
3229        let kind = match (binding, name) {
3230            (Some(binding), _) => StmtKind::ForOfPattern {
3231                binding_kind,
3232                binding,
3233                ty,
3234                iter,
3235                body,
3236            },
3237            (None, Some(name)) => StmtKind::ForOf {
3238                binding_kind,
3239                name,
3240                ty,
3241                iter,
3242                body,
3243            },
3244            (None, None) => return self.invariant_failure("either binding or name must be Some"),
3245        };
3246        parse_arena_result(
3247            self.ast.try_push_stmt(Stmt {
3248                kind,
3249                span: self.span(kw.span.start, body_end),
3250            }),
3251            &mut self.fatal,
3252        )
3253    }
3254
3255    fn parse_do_while(&mut self) -> Option<StmtId> {
3256        let kw = self.advance();
3257        let body = self.parse_loop_body()?;
3258        self.eat_asi_semicolon_before(TokenKind::While);
3259        if !matches!(self.peek().kind, TokenKind::While) {
3260            self.error_at_peek("expected `while` after `do` body");
3261            return None;
3262        }
3263        self.advance();
3264        let condition = self.parse_paren_condition()?;
3265        // ECMAScript inserts the terminator after a do-while's `)` unconditionally, so the
3266        // `;` is optional here — ASI can't supply it, since that `)` closes a `while`
3267        // header everywhere else.
3268        let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
3269            self.advance().span.end
3270        } else {
3271            parse_arena_result(self.ast.try_expr(condition), &mut self.fatal)?
3272                .span
3273                .end
3274        };
3275        parse_arena_result(
3276            self.ast.try_push_stmt(Stmt {
3277                kind: StmtKind::DoWhile { body, condition },
3278                span: self.span(kw.span.start, end),
3279            }),
3280            &mut self.fatal,
3281        )
3282    }
3283
3284    // Multiple consecutive `case`/`default` labels share the next body.
3285    fn parse_switch(&mut self) -> Option<StmtId> {
3286        let kw = self.advance();
3287        if !matches!(self.peek().kind, TokenKind::LeftParen) {
3288            self.error_at_peek("expected `(` after `switch`");
3289            return None;
3290        }
3291        self.advance();
3292        let discriminant = self.parse_expression()?;
3293        if !matches!(self.peek().kind, TokenKind::RightParen) {
3294            self.error_at_peek("expected `)` after `switch` discriminant");
3295            return None;
3296        }
3297        self.advance();
3298
3299        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
3300            self.error_at_peek("expected `{` to start `switch` body");
3301            return None;
3302        }
3303        self.advance();
3304        self.block_depth += 1;
3305        let arms = self.parse_switch_arms();
3306        self.block_depth -= 1;
3307        let (cases, default) = arms?;
3308
3309        if !matches!(self.peek().kind, TokenKind::RightBrace) {
3310            self.error_at_peek("expected `}` to close `switch` body");
3311            return None;
3312        }
3313        let close = self.advance();
3314
3315        parse_arena_result(
3316            self.ast.try_push_stmt(Stmt {
3317                kind: StmtKind::Switch {
3318                    discriminant,
3319                    cases,
3320                    default,
3321                },
3322                span: self.span(kw.span.start, close.span.end),
3323            }),
3324            &mut self.fatal,
3325        )
3326    }
3327
3328    // Reads the arms up to the closing `}`, which is left for the caller.
3329    fn parse_switch_arms(&mut self) -> Option<(Vec<SwitchCase>, Option<SwitchDefault>)> {
3330        let mut cases: Vec<SwitchCase> = Vec::new();
3331        let mut default: Option<SwitchDefault> = None;
3332        let mut pending_values: Vec<ExprId> = Vec::new();
3333        let mut pending_label_start: Option<u32> = None;
3334        let mut pending_default: Option<Span> = None;
3335
3336        loop {
3337            match self.peek().kind {
3338                TokenKind::Eof => break,
3339                // ASI inserts `;` after `:` on a newline — treat it as a no-op here.
3340                TokenKind::Semicolon => {
3341                    self.advance();
3342                }
3343                TokenKind::RightBrace => {
3344                    // Case bodies are required (typechecker enforces break/return termination).
3345                    if pending_label_start.is_some() {
3346                        self.error_at_peek(
3347                            "expected case body before `}` — case labels must be followed by statements",
3348                        );
3349                        return None;
3350                    }
3351                    break;
3352                }
3353                TokenKind::Case => {
3354                    let case_kw = self.advance();
3355                    if pending_label_start.is_none() {
3356                        pending_label_start = Some(case_kw.span.start);
3357                    }
3358                    let value = self.parse_expression()?;
3359                    if !matches!(self.peek().kind, TokenKind::Colon) {
3360                        self.error_at_peek("expected `:` after `case` label");
3361                        return None;
3362                    }
3363                    self.advance();
3364                    pending_values.push(value);
3365                }
3366                TokenKind::Default => {
3367                    let kw_tok = self.advance();
3368                    if default.is_some() {
3369                        self.error_at(kw_tok.span, "duplicate `default` clause in `switch`");
3370                        return None;
3371                    }
3372                    if !matches!(self.peek().kind, TokenKind::Colon) {
3373                        self.error_at_peek("expected `:` after `default`");
3374                        return None;
3375                    }
3376                    self.advance();
3377                    if pending_label_start.is_none() {
3378                        pending_label_start = Some(kw_tok.span.start);
3379                    }
3380                    pending_default = Some(kw_tok.span);
3381                }
3382                _ => {
3383                    if pending_label_start.is_none() {
3384                        self.error_at_peek(
3385                            "expected `case` or `default` at the start of a `switch` body",
3386                        );
3387                        return None;
3388                    }
3389                    let Some(label_start) = pending_label_start else {
3390                        return self.invariant_failure("switch arm has no label");
3391                    };
3392                    let body = self.parse_switch_arm_body(label_start)?;
3393                    let arm_span =
3394                        parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?.span;
3395                    if !pending_values.is_empty() {
3396                        let first = parse_arena_result(
3397                            self.ast.try_expr(pending_values[0]),
3398                            &mut self.fatal,
3399                        )?
3400                        .span
3401                        .start;
3402                        let case_span = self.span(first, arm_span.end);
3403                        cases.push(SwitchCase {
3404                            values: std::mem::take(&mut pending_values),
3405                            body,
3406                            span: case_span,
3407                        });
3408                    }
3409                    if let Some(def_kw_span) = pending_default.take() {
3410                        default = Some(SwitchDefault {
3411                            body,
3412                            span: self.span(def_kw_span.start, arm_span.end),
3413                        });
3414                    }
3415                    pending_label_start = None;
3416                }
3417            }
3418        }
3419        Some((cases, default))
3420    }
3421
3422    // `body_start` is the label keyword's offset so diagnostics point at the label.
3423    fn parse_switch_arm_body(&mut self, body_start: u32) -> Option<StmtId> {
3424        let mut stmts: Vec<StmtId> = Vec::new();
3425        let mut end_offset = body_start;
3426        loop {
3427            if matches!(
3428                self.peek().kind,
3429                TokenKind::Case | TokenKind::Default | TokenKind::RightBrace | TokenKind::Eof
3430            ) {
3431                break;
3432            }
3433            if matches!(self.peek().kind, TokenKind::Semicolon) {
3434                end_offset = self.peek().span.end;
3435                self.advance();
3436                continue;
3437            }
3438            let pos_before = self.pos;
3439            if let Some(id) = self.parse_statement() {
3440                stmts.push(id);
3441                stmts.extend(self.take_trailing_declarators());
3442                let last = stmts.last().copied().unwrap_or(id);
3443                end_offset = parse_arena_result(self.ast.try_stmt(last), &mut self.fatal)?
3444                    .span
3445                    .end;
3446            } else {
3447                self.recover();
3448                if self.pos == pos_before && !self.is_at_eof() {
3449                    self.advance();
3450                }
3451                // Recovery consumed tokens but produced no statement; advance the body
3452                // end past them so the arm span can't end before its own case value.
3453                end_offset = end_offset.max(self.prev_token_end());
3454            }
3455        }
3456        let span = self.span(body_start, end_offset);
3457        parse_arena_result(
3458            self.ast.try_push_stmt(Stmt {
3459                kind: StmtKind::Block(stmts),
3460                span,
3461            }),
3462            &mut self.fatal,
3463        )
3464    }
3465
3466    fn parse_break(&mut self) -> Option<StmtId> {
3467        let kw = self.advance();
3468        if !matches!(self.peek().kind, TokenKind::Semicolon) {
3469            self.error_at_peek("expected `;` after `break`");
3470            return None;
3471        }
3472        let semi = self.advance();
3473        parse_arena_result(
3474            self.ast.try_push_stmt(Stmt {
3475                kind: StmtKind::Break,
3476                span: self.span(kw.span.start, semi.span.end),
3477            }),
3478            &mut self.fatal,
3479        )
3480    }
3481
3482    fn parse_continue(&mut self) -> Option<StmtId> {
3483        let kw = self.advance();
3484        if !matches!(self.peek().kind, TokenKind::Semicolon) {
3485            self.error_at_peek("expected `;` after `continue`");
3486            return None;
3487        }
3488        let semi = self.advance();
3489        parse_arena_result(
3490            self.ast.try_push_stmt(Stmt {
3491                kind: StmtKind::Continue,
3492                span: self.span(kw.span.start, semi.span.end),
3493            }),
3494            &mut self.fatal,
3495        )
3496    }
3497
3498    fn parse_return(&mut self) -> Option<StmtId> {
3499        let kw = self.advance();
3500
3501        let value = if matches!(self.peek().kind, TokenKind::Semicolon) {
3502            None
3503        } else {
3504            Some(self.parse_expression()?)
3505        };
3506
3507        if !self.at_statement_end() {
3508            self.error_at_peek("expected `;` after return");
3509            return None;
3510        }
3511        let end = self.finish_statement();
3512
3513        parse_arena_result(
3514            self.ast.try_push_stmt(Stmt {
3515                kind: StmtKind::Return(value),
3516                span: self.span(kw.span.start, end),
3517            }),
3518            &mut self.fatal,
3519        )
3520    }
3521
3522    fn parse_throw(&mut self) -> Option<StmtId> {
3523        let kw = self.advance();
3524
3525        if matches!(self.peek().kind, TokenKind::Semicolon) {
3526            self.error_at_peek_with_help(
3527                "expected expression after `throw`",
3528                vec!["throw new Error(\"message\");".to_string()],
3529            );
3530            return None;
3531        }
3532
3533        let value = self.parse_expression()?;
3534
3535        if !self.at_statement_end() {
3536            self.error_at_peek("expected `;` after `throw`");
3537            return None;
3538        }
3539        let end = self.finish_statement();
3540
3541        parse_arena_result(
3542            self.ast.try_push_stmt(Stmt {
3543                kind: StmtKind::Throw { value },
3544                span: self.span(kw.span.start, end),
3545            }),
3546            &mut self.fatal,
3547        )
3548    }
3549
3550    fn parse_try(&mut self) -> Option<StmtId> {
3551        let kw = self.advance();
3552
3553        let body = self.parse_block()?;
3554
3555        let mut catches: Vec<CatchClause> = Vec::new();
3556        let mut finally: Option<StmtId> = None;
3557        let mut tail_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3558            .span
3559            .end;
3560
3561        loop {
3562            match self.peek().kind {
3563                TokenKind::Catch => {
3564                    if finally.is_some() {
3565                        self.error_at_peek("`catch` clause must appear before `finally`");
3566                        return None;
3567                    }
3568                    let clause = self.parse_catch_clause()?;
3569                    tail_end = clause.span.end;
3570                    catches.push(clause);
3571                }
3572                TokenKind::Finally => {
3573                    if finally.is_some() {
3574                        self.error_at_peek("duplicate `finally` clause");
3575                        return None;
3576                    }
3577                    self.advance();
3578                    let block = self.parse_block()?;
3579                    tail_end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
3580                        .span
3581                        .end;
3582                    finally = Some(block);
3583                }
3584                _ => break,
3585            }
3586        }
3587
3588        if catches.is_empty() && finally.is_none() {
3589            self.error_at(
3590                self.span(kw.span.start, tail_end),
3591                "try requires at least one of `catch` or `finally`",
3592            );
3593            return None;
3594        }
3595
3596        parse_arena_result(
3597            self.ast.try_push_stmt(Stmt {
3598                kind: StmtKind::Try {
3599                    body,
3600                    catches,
3601                    finally,
3602                },
3603                span: self.span(kw.span.start, tail_end),
3604            }),
3605            &mut self.fatal,
3606        )
3607    }
3608
3609    fn parse_catch_clause(&mut self) -> Option<CatchClause> {
3610        let kw = self.advance();
3611
3612        let (binding, ty) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
3613            // This binding is inaccessible to source code, including nested catches.
3614            (
3615                Ident {
3616                    name: "#catch".into(),
3617                    span: kw.span,
3618                },
3619                None,
3620            )
3621        } else {
3622            self.parse_catch_binding()?
3623        };
3624
3625        let body = self.parse_block()?;
3626        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3627            .span
3628            .end;
3629
3630        Some(CatchClause {
3631            binding,
3632            ty,
3633            body,
3634            span: self.span(kw.span.start, body_end),
3635        })
3636    }
3637
3638    fn parse_catch_binding(&mut self) -> Option<(Ident, Option<TypeAnnotation>)> {
3639        if !matches!(self.peek().kind, TokenKind::LeftParen) {
3640            self.error_at_peek("expected `(` after `catch`");
3641            return None;
3642        }
3643        self.advance();
3644        let name_tok = self.expect_binding_identifier("expected catch binding name")?;
3645        let binding = self.ident_from_token(&name_tok);
3646        let ty = if matches!(self.peek().kind, TokenKind::Colon) {
3647            self.advance();
3648            if self.reject_any_catch_annotation() {
3649                // The error is reported; parsing continues as an unannotated catch.
3650                None
3651            } else {
3652                Some(self.parse_type_annotation()?)
3653            }
3654        } else {
3655            None
3656        };
3657        if !matches!(self.peek().kind, TokenKind::RightParen) {
3658            self.error_at_peek("expected `)` after catch binding");
3659            return None;
3660        }
3661        self.advance();
3662        Some((binding, ty))
3663    }
3664
3665    /// Reports `catch (e: any)` and consumes the `any`, returning whether it did.
3666    /// Only exactly `any)` is handled; compound types keep the general diagnostic.
3667    fn reject_any_catch_annotation(&mut self) -> bool {
3668        let is_bare_any = self.peek_identifier_text_is("any")
3669            && matches!(self.peek_at(1).kind, TokenKind::RightParen);
3670        if !is_bare_any {
3671            return false;
3672        }
3673        let any_tok = self.advance();
3674        // The general `any` help suggests `unknown`, which a catch binding refuses.
3675        self.error_at_with_help(
3676            any_tok.span,
3677            "`any` is not supported",
3678            vec![
3679                "write `catch (e)` to catch every thrown error, or name an error class, \
3680                 as in `catch (e: RangeError)`, to catch only that type"
3681                    .to_string(),
3682            ],
3683        );
3684        true
3685    }
3686
3687    fn parse_paren_condition(&mut self) -> Option<ExprId> {
3688        if !matches!(self.peek().kind, TokenKind::LeftParen) {
3689            self.error_at_peek("expected `(`");
3690            return None;
3691        }
3692        self.advance();
3693        let expr = self.parse_expression()?;
3694        if !matches!(self.peek().kind, TokenKind::RightParen) {
3695            self.error_at_peek("expected `)`");
3696            return None;
3697        }
3698        self.advance();
3699        Some(expr)
3700    }
3701
3702    fn parse_predicate_or_return_type(
3703        &mut self,
3704        type_pos: TypePos,
3705    ) -> Option<(
3706        Option<TypeAnnotation>,
3707        Option<crate::TypePredicateAnnotation>,
3708    )> {
3709        if matches!(self.peek().kind, TokenKind::Identifier) && self.peek_at_is_word(1, "is") {
3710            let param_tok = self.advance();
3711            let param = self.ident_from_token(&param_tok);
3712            self.advance();
3713            let asserted = self.parse_type_annotation_in(type_pos)?;
3714            let span = self.span(param.span.start, asserted.span.end);
3715            return Some((
3716                None,
3717                Some(crate::TypePredicateAnnotation {
3718                    param,
3719                    asserted,
3720                    span,
3721                }),
3722            ));
3723        }
3724        Some((Some(self.parse_type_annotation_in(type_pos)?), None))
3725    }
3726
3727    fn parse_type_annotation(&mut self) -> Option<TypeAnnotation> {
3728        self.parse_type_annotation_in(TypePos::Anywhere)
3729    }
3730
3731    fn parse_type_annotation_in(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3732        // A leading `|` (prettier's multiline format) is accepted and discarded.
3733        let leading_pipe = matches!(self.peek().kind, TokenKind::Pipe);
3734        if leading_pipe {
3735            self.advance();
3736        }
3737        let first = self.parse_type_array(type_pos)?;
3738        if !matches!(self.peek().kind, TokenKind::Pipe) {
3739            return Some(first);
3740        }
3741        let start = first.span.start;
3742        let mut end = first.span.end;
3743        let mut members = vec![first];
3744        while matches!(self.peek().kind, TokenKind::Pipe) {
3745            self.advance();
3746            let member = self.parse_type_array(type_pos)?;
3747            end = member.span.end;
3748            members.push(member);
3749        }
3750        Some(TypeAnnotation {
3751            kind: TypeAnnotationKind::Union(members),
3752            span: self.span(start, end),
3753        })
3754    }
3755
3756    fn parse_type_array(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3757        self.with_recursion_limit(|parser| parser.parse_type_array_inner(type_pos))
3758    }
3759
3760    fn parse_type_array_inner(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3761        // `keyof` is contextual, not reserved: TypeScript allows it as an ordinary
3762        // identifier, and so does Submilli. It is an operator only when another type
3763        // follows it, which an identifier in type position never does.
3764        if self.is_contextual_type_operator(self.pos, "keyof") {
3765            let kw = self.advance();
3766            let operand = self.parse_type_array(type_pos)?;
3767            let span = self.span(kw.span.start, operand.span.end);
3768            return Some(TypeAnnotation {
3769                kind: TypeAnnotationKind::KeyOf(Box::new(operand)),
3770                span,
3771            });
3772        }
3773        if self.is_contextual_type_operator(self.pos, "readonly") {
3774            return self.parse_readonly_type(type_pos);
3775        }
3776
3777        let mut ty = match self.peek().kind {
3778            TokenKind::TemplateHead(ref text) => self.parse_template_type(text.clone())?,
3779            TokenKind::TemplateNoSubstitution(ref text) => {
3780                let text = text.clone();
3781                let token = self.advance();
3782                TypeAnnotation {
3783                    kind: TypeAnnotationKind::StringLiteral(text),
3784                    span: token.span,
3785                }
3786            }
3787
3788            // `typeof x` names the type of a *value*. Unlike `keyof` this is already a
3789            // reserved word (the `typeof x === "string"` guard uses it), so it needs no
3790            // contextual check — in type position it is only ever the operator. Built
3791            // here rather than returned early so the `[]` suffix loop below still runs,
3792            // making `typeof a[]` an array of it, as TypeScript parses it too.
3793            TokenKind::Typeof => {
3794                let kw = self.advance();
3795                let first = self.expect_identifier("expected a value name after `typeof`")?;
3796                let mut path = vec![self.type_name(first.span)?];
3797                let mut end = first.span.end;
3798                while matches!(self.peek().kind, TokenKind::Dot) {
3799                    self.advance();
3800                    let seg = self.expect_identifier_name("expected a property name after `.`")?;
3801                    end = seg.span.end;
3802                    path.push(self.type_name(seg.span)?);
3803                }
3804                TypeAnnotation {
3805                    kind: TypeAnnotationKind::TypeOf { path },
3806                    span: self.span(kw.span.start, end),
3807                }
3808            }
3809            TokenKind::Identifier | TokenKind::Void => {
3810                // `void` never starts a qualified path.
3811                let leading_is_identifier = matches!(self.peek().kind, TokenKind::Identifier);
3812                let tok = self.advance();
3813                let first_span = tok.span;
3814                let mut path_end = first_span.end;
3815                let first_name = self.type_name(first_span)?;
3816                let mut path = vec![first_name.clone()];
3817                if leading_is_identifier {
3818                    while matches!(self.peek().kind, TokenKind::Dot) {
3819                        self.advance();
3820                        if !matches!(self.peek().kind, TokenKind::Identifier) {
3821                            self.error_at_peek("expected identifier after `.` in type name");
3822                            return None;
3823                        }
3824                        let seg = self.advance();
3825                        path_end = seg.span.end;
3826                        path.push(self.type_name(seg.span)?);
3827                    }
3828                }
3829                // Type arguments, like the `[]` suffix, must start on the type's line.
3830                let (args, end) = if matches!(self.peek().kind, TokenKind::LessThan)
3831                    && !self.line_break_before_peek()
3832                {
3833                    self.parse_type_argument_list()?
3834                } else {
3835                    (Vec::new(), path_end)
3836                };
3837                let outer_span = self.span(first_span.start, end);
3838                let kind = if path.len() == 1 {
3839                    if first_name.name == "any" {
3840                        self.error_at_with_help(
3841                            first_span,
3842                            "`any` is not supported",
3843                            vec![
3844                                "annotate a concrete type instead — an object shape \
3845                                 like `{ id: string }`, a named `interface`, or \
3846                                 `unknown` (a safe dynamic type you narrow before use)"
3847                                    .to_string(),
3848                            ],
3849                        );
3850                    }
3851                    TypeAnnotationKind::Name {
3852                        name: first_name,
3853                        args,
3854                    }
3855                } else {
3856                    TypeAnnotationKind::Qualified { path, args }
3857                };
3858                TypeAnnotation {
3859                    kind,
3860                    span: outer_span,
3861                }
3862            }
3863            // `null<T>` is meaningless — no type args after `null`.
3864            TokenKind::NullLiteral => {
3865                let tok = self.advance();
3866                TypeAnnotation {
3867                    kind: TypeAnnotationKind::Name {
3868                        name: self.type_name(tok.span)?,
3869                        args: Vec::new(),
3870                    },
3871                    span: tok.span,
3872                }
3873            }
3874            TokenKind::StringLiteral(_) => {
3875                let tok = self.advance();
3876                let span = tok.span;
3877                let TokenKind::StringLiteral(s) = tok.kind else {
3878                    // advance returns the token just matched, before moving the cursor.
3879                    unreachable!("advance preserves the matched StringLiteral variant");
3880                };
3881                TypeAnnotation {
3882                    kind: TypeAnnotationKind::StringLiteral(s),
3883                    span,
3884                }
3885            }
3886            TokenKind::NumberLiteral(_) => {
3887                let tok = self.advance();
3888                let span = tok.span;
3889                let TokenKind::NumberLiteral(v) = tok.kind else {
3890                    // advance returns the token just matched, before moving the cursor.
3891                    unreachable!("advance preserves the matched NumberLiteral variant");
3892                };
3893                // Canonicalize `-0.0` → `0.0` so literal type `0` matches both signs.
3894                let canonical = if v == 0.0 { 0.0 } else { v };
3895                TypeAnnotation {
3896                    kind: TypeAnnotationKind::NumberLiteral(crate::types::LiteralF64(canonical)),
3897                    span,
3898                }
3899            }
3900            TokenKind::BigIntLiteral(_) => {
3901                let tok = self.advance();
3902                let TokenKind::BigIntLiteral(digits) = tok.kind else {
3903                    // advance returns the token just matched, before moving the cursor.
3904                    unreachable!("advance preserves the matched BigIntLiteral variant");
3905                };
3906                TypeAnnotation {
3907                    kind: TypeAnnotationKind::BigIntLiteral(digits),
3908                    span: tok.span,
3909                }
3910            }
3911            TokenKind::Minus
3912                if matches!(
3913                    self.peek_at(1).kind,
3914                    TokenKind::NumberLiteral(_) | TokenKind::BigIntLiteral(_)
3915                ) =>
3916            {
3917                let minus = self.advance().span;
3918                let tok = self.advance();
3919                let span = self.span(minus.start, tok.span.end);
3920                let kind =
3921                    match tok.kind {
3922                        TokenKind::NumberLiteral(v) => TypeAnnotationKind::NumberLiteral(
3923                            crate::types::LiteralF64(if v == 0.0 { 0.0 } else { -v }),
3924                        ),
3925                        TokenKind::BigIntLiteral(digits) => TypeAnnotationKind::BigIntLiteral(
3926                            crate::types::negate_bigint_digits(&digits),
3927                        ),
3928                        // The guard admits only the two numeric literal tokens.
3929                        _ => unreachable!("guarded to a numeric literal after `-`"),
3930                    };
3931                TypeAnnotation { kind, span }
3932            }
3933            TokenKind::BooleanLiteral(value) => {
3934                let span = self.advance().span;
3935                TypeAnnotation {
3936                    kind: TypeAnnotationKind::BooleanLiteral(value),
3937                    span,
3938                }
3939            }
3940            TokenKind::LeftBrace => self.parse_object_type_annotation()?,
3941            TokenKind::LeftParen if self.paren_at_opens_function_type(self.pos, type_pos) => {
3942                self.parse_function_type_annotation()?
3943            }
3944            TokenKind::LeftParen => self.parse_grouped_or_function_type()?,
3945            // Leading `[` is unambiguously a tuple (postfix `[]` can only follow a base type).
3946            TokenKind::LeftBracket => self.parse_tuple_type_annotation()?,
3947            _ => {
3948                self.error_at_peek("expected type");
3949                return None;
3950            }
3951        };
3952        // Array suffixes nest boxed annotations without recursing, so each one
3953        // spends the recursive grammar budget to keep the chain's drop bounded.
3954        let mut array_suffixes = 0usize;
3955        // As in TypeScript, a line break ends the type before `[`: in an object type,
3956        // `a: B` ⏎ `[k: string]: V` is a field followed by an index signature.
3957        while matches!(self.peek().kind, TokenKind::LeftBracket) && !self.line_break_before_peek() {
3958            array_suffixes += 1;
3959            if self.recursion_depth.saturating_add(array_suffixes) >= MAX_PARSE_DEPTH {
3960                self.stop_at_recursion_limit();
3961                return None;
3962            }
3963            let open = self.advance();
3964            if !matches!(self.peek().kind, TokenKind::RightBracket) {
3965                // `T[K]`, an indexed access type. The index is a whole type,
3966                // unions included.
3967                let index = self.parse_type_annotation()?;
3968                if !matches!(self.peek().kind, TokenKind::RightBracket) {
3969                    self.error_at(open.span, "expected `]` to close indexed access type");
3970                    return None;
3971                }
3972                let close = self.advance();
3973                let span = self.span(ty.span.start, close.span.end);
3974                ty = TypeAnnotation {
3975                    kind: TypeAnnotationKind::IndexedAccess {
3976                        object: Box::new(ty),
3977                        index: Box::new(index),
3978                    },
3979                    span,
3980                };
3981                continue;
3982            }
3983            let close = self.advance();
3984            let span = self.span(ty.span.start, close.span.end);
3985            ty = TypeAnnotation {
3986                kind: TypeAnnotationKind::Array(Box::new(ty)),
3987                span,
3988            };
3989        }
3990        Some(ty)
3991    }
3992
3993    /// `readonly T[]` or `readonly [A, B]`. The operand binds like `keyof`'s, so
3994    /// `readonly T[][]` is a readonly array of mutable arrays and
3995    /// `readonly T[] | null` a union with a readonly member.
3996    fn parse_readonly_type(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3997        let kw = self.advance();
3998        let opens_group = matches!(self.peek().kind, TokenKind::LeftParen);
3999        let operand = self.parse_type_array(type_pos)?;
4000        // `readonly (T[])` is rejected as TypeScript rejects it: the operand must be
4001        // written as an array or tuple type, not grouped into one.
4002        let grouped = opens_group
4003            && matches!(
4004                self.pos
4005                    .checked_sub(1)
4006                    .and_then(|index| self.tokens.get(index))
4007                    .map(|token| &token.kind),
4008                Some(TokenKind::RightParen)
4009            );
4010        if grouped
4011            || !matches!(
4012                operand.kind,
4013                TypeAnnotationKind::Array(_) | TypeAnnotationKind::Tuple(_)
4014            )
4015        {
4016            self.error_at_with_help(
4017                kw.span,
4018                "`readonly` only applies to array and tuple types",
4019                vec![
4020                    "write `readonly T[]` or `readonly [A, B]`; to protect an object's \
4021                     properties, mark each one `readonly` instead"
4022                        .to_string(),
4023                ],
4024            );
4025            return Some(operand);
4026        }
4027        let span = self.span(kw.span.start, operand.span.end);
4028        Some(TypeAnnotation {
4029            kind: TypeAnnotationKind::Readonly(Box::new(operand)),
4030            span,
4031        })
4032    }
4033
4034    /// In type position `(` is ambiguous: a function type's parameter list, or a
4035    /// grouping paren the postfix `[]` / `|` then composes with. Decided by what the
4036    /// parens *contain*, not by what follows them — a trailing `=>` can belong to an
4037    /// enclosing arrow function's body (`(): (string | null) => null`), so looking
4038    /// past the `)` would read that return type as a parameter list. The exception is
4039    /// `(T)` / `(T, U)`, spelled identically either way, which only a trailing `=>`
4040    /// can break.
4041    ///
4042    /// Empty parens count as a parameter list: `()` groups nothing, so the
4043    /// function-type parse's "expected `=>`" is the message that names the fix.
4044    fn paren_at_opens_function_type(&self, index: usize, type_pos: TypePos) -> bool {
4045        let after = |offset: usize| self.tokens.get(index + offset).map(|t| &t.kind);
4046        match (after(1), after(2)) {
4047            // Only a parameter list is empty or starts with a rest element.
4048            (Some(TokenKind::RightParen | TokenKind::DotDotDot), _) => true,
4049            // A named, annotated parameter — no type starts this way.
4050            (Some(TokenKind::Identifier), Some(TokenKind::Colon | TokenKind::Question)) => true,
4051            // `(T)` and `(T, U)` are spelled exactly like a grouped type. v1
4052            // rejects bare parameter names, and a trailing `=>` is the only sign
4053            // the writer meant one — routing those to the function-type parse
4054            // keeps its "params require named annotations" message reachable in
4055            // declaration position (`type F = (T) => R`). In an arrow's *return*
4056            // annotation the trailing `=>` is the body's, so `(): (T) => x` loses
4057            // that message and falls back to the expression parse; dropping the
4058            // redundant parens is the fix there.
4059            (Some(TokenKind::Identifier), Some(TokenKind::Comma | TokenKind::RightParen)) => {
4060                type_pos == TypePos::Anywhere
4061                    && self.index_after_matching_paren(index).is_some_and(|i| {
4062                        matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow))
4063                    })
4064            }
4065            // Anything else can only begin a type, so the parens group.
4066            _ => false,
4067        }
4068    }
4069
4070    /// Parse a `(` the contents rule called a group. If that fails and a `=>` follows
4071    /// the matching `)`, the author was writing a function type after all — a typo in
4072    /// its parameter list is what made it look like a group — so re-parse that way to
4073    /// recover the diagnostic naming the parameter rule instead of a stray-`)` error.
4074    fn parse_grouped_or_function_type(&mut self) -> Option<TypeAnnotation> {
4075        let open = self.pos;
4076        let diagnostics_before = self.diagnostics.len();
4077        if let Some(ty) = self.parse_grouped_type_annotation() {
4078            return Some(ty);
4079        }
4080        let arrow_follows = self
4081            .index_after_matching_paren(open)
4082            .is_some_and(|i| matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)));
4083        if !arrow_follows {
4084            return None;
4085        }
4086        self.pos = open;
4087        self.diagnostics.truncate(diagnostics_before);
4088        self.parse_function_type_annotation()
4089    }
4090
4091    /// A parenthesized type is its inner type — the parens only group, so no node
4092    /// records them. The span covers them so a diagnostic points at what was written.
4093    fn parse_grouped_type_annotation(&mut self) -> Option<TypeAnnotation> {
4094        let open = self.advance();
4095        let inner = self.parse_type_annotation()?;
4096        if !matches!(self.peek().kind, TokenKind::RightParen) {
4097            self.error_at_peek("expected `)` to close a parenthesized type");
4098            return None;
4099        }
4100        let close = self.advance();
4101        Some(TypeAnnotation {
4102            span: self.span(open.span.start, close.span.end),
4103            ..inner
4104        })
4105    }
4106
4107    // Param names are required — bare `(T, U) => R` is rejected.
4108    fn parse_function_type_annotation(&mut self) -> Option<TypeAnnotation> {
4109        let open = self.advance();
4110        let params = self.parse_function_type_params()?;
4111        if !matches!(self.peek().kind, TokenKind::Arrow) {
4112            self.error_at_peek("expected `=>` in function-type annotation");
4113            return None;
4114        }
4115        self.advance();
4116        let return_type = self.parse_type_annotation()?;
4117        let end = return_type.span.end;
4118        Some(TypeAnnotation {
4119            kind: TypeAnnotationKind::Function {
4120                params,
4121                return_type: Box::new(return_type),
4122            },
4123            span: self.span(open.span.start, end),
4124        })
4125    }
4126
4127    /// The `(…)` of a function type, its `(` already consumed and its `)` consumed here.
4128    /// Shared with a type literal's method members, which spell the same parameter list
4129    /// before a `:` rather than a `=>`.
4130    fn parse_function_type_params(&mut self) -> Option<Vec<TypeAnnotationField>> {
4131        let mut params: Vec<TypeAnnotationField> = Vec::new();
4132        if !matches!(self.peek().kind, TokenKind::RightParen) {
4133            loop {
4134                let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
4135                    self.advance();
4136                    true
4137                } else {
4138                    false
4139                };
4140                let name_tok = self.expect_binding_identifier(if rest {
4141                    "expected identifier after `...` in function-type annotation"
4142                } else {
4143                    "expected parameter name in function-type annotation"
4144                })?;
4145                let name = self.ident_from_token(&name_tok);
4146                let optional = self.parse_parameter_optional(rest, false)?;
4147                if !matches!(self.peek().kind, TokenKind::Colon) {
4148                    if rest {
4149                        self.error_at_peek_with_help(
4150                            "rest parameter requires a type annotation",
4151                            vec!["(...args: T[]) => R".to_string()],
4152                        );
4153                    } else {
4154                        self.error_at_peek(
4155                            "expected `:` after parameter name (function-type params \
4156                             require named annotations in v1)",
4157                        );
4158                    }
4159                    return None;
4160                }
4161                self.advance();
4162                let ty = self.parse_type_annotation()?;
4163                if rest && !is_rest_array_annotation(&ty) {
4164                    self.error_at(ty.span, "rest parameter type must be an array");
4165                    return None;
4166                }
4167                if matches!(self.peek().kind, TokenKind::Equals) {
4168                    if rest {
4169                        self.error_rest_parameter_default();
4170                    } else {
4171                        self.error_at_peek(
4172                            "default values in function-type annotations are not yet supported",
4173                        );
4174                    }
4175                    return None;
4176                }
4177                params.push(TypeAnnotationField {
4178                    name,
4179                    ty,
4180                    optional,
4181                    readonly: false,
4182                    rest,
4183                    method: false,
4184                });
4185                match self.peek().kind {
4186                    TokenKind::Comma => {
4187                        if rest {
4188                            let comma = self.advance();
4189                            self.error_at(comma.span, "rest parameter must be the last parameter");
4190                            return None;
4191                        }
4192                        self.advance();
4193                        if matches!(self.peek().kind, TokenKind::RightParen) {
4194                            break;
4195                        }
4196                    }
4197                    TokenKind::RightParen => break,
4198                    _ => {
4199                        self.error_at_peek("expected `,` or `)`");
4200                        return None;
4201                    }
4202                }
4203            }
4204            self.check_parameter_order(params.iter().map(ParamOrder::of_field));
4205        }
4206        if !matches!(self.peek().kind, TokenKind::RightParen) {
4207            self.error_at_peek("expected `)`");
4208            return None;
4209        }
4210        self.advance();
4211        Some(params)
4212    }
4213
4214    fn parse_tuple_type_annotation(&mut self) -> Option<TypeAnnotation> {
4215        let open = self.advance();
4216        if matches!(self.peek().kind, TokenKind::RightBracket) {
4217            let close = self.peek().span;
4218            self.error_at(
4219                self.span(open.span.start, close.end),
4220                "tuple types must have at least one element",
4221            );
4222            return None;
4223        }
4224        let mut elements: Vec<TypeAnnotation> = Vec::new();
4225        loop {
4226            if matches!(self.peek().kind, TokenKind::DotDotDot) {
4227                self.error_at_with_help(
4228                    self.peek().span,
4229                    "rest elements in tuple types are not supported",
4230                    vec!["use an array type `T[]` for a list of varying length".to_string()],
4231                );
4232                return None;
4233            }
4234            // Labels may be mixed with unlabeled elements, as TypeScript allows
4235            // since 5.2.
4236            let label = self.skip_tuple_element_label();
4237            let mut ty = self.parse_type_annotation()?;
4238            let suffix_optional = matches!(self.peek().kind, TokenKind::Question);
4239            if suffix_optional && label != TupleElementLabel::None {
4240                self.error_at_peek_with_help(
4241                    "put the optional marker after the tuple element label",
4242                    vec!["write `name?: T` instead of `name: T?`".to_string()],
4243                );
4244                return None;
4245            }
4246            let optional = label == TupleElementLabel::Optional || suffix_optional;
4247            if optional {
4248                let end = if suffix_optional {
4249                    self.advance().span.end
4250                } else {
4251                    ty.span.end
4252                };
4253                let span = self.span(ty.span.start, end);
4254                ty = TypeAnnotation {
4255                    kind: TypeAnnotationKind::Optional(Box::new(ty)),
4256                    span,
4257                };
4258            } else if elements
4259                .iter()
4260                .any(|element| matches!(element.kind, TypeAnnotationKind::Optional(_)))
4261            {
4262                self.error_at(
4263                    ty.span,
4264                    "a required tuple element cannot follow an optional element",
4265                );
4266                return None;
4267            }
4268            elements.push(ty);
4269            match self.peek().kind {
4270                TokenKind::Comma => {
4271                    self.advance();
4272                    if matches!(self.peek().kind, TokenKind::RightBracket) {
4273                        break;
4274                    }
4275                }
4276                TokenKind::RightBracket => break,
4277                _ => {
4278                    self.error_at_peek("expected `,` or `]`");
4279                    return None;
4280                }
4281            }
4282        }
4283        if !matches!(self.peek().kind, TokenKind::RightBracket) {
4284            self.error_at_peek("expected `]`");
4285            return None;
4286        }
4287        let close = self.advance();
4288        Some(TypeAnnotation {
4289            kind: TypeAnnotationKind::Tuple(elements),
4290            span: self.span(open.span.start, close.span.end),
4291        })
4292    }
4293
4294    /// Labels document positions; retain only whether the label was optional.
4295    fn skip_tuple_element_label(&mut self) -> TupleElementLabel {
4296        if !is_property_name(&self.peek().kind) {
4297            return TupleElementLabel::None;
4298        }
4299        match self.peek_at(1).kind {
4300            TokenKind::Colon => {
4301                self.advance();
4302                self.advance();
4303                TupleElementLabel::Required
4304            }
4305            TokenKind::Question if matches!(self.peek_at(2).kind, TokenKind::Colon) => {
4306                self.advance();
4307                self.advance();
4308                self.advance();
4309                TupleElementLabel::Optional
4310            }
4311            _ => TupleElementLabel::None,
4312        }
4313    }
4314
4315    /// `[]` or `[]: V` — an index signature with no parameter, which TypeScript
4316    /// reads and rejects.
4317    fn peek_is_parameterless_index_signature(&self) -> bool {
4318        matches!(self.peek().kind, TokenKind::LeftBracket)
4319            && matches!(self.peek_at(1).kind, TokenKind::RightBracket)
4320    }
4321
4322    /// Diagnoses and skips a parameterless index signature (see
4323    /// `peek_is_parameterless_index_signature`) so the members after it still parse.
4324    /// `member_start` is where the member begins, at a `readonly` modifier if any.
4325    fn skip_parameterless_index_signature(&mut self, member_start: u32) -> Option<()> {
4326        self.advance();
4327        let close = self.advance();
4328        self.error_at_with_help(
4329            self.span(member_start, close.span.end),
4330            "an index signature must declare exactly one parameter",
4331            vec!["write `[key: string]: V`".to_string()],
4332        );
4333        if matches!(self.peek().kind, TokenKind::Colon) {
4334            self.advance();
4335            self.parse_type_annotation()?;
4336        }
4337        Some(())
4338    }
4339
4340    fn parse_template_type(&mut self, text: String) -> Option<TypeAnnotation> {
4341        let head = self.advance();
4342        let mut parts = vec![text];
4343        let mut substitutions = Vec::new();
4344        loop {
4345            substitutions.push(self.parse_type_annotation()?);
4346            let token = self.advance();
4347            match token.kind {
4348                TokenKind::TemplateMiddle(text) => parts.push(text),
4349                TokenKind::TemplateTail(text) => {
4350                    parts.push(text);
4351                    return Some(TypeAnnotation {
4352                        kind: TypeAnnotationKind::TemplateLiteral {
4353                            parts,
4354                            substitutions,
4355                        },
4356                        span: self.span(head.span.start, token.span.end),
4357                    });
4358                }
4359                _ => {
4360                    self.error_at_peek("expected template type continuation");
4361                    return None;
4362                }
4363            }
4364        }
4365    }
4366
4367    fn parse_index_signature(&mut self, readonly: bool) -> Option<crate::IndexSignatureAnnotation> {
4368        let open = self.advance();
4369        let parameter = self.expect_property_ident("expected index parameter name")?;
4370        self.reject_eval_or_arguments(parameter.span);
4371        if !matches!(self.peek().kind, TokenKind::Colon) {
4372            self.error_at_peek("expected `:` after index parameter name");
4373            return None;
4374        }
4375        self.advance();
4376        let key = self.parse_type_annotation()?;
4377        if !matches!(self.peek().kind, TokenKind::RightBracket) {
4378            self.error_at_peek("expected `]` after string index key type");
4379            return None;
4380        }
4381        self.advance();
4382        if !matches!(self.peek().kind, TokenKind::Colon) {
4383            self.error_at_peek("expected `:` before index value type");
4384            return None;
4385        }
4386        self.advance();
4387        let value = self.parse_type_annotation()?;
4388        Some(crate::IndexSignatureAnnotation {
4389            key,
4390            span: self.span(open.span.start, value.span.end),
4391            value,
4392            readonly,
4393        })
4394    }
4395
4396    fn parse_object_type_annotation(&mut self) -> Option<TypeAnnotation> {
4397        let open = self.advance();
4398        let mut fields: Vec<TypeAnnotationField> = Vec::new();
4399        let mut index = None;
4400        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
4401            && !self.peek_starts_top_level_item()
4402        {
4403            let member_start = self.peek().span.start;
4404            let readonly = self.eat_readonly_property_modifier();
4405            if self.peek_is_parameterless_index_signature() {
4406                self.skip_parameterless_index_signature(member_start)?;
4407            } else if self.peek_is_construct_signature() {
4408                self.reject_construct_signature()?;
4409            } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
4410                let signature = self.parse_index_signature(readonly)?;
4411                if index.is_some() {
4412                    self.error_at_with_help(
4413                        signature.span,
4414                        "duplicate string index signature",
4415                        vec![],
4416                    );
4417                }
4418                index = Some(Box::new(signature));
4419            } else {
4420                let field = self.parse_object_type_field(readonly, member_start)?;
4421                if let Some(existing) = fields.iter().find(|f| f.name.name == field.name.name) {
4422                    self.diagnostics.push(Diagnostic {
4423                        severity: Severity::Error,
4424                        span: field.name.span,
4425                        message: format!("duplicate field `{}` in object type", field.name.name),
4426                        help: vec![],
4427                        notes: vec![(existing.name.span, "first defined here".into())],
4428                    });
4429                }
4430                fields.push(field);
4431            }
4432            self.finish_type_member(self.prev_token_end(), "expected `;`, `,`, or `}`")?;
4433        }
4434        if !matches!(self.peek().kind, TokenKind::RightBrace) {
4435            self.error_at_peek("expected `}`");
4436            return None;
4437        }
4438        let close = self.advance();
4439        Some(TypeAnnotation {
4440            kind: TypeAnnotationKind::Object { index, fields },
4441            span: self.span(open.span.start, close.span.end),
4442        })
4443    }
4444
4445    /// A declaration or an `import` statement: what a top-level item starts with.
4446    /// `import(…)` and `import.meta` are expressions, not statements.
4447    fn peek_starts_top_level_item(&self) -> bool {
4448        let import_statement = matches!(self.peek().kind, TokenKind::Import)
4449            && matches!(
4450                self.peek_at(1).kind,
4451                TokenKind::LeftBrace
4452                    | TokenKind::Star
4453                    | TokenKind::Identifier
4454                    | TokenKind::StringLiteral(_)
4455            );
4456        import_statement || self.peek_starts_declaration()
4457    }
4458
4459    /// Whether the next tokens start a declaration (`function f`, `class C`, `type T`,
4460    /// …, optionally after `export`, `declare`, `async` or `abstract`) or an export
4461    /// statement (`export {`, `export *`, `export default`), which no type or class
4462    /// member can: a keyword is a member name only before `:`, `?` or `(`. A member
4463    /// list that reaches one was left unclosed, so it stops there and leaves the
4464    /// declaration to be parsed.
4465    fn peek_starts_declaration(&self) -> bool {
4466        if matches!(self.peek().kind, TokenKind::Export)
4467            && matches!(
4468                self.peek_at(1).kind,
4469                TokenKind::Default | TokenKind::LeftBrace | TokenKind::Star
4470            )
4471        {
4472            return true;
4473        }
4474        let mut offset = 0;
4475        while self.peek_at_is_declaration_modifier(offset) {
4476            offset += 1;
4477        }
4478        let declaration_keyword = matches!(
4479            self.peek_at(offset).kind,
4480            TokenKind::Function
4481                | TokenKind::Class
4482                | TokenKind::Let
4483                | TokenKind::Const
4484                | TokenKind::Enum
4485                | TokenKind::Interface
4486        ) || self.peek_at_is_word(offset, "type");
4487        declaration_keyword && matches!(self.peek_at(offset + 1).kind, TokenKind::Identifier)
4488    }
4489
4490    /// `export`, `declare`, `async` or `abstract` before a declaration keyword.
4491    fn peek_at_is_declaration_modifier(&self, offset: usize) -> bool {
4492        matches!(self.peek_at(offset).kind, TokenKind::Export)
4493            || ["declare", "async", "abstract"]
4494                .iter()
4495                .any(|word| self.peek_at_is_word(offset, word))
4496    }
4497
4498    /// `new (params): T` or `new <T>(params): T` inside a type literal: a construct
4499    /// signature, which TypeScript reads where it would otherwise see a method named
4500    /// `new`. A quoted `"new"()`, an optional `new?()` and a field `new: T` are
4501    /// ordinary members, as in TypeScript.
4502    fn peek_is_construct_signature(&self) -> bool {
4503        matches!(self.peek().kind, TokenKind::New)
4504            && matches!(
4505                self.peek_at(1).kind,
4506                TokenKind::LeftParen | TokenKind::LessThan
4507            )
4508    }
4509
4510    /// Only interfaces declare construct signatures, so one in a type literal (see
4511    /// `peek_is_construct_signature`) is diagnosed, then skipped rather than misread
4512    /// as a method named `new`.
4513    fn reject_construct_signature(&mut self) -> Option<()> {
4514        let keyword = self.advance();
4515        self.error_at_with_help(
4516            keyword.span,
4517            "construct signatures are not supported in object types",
4518            vec![
4519                "declare an `interface` with the `new (…)` signature, or pass a factory \
4520                 function such as `() => T`"
4521                    .to_string(),
4522            ],
4523        );
4524        if matches!(self.peek().kind, TokenKind::LessThan) {
4525            self.parse_generic_param_list()?;
4526        }
4527        if !matches!(self.peek().kind, TokenKind::LeftParen) {
4528            self.error_at_peek("expected `(` after construct signature type parameters");
4529            return None;
4530        }
4531        self.parse_object_type_method_signature(keyword.span.start)?;
4532        Some(())
4533    }
4534
4535    /// `name: T`, `name?: T` or the method signature `name(params): T`.
4536    fn parse_object_type_field(
4537        &mut self,
4538        readonly: bool,
4539        member_start: u32,
4540    ) -> Option<TypeAnnotationField> {
4541        let name = self.expect_property_ident("expected field name in object type")?;
4542        // `name?: T` — omittable at construction; reads widen to `T | undefined`.
4543        let optional = matches!(self.peek().kind, TokenKind::Question);
4544        if optional {
4545            self.advance();
4546        }
4547        let method = matches!(self.peek().kind, TokenKind::LeftParen);
4548        let ty = if method {
4549            if readonly {
4550                self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
4551            }
4552            self.parse_object_type_method_signature(name.span.start)?
4553        } else {
4554            if !matches!(self.peek().kind, TokenKind::Colon) {
4555                self.error_at_peek("expected `:` after field name");
4556                return None;
4557            }
4558            self.advance();
4559            self.parse_type_annotation()?
4560        };
4561        Some(TypeAnnotationField {
4562            name,
4563            ty,
4564            optional,
4565            readonly,
4566            rest: false,
4567            method,
4568        })
4569    }
4570
4571    /// `m(params): T` inside a type literal, spelled as the function-typed field
4572    /// `m: (params) => T` — the two are interchangeable in a structural shape, and
4573    /// interfaces already accept both spellings for the same member. `start` is the
4574    /// member name's offset, so the annotation's span covers the name too.
4575    fn parse_object_type_method_signature(&mut self, start: u32) -> Option<TypeAnnotation> {
4576        self.advance();
4577        let params = self.parse_function_type_params()?;
4578        if !matches!(self.peek().kind, TokenKind::Colon) {
4579            self.error_at_peek("expected `:` and return type after method parameters");
4580            return None;
4581        }
4582        self.advance();
4583        let return_type = self.parse_type_annotation()?;
4584        let end = return_type.span.end;
4585        Some(TypeAnnotation {
4586            kind: TypeAnnotationKind::Function {
4587                params,
4588                return_type: Box::new(return_type),
4589            },
4590            span: self.span(start, end),
4591        })
4592    }
4593
4594    /// An identifier that names a binding or a type, which rejects the words strict
4595    /// mode reserves (see [`STRICT_MODE_RESERVED_WORDS`]).
4596    fn expect_identifier(&mut self, message: &str) -> Option<Token> {
4597        let tok = self.expect_identifier_name(message)?;
4598        self.reject_strict_mode_reserved_word(&tok);
4599        Some(tok)
4600    }
4601
4602    /// The name a variable, parameter, function or `catch` binds. Strict mode also
4603    /// forbids `eval` and `arguments` there, though not as a class, enum or type
4604    /// name (TypeScript's TS1100 covers the same positions).
4605    fn expect_binding_identifier(&mut self, message: &str) -> Option<Token> {
4606        let tok = self.expect_identifier(message)?;
4607        self.reject_eval_or_arguments(tok.span);
4608        Some(tok)
4609    }
4610
4611    /// An identifier in a position where strict mode's reserved words are allowed: an
4612    /// enum member, a property, or the name an import specifier refers to.
4613    fn expect_identifier_name(&mut self, message: &str) -> Option<Token> {
4614        if matches!(self.peek().kind, TokenKind::Identifier) {
4615            return Some(self.advance());
4616        }
4617        if is_reserved_identifier_word(&self.peek().kind) {
4618            let tok = self.advance();
4619            let keyword = &self.source[tok.span.start as usize..tok.span.end as usize];
4620            self.error_at_with_help(
4621                tok.span,
4622                format!("`{keyword}` is a reserved keyword and can't be used as a name"),
4623                vec![format!(
4624                    "rename it, for example: `{}`",
4625                    reserved_keyword_rename_example(keyword)
4626                )],
4627            );
4628            return None;
4629        }
4630        self.error_at_peek(message.to_string());
4631        None
4632    }
4633
4634    /// Reports a binding named by a word strict mode reserves. Parsing continues
4635    /// with the name: nothing else about it is wrong.
4636    fn reject_strict_mode_reserved_word(&mut self, tok: &Token) {
4637        let word = &self.source[tok.span.start as usize..tok.span.end as usize];
4638        if !STRICT_MODE_RESERVED_WORDS.contains(&word) {
4639            return;
4640        }
4641        self.error_at_with_help(
4642            tok.span,
4643            format!("`{word}` is a reserved word in strict mode and can't be used as a name"),
4644            vec![format!(
4645                "rename it, for example: `{}`",
4646                reserved_keyword_rename_example(word)
4647            )],
4648        );
4649    }
4650
4651    /// Reports a binding named `eval` or `arguments`. Parsing continues with the
4652    /// name: nothing else about it is wrong.
4653    fn reject_eval_or_arguments(&mut self, span: Span) {
4654        let word = &self.source[span.start as usize..span.end as usize];
4655        if !matches!(word, "eval" | "arguments") {
4656            return;
4657        }
4658        self.error_at_with_help(
4659            span,
4660            format!("`{word}` can't name a binding in strict mode"),
4661            vec![format!(
4662                "rename it, for example: `{}`",
4663                reserved_keyword_rename_example(word)
4664            )],
4665        );
4666    }
4667
4668    fn ident_from_token(&self, tok: &Token) -> Ident {
4669        Ident {
4670            name: self.source[tok.span.start as usize..tok.span.end as usize].to_string(),
4671            span: tok.span,
4672        }
4673    }
4674
4675    fn property_ident_from_token(&self, tok: &Token) -> Ident {
4676        if let TokenKind::StringLiteral(s) = &tok.kind {
4677            Ident {
4678                name: s.clone(),
4679                span: tok.span,
4680            }
4681        } else if let TokenKind::NumberLiteral(value) = &tok.kind {
4682            Ident {
4683                name: crate::runtime::number::format_number_js(*value),
4684                span: tok.span,
4685            }
4686        } else {
4687            self.ident_from_token(tok)
4688        }
4689    }
4690
4691    fn expect_property_name(&mut self, message: &str) -> Option<Token> {
4692        if !is_property_name(&self.peek().kind) {
4693            self.error_at_peek(message.to_string());
4694            return None;
4695        }
4696        Some(self.advance())
4697    }
4698
4699    fn expect_property_ident(&mut self, message: &str) -> Option<Ident> {
4700        if !is_property_name(&self.peek().kind)
4701            && !matches!(
4702                self.peek().kind,
4703                TokenKind::StringLiteral(_) | TokenKind::NumberLiteral(_)
4704            )
4705        {
4706            self.error_at_peek(message.to_string());
4707            return None;
4708        }
4709        let tok = self.advance();
4710        Some(self.property_ident_from_token(&tok))
4711    }
4712
4713    /// The span of a `readonly` modifier that [`Parser::eat_readonly_property_modifier`]
4714    /// consumed at `member_start`.
4715    fn readonly_modifier_span(&self, member_start: u32) -> Span {
4716        self.span(member_start, member_start + "readonly".len() as u32)
4717    }
4718
4719    /// Reports `readonly` on a method, accessor or method signature, as TypeScript
4720    /// does (TS1024). Parsing continues: the modifier changes nothing else.
4721    fn reject_readonly_modifier(&mut self, span: Span) {
4722        self.error_at_with_help(
4723            span,
4724            "`readonly` can only modify a property or index signature",
4725            vec!["remove `readonly`".to_string()],
4726        );
4727    }
4728
4729    fn eat_readonly_property_modifier(&mut self) -> bool {
4730        if self.peek_identifier_text_is("readonly")
4731            && !self.line_break_after_peek()
4732            && (self.peek_starts_property_after_readonly()
4733                || matches!(self.peek_at(1).kind, TokenKind::LeftBracket))
4734        {
4735            self.advance();
4736            return true;
4737        }
4738        false
4739    }
4740
4741    fn peek_starts_property_after_readonly(&self) -> bool {
4742        let name = &self.peek_at(1).kind;
4743        let after_name = &self.peek_at(2).kind;
4744        let after_optional = &self.peek_at(3).kind;
4745        // A member starts its type with `:`, or its parameter list with `(` — otherwise
4746        // `readonly` is the member's own name.
4747        let starts_member_type =
4748            |kind: &TokenKind| matches!(kind, TokenKind::Colon | TokenKind::LeftParen);
4749        (is_property_name(name) || matches!(name, TokenKind::StringLiteral(_)))
4750            && (starts_member_type(after_name)
4751                || matches!(after_name, TokenKind::Question) && starts_member_type(after_optional))
4752    }
4753
4754    /// `as` and `satisfies`, which lex as identifiers but continue an expression.
4755    fn peek_at_is_binary_word(&self, offset: usize) -> bool {
4756        self.peek_at_is_word(offset, "as") || self.peek_at_is_word(offset, "satisfies")
4757    }
4758
4759    fn peek_identifier_text_is(&self, expected: &str) -> bool {
4760        self.token_is_word(self.peek(), expected)
4761    }
4762
4763    fn peek_at_is_word(&self, offset: usize, expected: &str) -> bool {
4764        self.token_is_word(self.peek_at(offset), expected)
4765    }
4766
4767    fn token_is_word(&self, tok: &Token, expected: &str) -> bool {
4768        matches!(tok.kind, TokenKind::Identifier)
4769            && &self.source[tok.span.start as usize..tok.span.end as usize] == expected
4770    }
4771
4772    /// `delete` is lexed as an identifier: `map.delete(k)`, a `delete()` method and
4773    /// `submilli:http`'s `delete(url)` function all use the name. So it is the
4774    /// operator only when an operand that can't be an argument list follows it, and
4775    /// `delete (o.x)` stays a call. `as` and `satisfies` are lexed as identifiers too:
4776    /// `delete as unknown` casts the function value, while `delete as.x` deletes from a
4777    /// binding named `as`.
4778    fn at_delete_operator(&self) -> bool {
4779        if !self.peek_identifier_text_is("delete") {
4780            return false;
4781        }
4782        if !matches!(
4783            self.peek_at(1).kind,
4784            TokenKind::Identifier | TokenKind::This
4785        ) {
4786            return false;
4787        }
4788        let next_is_cast_keyword =
4789            self.peek_at_is_word(1, "as") || self.peek_at_is_word(1, "satisfies");
4790        !next_is_cast_keyword || cannot_start_type(&self.peek_at(2).kind)
4791    }
4792
4793    /// `type X …` commits to a type-alias declaration only when followed by an
4794    /// identifier, mirroring TypeScript; `type = 5`, `type;`, `type(x)` stay
4795    /// expression statements over a binding named `type`.
4796    fn at_type_alias_head(&self) -> bool {
4797        self.peek_identifier_text_is("type")
4798            && matches!(self.peek_at(1).kind, TokenKind::Identifier)
4799    }
4800
4801    /// Assignment is the lowest-precedence expression and right-associative:
4802    /// `a = b = c` assigns `c` to both, and yields it.
4803    fn parse_expression(&mut self) -> Option<ExprId> {
4804        self.with_recursion_limit(Self::parse_expression_inner)
4805    }
4806
4807    fn parse_expression_inner(&mut self) -> Option<ExprId> {
4808        let written = self.parse_conditional()?;
4809        let shift_assignment = self.peek_shift().filter(|shift| shift.assignment);
4810        if !is_assign_lookahead(&self.peek().kind) && shift_assignment.is_none() {
4811            return Some(written);
4812        }
4813        let target_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4814        let target = self.assignment_target(written)?;
4815        let op_tok = self.advance();
4816        let mut op_span = op_tok.span;
4817        let op = if let Some(ShiftToken { op, tokens, .. }) = shift_assignment {
4818            for _ in 1..tokens {
4819                op_span.end = self.advance().span.end;
4820            }
4821            Some(op)
4822        } else {
4823            compound_op_for_token(&op_tok.kind)
4824        };
4825        let value = self.parse_expression()?;
4826        let value_span = parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
4827        parse_arena_result(
4828            self.ast.try_push_expr(Expr {
4829                kind: ExprKind::Assign {
4830                    target,
4831                    op,
4832                    op_span,
4833                    value,
4834                },
4835                span: self.span(target_span.start, value_span.end),
4836            }),
4837            &mut self.fatal,
4838        )
4839    }
4840
4841    /// The binding, field, or element `written` names, through any parentheses:
4842    /// `(a) = 1` assigns `a`, as in JavaScript.
4843    fn assignment_target(&mut self, written: ExprId) -> Option<ExprId> {
4844        let mut target = written;
4845        while let ExprKind::Paren(inner) =
4846            parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind
4847        {
4848            target = inner;
4849        }
4850        if matches!(
4851            parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind,
4852            ExprKind::Identifier(_) | ExprKind::FieldAccess { .. } | ExprKind::IndexAccess { .. }
4853        ) {
4854            return Some(target);
4855        }
4856        let error_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4857        self.error_at_with_help(
4858            error_span,
4859            "invalid assignment target",
4860            vec![
4861                "assign to a variable, a field (`o.f = …`), or an element (`a[i] = …`)".to_string(),
4862            ],
4863        );
4864        None
4865    }
4866
4867    fn parse_conditional(&mut self) -> Option<ExprId> {
4868        self.reject_async_modifier();
4869        if self.is_arrow_start() {
4870            return self.parse_arrow();
4871        }
4872        let cond = self.parse_binary(0)?;
4873        // Ternary sits below every binary op; right-associative.
4874        if !matches!(self.peek().kind, TokenKind::Question) {
4875            return Some(cond);
4876        }
4877        self.advance();
4878        let then_ = self.parse_expression()?;
4879        if !matches!(self.peek().kind, TokenKind::Colon) {
4880            self.error_at_peek_with_help(
4881                "expected `:` to complete ternary expression",
4882                vec!["const x = cond ? then : else;".to_string()],
4883            );
4884            return None;
4885        }
4886        self.advance();
4887        let else_ = self.parse_expression()?;
4888        let cond_span = parse_arena_result(self.ast.try_expr(cond), &mut self.fatal)?.span;
4889        let else_span = parse_arena_result(self.ast.try_expr(else_), &mut self.fatal)?.span;
4890        parse_arena_result(
4891            self.ast.try_push_expr(Expr {
4892                kind: ExprKind::Ternary { cond, then_, else_ },
4893                span: self.span(cond_span.start, else_span.end),
4894            }),
4895            &mut self.fatal,
4896        )
4897    }
4898
4899    fn is_arrow_start(&self) -> bool {
4900        match self.peek().kind {
4901            TokenKind::Identifier => matches!(self.peek_at(1).kind, TokenKind::Arrow),
4902            TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos),
4903            _ => false,
4904        }
4905    }
4906
4907    /// Reject an `async` that modifies a function, arrow or method, and keep a
4908    /// binding named `async` as it is.
4909    fn reject_async_modifier(&mut self) {
4910        if !self.peek_identifier_text_is("async") {
4911            return;
4912        }
4913        let modifies = match self.peek_at(1).kind {
4914            TokenKind::Function => true,
4915            // `async (x) => …`, not a call of a function named `async`.
4916            TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos + 1),
4917            // `async x => …`, or a method `async run(…)` / `async run<T>(…)`.
4918            // `async in (o)` and `async as (T)` are operators on a binding named
4919            // `async`.
4920            ref name
4921                if is_property_name(name)
4922                    && !matches!(name, TokenKind::In)
4923                    && !self.peek_at_is_binary_word(1) =>
4924            {
4925                matches!(
4926                    self.peek_at(2).kind,
4927                    TokenKind::Arrow | TokenKind::LeftParen | TokenKind::LessThan
4928                )
4929            }
4930            _ => false,
4931        };
4932        if modifies {
4933            self.report_async_modifier();
4934        }
4935    }
4936
4937    /// Reject an `async` that starts a class or object-literal method. A member's
4938    /// `async` is never a value an operator applies to, so `async *run`,
4939    /// `async in()` and `async "s"()` are methods too. After a line break,
4940    /// `async` is a member of its own, as in TypeScript.
4941    fn reject_async_member_modifier(&mut self) {
4942        if !self.peek_identifier_text_is("async") || self.line_break_after_peek() {
4943            return;
4944        }
4945        let modifies = match self.peek_at(1).kind {
4946            TokenKind::Star
4947            | TokenKind::LeftBracket
4948            | TokenKind::StringLiteral(_)
4949            | TokenKind::NumberLiteral(_) => true,
4950            ref name if is_property_name(name) => {
4951                matches!(
4952                    self.peek_at(2).kind,
4953                    TokenKind::LeftParen | TokenKind::LessThan
4954                )
4955            }
4956            _ => false,
4957        };
4958        if modifies {
4959            self.report_async_modifier();
4960        }
4961    }
4962
4963    /// There are no promises here, so name the rewrite, then consume `async`
4964    /// and parse on as if it were absent: the body's `await`s are reported in
4965    /// the same run.
4966    fn report_async_modifier(&mut self) {
4967        let tok = self.advance();
4968        self.error_at_with_help(
4969            tok.span,
4970            "`async` functions are not supported",
4971            vec![
4972                "calls return their value directly: remove `async`, write the return type \
4973                 `T` instead of `Promise<T>`, and drop `await`"
4974                    .to_string(),
4975            ],
4976        );
4977    }
4978
4979    /// `await` followed on the same line by an operand that cannot continue a
4980    /// binding named `await`: an identifier, a keyword, a literal or a template. `await(x)`
4981    /// and `await[i]` stay a call and an index; the typechecker names the fix if
4982    /// `await` then does not resolve. Calls are synchronous, so report it and
4983    /// parse on: the operand stays, and one run names every `await` in the program.
4984    fn reject_await_operator(&mut self) -> bool {
4985        if !self.peek_identifier_text_is("await") {
4986            return false;
4987        }
4988        let operand_starts = matches!(
4989            self.peek_at(1).kind,
4990            TokenKind::Identifier
4991                | TokenKind::This
4992                | TokenKind::Super
4993                | TokenKind::New
4994                | TokenKind::Function
4995                | TokenKind::StringLiteral(_)
4996                | TokenKind::NumberLiteral(_)
4997                | TokenKind::NullLiteral
4998                | TokenKind::BooleanLiteral(_)
4999                // A tagged template is not supported, so a template after `await`
5000                // can only be its operand.
5001                | TokenKind::TemplateHead(_)
5002                | TokenKind::TemplateNoSubstitution(_)
5003        ) && !self.peek_at_is_binary_word(1);
5004        if !operand_starts || self.line_break_after_peek() {
5005            return false;
5006        }
5007        let tok = self.advance();
5008        self.error_at_with_help(
5009            tok.span,
5010            "`await` is not supported",
5011            vec!["calls return their value directly: remove `await`".to_string()],
5012        );
5013        true
5014    }
5015
5016    /// Index just past the `)` matching the `(` at `index`; `None` when the parens
5017    /// never balance. Pure lookahead — emits no diagnostics.
5018    fn index_after_matching_paren(&self, index: usize) -> Option<usize> {
5019        if !matches!(
5020            self.tokens.get(index).map(|t| &t.kind),
5021            Some(TokenKind::LeftParen)
5022        ) {
5023            return None;
5024        }
5025        self.scan_past_balanced(
5026            index,
5027            |k| matches!(k, TokenKind::LeftParen),
5028            |k| matches!(k, TokenKind::RightParen),
5029        )
5030    }
5031
5032    fn find_arrow_after_matching_paren(&self, open: usize) -> bool {
5033        let Some(i) = self.index_after_matching_paren(open) else {
5034            return false;
5035        };
5036        let after_type = if matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Colon)) {
5037            match self.scan_past_type_annotation(i + 1, TypePos::ArrowReturn) {
5038                Some(p) => p,
5039                None => return false,
5040            }
5041        } else {
5042            i
5043        };
5044        matches!(
5045            self.tokens.get(after_type).map(|t| &t.kind),
5046            Some(TokenKind::Arrow),
5047        )
5048    }
5049
5050    // Pure lookahead used only by the arrow-disambiguator. No diagnostics.
5051    // Must accept every form `parse_type_annotation` accepts: a form missing
5052    // here makes the disambiguator reject a valid arrow, so the expression
5053    // parser reports a misleading `expected expression` at the return type.
5054    // `arrow_return_type_scanner_matches_type_grammar` pins the two together.
5055    fn scan_past_type_annotation(&self, start: usize, type_pos: TypePos) -> Option<usize> {
5056        self.scan_past_type_annotation_at_depth(start, type_pos, 0)
5057    }
5058
5059    fn scan_past_type_annotation_at_depth(
5060        &self,
5061        start: usize,
5062        type_pos: TypePos,
5063        depth: usize,
5064    ) -> Option<usize> {
5065        if depth >= MAX_PARSE_DEPTH {
5066            return None;
5067        }
5068        if matches!(
5069            self.tokens.get(start).map(|t| &t.kind),
5070            Some(TokenKind::Identifier)
5071        ) && self
5072            .tokens
5073            .get(start + 1)
5074            .is_some_and(|t| self.token_is_word(t, "is"))
5075        {
5076            return self.scan_past_type_annotation_at_depth(start + 2, type_pos, depth + 1);
5077        }
5078        // Leading `|` (prettier's multiline union format).
5079        let start = if matches!(
5080            self.tokens.get(start).map(|t| &t.kind),
5081            Some(TokenKind::Pipe)
5082        ) {
5083            start + 1
5084        } else {
5085            start
5086        };
5087        let mut i = self.scan_past_single_type_member(start, type_pos, depth + 1)?;
5088        while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Pipe)) {
5089            i = self.scan_past_single_type_member(i + 1, type_pos, depth + 1)?;
5090        }
5091        Some(i)
5092    }
5093
5094    fn scan_past_single_type_member(
5095        &self,
5096        start: usize,
5097        type_pos: TypePos,
5098        depth: usize,
5099    ) -> Option<usize> {
5100        if depth >= MAX_PARSE_DEPTH {
5101            return None;
5102        }
5103        if self.is_contextual_type_operator(start, "keyof")
5104            || self.is_contextual_type_operator(start, "readonly")
5105        {
5106            return self.scan_past_single_type_member(start + 1, type_pos, depth + 1);
5107        }
5108        let mut i = start;
5109        match self.tokens.get(i)?.kind {
5110            TokenKind::Identifier | TokenKind::Void => {
5111                i += 1;
5112                while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Dot)) {
5113                    if !matches!(
5114                        self.tokens.get(i + 1).map(|t| &t.kind),
5115                        Some(TokenKind::Identifier)
5116                    ) {
5117                        return None;
5118                    }
5119                    i += 2;
5120                }
5121                if matches!(
5122                    self.tokens.get(i).map(|t| &t.kind),
5123                    Some(TokenKind::LessThan)
5124                ) {
5125                    i = self.scan_past_balanced(
5126                        i,
5127                        |k| matches!(k, TokenKind::LessThan),
5128                        |k| matches!(k, TokenKind::GreaterThan),
5129                    )?;
5130                }
5131            }
5132            TokenKind::NullLiteral
5133            | TokenKind::StringLiteral(_)
5134            | TokenKind::NumberLiteral(_)
5135            | TokenKind::BooleanLiteral(_) => {
5136                i += 1;
5137            }
5138            TokenKind::LeftBrace => {
5139                i = self.scan_past_balanced(
5140                    i,
5141                    |k| matches!(k, TokenKind::LeftBrace),
5142                    |k| matches!(k, TokenKind::RightBrace),
5143                )?;
5144            }
5145            TokenKind::LeftBracket => {
5146                i = self.scan_past_balanced(
5147                    i,
5148                    |k| matches!(k, TokenKind::LeftBracket),
5149                    |k| matches!(k, TokenKind::RightBracket),
5150                )?;
5151            }
5152            // Same `(`-disambiguation `parse_type_array` uses, and it has to agree:
5153            // a function type carries its own `=>`, so everything after it is the
5154            // return type and the scan ends with that. Grouping parens instead fall
5155            // through to the postfix `[]` loop below.
5156            TokenKind::LeftParen => {
5157                let is_function_type = self.paren_at_opens_function_type(i, type_pos);
5158                i = self.scan_past_balanced(
5159                    i,
5160                    |k| matches!(k, TokenKind::LeftParen),
5161                    |k| matches!(k, TokenKind::RightParen),
5162                )?;
5163                if is_function_type {
5164                    if !matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)) {
5165                        return None;
5166                    }
5167                    return self.scan_past_type_annotation_at_depth(
5168                        i + 1,
5169                        TypePos::Anywhere,
5170                        depth + 1,
5171                    );
5172                }
5173            }
5174            _ => return None,
5175        }
5176        while matches!(
5177            self.tokens.get(i).map(|t| &t.kind),
5178            Some(TokenKind::LeftBracket)
5179        ) {
5180            if matches!(
5181                self.tokens.get(i + 1).map(|t| &t.kind),
5182                Some(TokenKind::RightBracket)
5183            ) {
5184                i += 2;
5185                continue;
5186            }
5187            // `T[K]`: an indexed access type, whose index is a whole type.
5188            i = self.scan_past_type_annotation_at_depth(i + 1, TypePos::Anywhere, depth + 1)?;
5189            if !matches!(
5190                self.tokens.get(i).map(|t| &t.kind),
5191                Some(TokenKind::RightBracket)
5192            ) {
5193                return None;
5194            }
5195            i += 1;
5196        }
5197        Some(i)
5198    }
5199
5200    // `start` must index the opening token; returns the index just past the
5201    // matching close, or None if the tokens run out first.
5202    fn scan_past_balanced(
5203        &self,
5204        start: usize,
5205        is_open: fn(&TokenKind) -> bool,
5206        is_close: fn(&TokenKind) -> bool,
5207    ) -> Option<usize> {
5208        // At most one unmatched opening per token: depth is bounded by the
5209        // token vector's length, so use its index type rather than a signed count.
5210        let mut depth: usize = 1;
5211        let mut i = start + 1;
5212        while i < self.tokens.len() && depth > 0 {
5213            let token = self.tokens.get(i)?;
5214            let kind = &token.kind;
5215            if is_open(kind) {
5216                depth += 1;
5217            } else if is_close(kind) {
5218                depth -= 1;
5219            } else if matches!(kind, TokenKind::Eof) {
5220                return None;
5221            }
5222            i += 1;
5223        }
5224        if depth == 0 { Some(i) } else { None }
5225    }
5226
5227    fn parse_arrow(&mut self) -> Option<ExprId> {
5228        let start_span = self.peek().span;
5229
5230        let params = match self.peek().kind {
5231            TokenKind::Identifier => {
5232                let name_tok = self.advance();
5233                self.reject_eval_or_arguments(name_tok.span);
5234                let name = self.ident_from_token(&name_tok);
5235                vec![ParamDecl {
5236                    name,
5237                    ty: None,
5238                    default: None,
5239                    optional: false,
5240                    pattern: None,
5241                    rest: false,
5242                    modifiers: None,
5243                }]
5244            }
5245            TokenKind::LeftParen => {
5246                self.advance();
5247                let params = if matches!(self.peek().kind, TokenKind::RightParen) {
5248                    Vec::new()
5249                } else {
5250                    self.parse_arrow_param_list()?
5251                };
5252                if !matches!(self.peek().kind, TokenKind::RightParen) {
5253                    self.error_at_peek("expected `)`");
5254                    return None;
5255                }
5256                self.advance();
5257                params
5258            }
5259            _ => return self.invariant_failure("is_arrow_start guarded the entry"),
5260        };
5261
5262        let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
5263            self.advance();
5264            self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
5265        } else {
5266            (None, None)
5267        };
5268
5269        if !matches!(self.peek().kind, TokenKind::Arrow) {
5270            self.error_at_peek("expected `=>` in arrow function");
5271            return None;
5272        }
5273        // Like TypeScript, the arrow still parses, so later errors keep their spans.
5274        if self.line_break_before_peek() {
5275            let arrow_span = self.peek().span;
5276            self.error_at(arrow_span, "a line break is not allowed before `=>`");
5277        }
5278        self.advance();
5279
5280        let (body, end_pos) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
5281            let block = self.parse_block()?;
5282            let span = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?.span;
5283            (ArrowBody::Block(block), span.end)
5284        } else {
5285            let expr = self.parse_expression()?;
5286            let span = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?.span;
5287            (ArrowBody::Expr(expr), span.end)
5288        };
5289
5290        parse_arena_result(
5291            self.ast.try_push_expr(Expr {
5292                kind: ExprKind::Arrow {
5293                    params,
5294                    return_type,
5295                    type_predicate,
5296                    body,
5297                },
5298                span: self.span(start_span.start, end_pos),
5299            }),
5300            &mut self.fatal,
5301        )
5302    }
5303
5304    /// Function expressions share parameter/body parsing with arrows, but retain
5305    /// their own receiver and optional recursive-name binding.
5306    fn parse_function_expression(&mut self) -> Option<ExprId> {
5307        let kw = self.advance();
5308
5309        if matches!(self.peek().kind, TokenKind::LessThan) {
5310            self.error_at_peek("generic function expressions are not supported");
5311            return None;
5312        }
5313
5314        let self_name = if matches!(self.peek().kind, TokenKind::Identifier) {
5315            let name_tok = self.advance();
5316            self.reject_eval_or_arguments(name_tok.span);
5317            Some(self.ident_from_token(&name_tok))
5318        } else {
5319            None
5320        };
5321
5322        if !matches!(self.peek().kind, TokenKind::LeftParen) {
5323            self.error_at_peek("expected `(` after `function`");
5324            return None;
5325        }
5326        self.advance();
5327
5328        let this_type = if matches!(self.peek().kind, TokenKind::This) {
5329            self.advance();
5330            if !matches!(self.peek().kind, TokenKind::Colon) {
5331                self.error_at_peek("expected a type annotation after `this`");
5332                return None;
5333            }
5334            self.advance();
5335            let ty = self.parse_type_annotation()?;
5336            if matches!(self.peek().kind, TokenKind::Comma) {
5337                self.advance();
5338            } else if !matches!(self.peek().kind, TokenKind::RightParen) {
5339                self.error_at_peek("expected `,` or `)` after the `this` parameter");
5340                return None;
5341            }
5342            Some(ty)
5343        } else {
5344            None
5345        };
5346        let params = if matches!(self.peek().kind, TokenKind::RightParen) {
5347            Vec::new()
5348        } else {
5349            let saved_class = self.class_member_body_depth;
5350            let saved_function = self.function_expression_body_depth;
5351            self.class_member_body_depth = 0;
5352            self.function_expression_body_depth = 1;
5353            let params = self.parse_arrow_param_list();
5354            self.class_member_body_depth = saved_class;
5355            self.function_expression_body_depth = saved_function;
5356            params?
5357        };
5358        if !matches!(self.peek().kind, TokenKind::RightParen) {
5359            self.error_at_peek("expected `)`");
5360            return None;
5361        }
5362        self.advance();
5363
5364        let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
5365            self.advance();
5366            self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
5367        } else {
5368            (None, None)
5369        };
5370
5371        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
5372            self.error_at_peek("expected `{` to open the function body");
5373            return None;
5374        }
5375        let saved = self.class_member_body_depth;
5376        self.class_member_body_depth = 0;
5377        self.function_expression_body_depth += 1;
5378        let block = self.parse_block();
5379        self.function_expression_body_depth -= 1;
5380        self.class_member_body_depth = saved;
5381        let block = block?;
5382        let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
5383            .span
5384            .end;
5385
5386        let function = parse_arena_result(
5387            self.ast.try_push_expr(Expr {
5388                kind: ExprKind::Arrow {
5389                    params,
5390                    return_type,
5391                    type_predicate,
5392                    body: ArrowBody::Block(block),
5393                },
5394                span: self.span(kw.span.start, end),
5395            }),
5396            &mut self.fatal,
5397        )?;
5398        parse_arena_result(
5399            self.ast.try_push_expr(Expr {
5400                kind: ExprKind::FunctionExpression {
5401                    name: self_name,
5402                    this_type,
5403                    function,
5404                },
5405                span: self.span(kw.span.start, end),
5406            }),
5407            &mut self.fatal,
5408        )
5409    }
5410
5411    /// Shorthand methods still use arrow lowering. Reject their receiver syntax
5412    /// so an enclosing class or function receiver cannot leak into the body.
5413    fn parse_outer_this_boundary<T>(
5414        &mut self,
5415        parse: impl FnOnce(&mut Self) -> Option<T>,
5416    ) -> Option<T> {
5417        let saved = self.class_member_body_depth;
5418        self.class_member_body_depth = 0;
5419        let saved_function = self.function_expression_body_depth;
5420        self.function_expression_body_depth = 0;
5421        let parsed = parse(self);
5422        self.class_member_body_depth = saved;
5423        self.function_expression_body_depth = saved_function;
5424        parsed
5425    }
5426
5427    fn parse_arrow_param_list(&mut self) -> Option<Vec<ParamDecl>> {
5428        let mut params = Vec::new();
5429        loop {
5430            let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
5431                self.advance();
5432                true
5433            } else {
5434                false
5435            };
5436            // The lowering pass rewrites expression-body arrows to block form when any param
5437            // has a destructuring pattern.
5438            let (name, pattern) = match self.peek().kind {
5439                TokenKind::LeftBrace | TokenKind::LeftBracket => {
5440                    if rest {
5441                        self.error_at_peek("rest parameter cannot be destructured");
5442                        return None;
5443                    }
5444                    let binding = self.parse_binding()?;
5445                    let placeholder = Ident {
5446                        name: String::new(),
5447                        span: binding.span(),
5448                    };
5449                    (placeholder, Some(binding))
5450                }
5451                _ => {
5452                    let name_tok = self.expect_binding_identifier(if rest {
5453                        "expected identifier after `...`"
5454                    } else {
5455                        "expected parameter name"
5456                    })?;
5457                    let name = self.ident_from_token(&name_tok);
5458                    (name, None)
5459                }
5460            };
5461            let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
5462            let ty = if matches!(self.peek().kind, TokenKind::Colon) {
5463                self.advance();
5464                Some(self.parse_type_annotation()?)
5465            } else {
5466                // Rest without annotation is OK at parse time; the typechecker fills it
5467                // from a contextual function-type hint if available.
5468                None
5469            };
5470            let default = self.parse_parameter_default(rest, optional)?;
5471            params.push(ParamDecl {
5472                name,
5473                ty,
5474                default,
5475                optional,
5476                pattern,
5477                rest,
5478                modifiers: None,
5479            });
5480            match self.peek().kind {
5481                TokenKind::Comma => {
5482                    if rest {
5483                        let comma = self.advance();
5484                        self.error_at(comma.span, "rest parameter must be the last parameter");
5485                        return None;
5486                    }
5487                    self.advance();
5488                    if matches!(self.peek().kind, TokenKind::RightParen) {
5489                        break;
5490                    }
5491                }
5492                TokenKind::RightParen => break,
5493                _ => {
5494                    self.error_at_peek("expected `,` or `)`");
5495                    return None;
5496                }
5497            }
5498        }
5499        self.check_parameter_order(params.iter().map(ParamOrder::of_param));
5500        Some(params)
5501    }
5502
5503    fn parse_binary(&mut self, min_prec: u8) -> Option<ExprId> {
5504        self.with_recursion_limit(|parser| parser.parse_binary_inner(min_prec))
5505    }
5506
5507    /// Angle tokens stay separate for nested type arguments. Only adjacent
5508    /// source tokens form a shift; whitespace and comments cannot join them.
5509    fn peek_shift(&self) -> Option<ShiftToken> {
5510        let first = self.peek();
5511        let second = self.peek_at(1);
5512        if first.span.end != second.span.start {
5513            return None;
5514        }
5515        match (&first.kind, &second.kind) {
5516            (TokenKind::LessThan, TokenKind::LessThan) => {
5517                Some(ShiftToken::new(BinOp::Shl, 2, false))
5518            }
5519            (TokenKind::LessThan, TokenKind::LessEquals) => {
5520                Some(ShiftToken::new(BinOp::Shl, 2, true))
5521            }
5522            (TokenKind::GreaterThan, TokenKind::GreaterEquals) => {
5523                Some(ShiftToken::new(BinOp::Shr, 2, true))
5524            }
5525            (TokenKind::GreaterThan, TokenKind::GreaterThan) => {
5526                let third = self.peek_at(2);
5527                if second.span.end == third.span.start {
5528                    match third.kind {
5529                        TokenKind::GreaterThan => {
5530                            return Some(ShiftToken::new(BinOp::UnsignedShr, 3, false));
5531                        }
5532                        TokenKind::GreaterEquals => {
5533                            return Some(ShiftToken::new(BinOp::UnsignedShr, 3, true));
5534                        }
5535                        _ => {}
5536                    }
5537                }
5538                Some(ShiftToken::new(BinOp::Shr, 2, false))
5539            }
5540            _ => None,
5541        }
5542    }
5543
5544    fn peek_binary(&self) -> Option<BinaryToken> {
5545        if let Some(shift) = self.peek_shift() {
5546            return (!shift.assignment).then_some(BinaryToken {
5547                op: shift.op,
5548                precedence: SHIFT_PRECEDENCE,
5549                tokens: shift.tokens,
5550            });
5551        }
5552        peek_binop(&self.peek().kind).map(|(op, precedence)| BinaryToken {
5553            op,
5554            precedence,
5555            tokens: 1,
5556        })
5557    }
5558
5559    fn parse_binary_inner(&mut self, min_prec: u8) -> Option<ExprId> {
5560        let mut lhs = self.parse_cast()?;
5561        // Track the previous op to reject `a || b ?? c` / `a ?? b || c` mixes. Both
5562        // reach this loop: `??` parses its right operand above `&&` (see
5563        // `right_operand_min_prec`), so `||` / `&&` after it is left for this check.
5564        let mut last_op: Option<BinOp> = None;
5565        let mut reported_mixing = false;
5566        while let Some(BinaryToken {
5567            op,
5568            precedence: prec,
5569            tokens: count,
5570        }) = self.peek_binary()
5571        {
5572            if prec < min_prec {
5573                break;
5574            }
5575            if !reported_mixing
5576                && let Some(last) = last_op
5577                && mixed_logical(last, op)
5578            {
5579                reported_mixing = true;
5580                let op_span = self.peek().span;
5581                // Parsing continues as if the left side were grouped, so one error
5582                // inside an `if (...)` header doesn't cascade through the statement.
5583                // Later mixes in the same chain add nothing, so only the first is reported.
5584                self.error_at_with_help(
5585                    op_span,
5586                    "mixing `??` with `||` / `&&` requires parentheses",
5587                    vec![mixed_logical_help(last, op)],
5588                );
5589            }
5590            if op == BinOp::Pow
5591                && matches!(
5592                    parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.kind,
5593                    ExprKind::Unary { .. }
5594                        | ExprKind::Typeof { .. }
5595                        | ExprKind::Delete { .. }
5596                        | ExprKind::Void { .. }
5597                )
5598            {
5599                let error_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5600                self.error_at_with_help(
5601                    error_span,
5602                    "an unparenthesized unary expression cannot be the left operand of `**`",
5603                    vec!["choose the grouping explicitly: `(-x) ** 2` or `-(x ** 2)`".into()],
5604                );
5605                return None;
5606            }
5607            let loose = matches!(self.peek().kind, TokenKind::EqEq | TokenKind::BangEq);
5608            for _ in 0..count {
5609                self.advance();
5610            }
5611            let rhs = self.parse_binary(right_operand_min_prec(op, prec))?;
5612            let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5613            let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
5614            if loose {
5615                self.reject_loose_nullish_comparison(op, lhs, rhs)?;
5616            }
5617            lhs = parse_arena_result(
5618                self.ast.try_push_expr(Expr {
5619                    kind: ExprKind::Binary { op, lhs, rhs },
5620                    span: self.span(lhs_span.start, rhs_span.end),
5621                }),
5622                &mut self.fatal,
5623            )?;
5624            last_op = Some(op);
5625        }
5626        Some(lhs)
5627    }
5628
5629    // Higher than every binary op, lower than every unary prefix. Left-associative.
5630    fn parse_cast(&mut self) -> Option<ExprId> {
5631        let mut expr = self.parse_unary()?;
5632        while matches!(self.peek().kind, TokenKind::Instanceof)
5633            || self.peek_identifier_text_is("as")
5634        {
5635            let is_instanceof = matches!(self.peek().kind, TokenKind::Instanceof);
5636            self.advance();
5637            let ty = self.parse_type_annotation()?;
5638            let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5639                .span
5640                .start;
5641            let end = ty.span.end;
5642            let kind = if is_instanceof {
5643                ExprKind::InstanceOf { value: expr, ty }
5644            } else {
5645                self.last_as_type_end = Some(end);
5646                ExprKind::As { expr, ty }
5647            };
5648            expr = parse_arena_result(
5649                self.ast.try_push_expr(Expr {
5650                    kind,
5651                    span: self.span(start, end),
5652                }),
5653                &mut self.fatal,
5654            )?;
5655        }
5656        Some(expr)
5657    }
5658
5659    fn parse_unary(&mut self) -> Option<ExprId> {
5660        self.with_recursion_limit(Self::parse_unary_inner)
5661    }
5662
5663    fn parse_unary_inner(&mut self) -> Option<ExprId> {
5664        if self.reject_await_operator() {
5665            return self.parse_unary();
5666        }
5667        // `typeof` is parsed as a unary prefix so `typeof x === "T"` works without a new layer;
5668        // the typechecker rejects `Typeof` outside the recognized equality fold position.
5669        if matches!(self.peek().kind, TokenKind::Typeof) {
5670            return self.parse_prefix_expr(|operand| ExprKind::Typeof { operand });
5671        }
5672        if matches!(self.peek().kind, TokenKind::Void) {
5673            return self.parse_prefix_expr(|operand| ExprKind::Void { operand });
5674        }
5675        if self.at_delete_operator() {
5676            return self.parse_prefix_expr(|operand| ExprKind::Delete { operand });
5677        }
5678        if matches!(self.peek().kind, TokenKind::New) {
5679            let op_tok = self.advance();
5680            let mut callee = self.parse_atom()?;
5681            while matches!(self.peek().kind, TokenKind::Dot) {
5682                self.advance();
5683                if !matches!(self.peek().kind, TokenKind::Identifier) {
5684                    self.error_at_peek("expected identifier after `.` in constructor name");
5685                    return None;
5686                }
5687                let ident_tok = self.advance();
5688                let name = self.ident_from_token(&ident_tok);
5689                let receiver_span =
5690                    parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5691                let combined_span = self.span(receiver_span.start, ident_tok.span.end);
5692                callee = parse_arena_result(
5693                    self.ast.try_push_expr(Expr {
5694                        kind: ExprKind::FieldAccess {
5695                            receiver: callee,
5696                            name,
5697                        },
5698                        span: combined_span,
5699                    }),
5700                    &mut self.fatal,
5701                )?;
5702            }
5703            let type_args = if matches!(self.peek().kind, TokenKind::LessThan) {
5704                let saved_pos = self.pos;
5705                let saved_splits = self.split_type_tokens.len();
5706                let saved_diag_len = self.diagnostics.len();
5707                if let Some(args) = self.try_parse_type_args_only() {
5708                    Some(args)
5709                } else {
5710                    self.restore_type_tokens(saved_splits);
5711                    self.pos = saved_pos;
5712                    self.diagnostics.truncate(saved_diag_len);
5713                    None
5714                }
5715            } else {
5716                None
5717            };
5718            if !matches!(self.peek().kind, TokenKind::LeftParen) {
5719                self.error_at_peek("expected `(` after constructor name in `new` expression");
5720                return None;
5721            }
5722            self.advance();
5723            let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5724                Vec::new()
5725            } else {
5726                self.parse_call_args()?
5727            };
5728            if !matches!(self.peek().kind, TokenKind::RightParen) {
5729                self.error_at_peek("expected `)`");
5730                return None;
5731            }
5732            let close = self.advance();
5733            let new_expr = parse_arena_result(
5734                self.ast.try_push_expr(Expr {
5735                    kind: ExprKind::New {
5736                        callee,
5737                        type_args,
5738                        args,
5739                    },
5740                    span: self.span(op_tok.span.start, close.span.end),
5741                }),
5742                &mut self.fatal,
5743            )?;
5744            // `new Foo()` is a postfix primary: `new Map().set(...)`, `new Map().size`,
5745            // `new Foo()[i]` all continue the chain off the constructor result.
5746            return self.parse_postfix_from(new_expr);
5747        }
5748        let op = match self.peek().kind {
5749            TokenKind::Bang => UnOp::Not,
5750            TokenKind::Tilde => UnOp::BitNot,
5751            TokenKind::Minus => UnOp::Neg,
5752            TokenKind::Plus => UnOp::Pos,
5753            _ => return self.parse_postfix(),
5754        };
5755        self.parse_prefix_expr(|operand| ExprKind::Unary { op, operand })
5756    }
5757
5758    /// Consume a prefix operator and its operand, spanning both.
5759    fn parse_prefix_expr(&mut self, kind: impl FnOnce(ExprId) -> ExprKind) -> Option<ExprId> {
5760        let op_tok = self.advance();
5761        // ASI cannot distinguish the `void` type from the expression operator.
5762        // In expression position a newline after the keyword still awaits an operand.
5763        if matches!(op_tok.kind, TokenKind::Void) && self.at_inserted_semicolon() {
5764            self.advance();
5765        }
5766        let operand = self.parse_unary()?;
5767        let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
5768        parse_arena_result(
5769            self.ast.try_push_expr(Expr {
5770                kind: kind(operand),
5771                span: self.span(op_tok.span.start, operand_span.end),
5772            }),
5773            &mut self.fatal,
5774        )
5775    }
5776
5777    fn parse_postfix(&mut self) -> Option<ExprId> {
5778        let expr = self.parse_atom()?;
5779        self.parse_postfix_from(expr)
5780    }
5781
5782    fn parse_postfix_from(&mut self, mut expr: ExprId) -> Option<ExprId> {
5783        // OptionalChain is emitted once at end-of-loop so a speculative-`<` rewind
5784        // never strands a half-built chain. Everything before the first `?.` accumulates
5785        // in `expr` normally; from the first `?.` onward ops go into `parts`.
5786        let mut chain_parts: Option<(ExprId, Vec<crate::ChainPart>)> = None;
5787        loop {
5788            match self.peek().kind {
5789                TokenKind::LeftParen => {
5790                    if let Some((_, parts)) = &mut chain_parts {
5791                        let part = self.parse_chain_call_tail(/*optional=*/ false)?;
5792                        parts.push(part);
5793                    } else {
5794                        expr = self.parse_call_tail(expr, None)?;
5795                    }
5796                }
5797                TokenKind::Dot => {
5798                    if let Some((_, parts)) = &mut chain_parts {
5799                        let part = self.parse_chain_field_tail(/*optional=*/ false)?;
5800                        parts.push(part);
5801                    } else {
5802                        expr = self.parse_field_access_tail(expr)?;
5803                    }
5804                }
5805                TokenKind::LeftBracket => {
5806                    if let Some((_, parts)) = &mut chain_parts {
5807                        let part = self.parse_chain_index_tail(/*optional=*/ false)?;
5808                        parts.push(part);
5809                    } else {
5810                        expr = self.parse_index_access_tail(expr)?;
5811                    }
5812                }
5813                TokenKind::QuestionDot => {
5814                    if chain_parts.is_none() {
5815                        chain_parts = Some((expr, Vec::new()));
5816                    }
5817                    self.advance();
5818                    let part = match self.peek().kind {
5819                        TokenKind::LeftParen => self.parse_chain_call_tail(true)?,
5820                        TokenKind::LeftBracket => self.parse_chain_index_tail(true)?,
5821                        _ if is_property_name(&self.peek().kind) => {
5822                            self.parse_chain_field_tail(true)?
5823                        }
5824                        _ => {
5825                            self.error_at_peek(
5826                                "expected field name, `(` for call, or `[` for index after `?.`",
5827                            );
5828                            return None;
5829                        }
5830                    };
5831                    if let Some((_, parts)) = &mut chain_parts {
5832                        parts.push(part);
5833                    }
5834                }
5835                // `<` after a callable expression might be a generic call `f<T>(args)`.
5836                // Speculatively parse; on failure restore state and let `parse_binary`
5837                // treat `<` as a comparison.
5838                TokenKind::LessThan => {
5839                    if chain_parts.is_some() {
5840                        // `a?.<T>()` not yet supported — terminate the chain.
5841                        break;
5842                    }
5843                    let saved_pos = self.pos;
5844                    let saved_splits = self.split_type_tokens.len();
5845                    let saved_diag_len = self.diagnostics.len();
5846                    if let Some(call_id) = self.try_parse_generic_call_tail(expr) {
5847                        expr = call_id;
5848                    } else {
5849                        self.restore_type_tokens(saved_splits);
5850                        self.pos = saved_pos;
5851                        self.diagnostics.truncate(saved_diag_len);
5852                        break;
5853                    }
5854                }
5855                TokenKind::Bang => {
5856                    if let Some((_, parts)) = &mut chain_parts {
5857                        let tok = self.advance();
5858                        parts.push(crate::ChainPart::NonNull { span: tok.span });
5859                        continue;
5860                    }
5861                    let tok = self.advance();
5862                    let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5863                        .span
5864                        .start;
5865                    expr = parse_arena_result(
5866                        self.ast.try_push_expr(Expr {
5867                            kind: ExprKind::PostfixUnary {
5868                                op: PostfixOp::NonNullAssert,
5869                                operand: expr,
5870                            },
5871                            span: self.span(start, tok.span.end),
5872                        }),
5873                        &mut self.fatal,
5874                    )?;
5875                }
5876                // Postfix `++`/`--` terminates the chain; `a?.b++` is rejected.
5877                TokenKind::PlusPlus | TokenKind::MinusMinus => {
5878                    if chain_parts.is_some() {
5879                        break;
5880                    }
5881                    let tok = self.advance();
5882                    let op = if matches!(tok.kind, TokenKind::PlusPlus) {
5883                        PostfixOp::Inc
5884                    } else {
5885                        PostfixOp::Dec
5886                    };
5887                    let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5888                        .span
5889                        .start;
5890                    expr = parse_arena_result(
5891                        self.ast.try_push_expr(Expr {
5892                            kind: ExprKind::PostfixUnary { op, operand: expr },
5893                            span: self.span(start, tok.span.end),
5894                        }),
5895                        &mut self.fatal,
5896                    )?;
5897                    break;
5898                }
5899                _ => break,
5900            }
5901        }
5902        if let Some((base, parts)) = chain_parts {
5903            let base_span = parse_arena_result(self.ast.try_expr(base), &mut self.fatal)?.span;
5904            let end = parts.last().map_or(base_span.end, |p| p.span().end);
5905            Some(parse_arena_result(
5906                self.ast.try_push_expr(Expr {
5907                    kind: ExprKind::OptionalChain { base, parts },
5908                    span: self.span(base_span.start, end),
5909                }),
5910                &mut self.fatal,
5911            )?)
5912        } else {
5913            Some(expr)
5914        }
5915    }
5916
5917    // `.` is consumed here for the non-optional case; `?.` was consumed by the caller.
5918    fn parse_chain_field_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5919        let start_pos = self.peek().span.start;
5920        if !optional {
5921            self.advance();
5922        }
5923        let name_tok = if is_property_name(&self.peek().kind) {
5924            self.advance()
5925        } else {
5926            let what = if optional { "?." } else { "." };
5927            self.error_at_peek(format!("expected field name after `{what}`"));
5928            return None;
5929        };
5930        let name = self.ident_from_token(&name_tok);
5931        let end = name.span.end;
5932        Some(crate::ChainPart::Field {
5933            name,
5934            optional,
5935            span: self.span(start_pos, end),
5936        })
5937    }
5938
5939    fn parse_chain_index_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5940        let open = self.advance();
5941        let idx = self.parse_expression()?;
5942        if !matches!(self.peek().kind, TokenKind::RightBracket) {
5943            self.error_at_peek("expected `]`");
5944            return None;
5945        }
5946        let close = self.advance();
5947        Some(crate::ChainPart::Index {
5948            idx,
5949            optional,
5950            span: self.span(open.span.start, close.span.end),
5951        })
5952    }
5953
5954    fn parse_chain_call_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5955        let open = self.advance();
5956        let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5957            Vec::new()
5958        } else {
5959            self.parse_call_args()?
5960        };
5961        if !matches!(self.peek().kind, TokenKind::RightParen) {
5962            self.error_at_peek("expected `)`");
5963            return None;
5964        }
5965        let close = self.advance();
5966        Some(crate::ChainPart::Call {
5967            args,
5968            type_args: None,
5969            optional,
5970            span: self.span(open.span.start, close.span.end),
5971        })
5972    }
5973
5974    fn parse_field_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5975        self.advance();
5976        // Keywords are valid field names — `Foo.new(args)` is the canonical constructor call.
5977        let name_tok = if is_property_name(&self.peek().kind) {
5978            self.advance()
5979        } else {
5980            self.error_at_peek("expected field name after `.`");
5981            return None;
5982        };
5983        let name = self.ident_from_token(&name_tok);
5984        let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5985        parse_arena_result(
5986            self.ast.try_push_expr(Expr {
5987                kind: ExprKind::FieldAccess {
5988                    receiver,
5989                    name: name.clone(),
5990                },
5991                span: self.span(receiver_span.start, name.span.end),
5992            }),
5993            &mut self.fatal,
5994        )
5995    }
5996
5997    fn parse_index_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5998        self.advance();
5999        let index = self.parse_expression()?;
6000        if !matches!(self.peek().kind, TokenKind::RightBracket) {
6001            self.error_at_peek("expected `]`");
6002            return None;
6003        }
6004        let close = self.advance();
6005        let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
6006        parse_arena_result(
6007            self.ast.try_push_expr(Expr {
6008                kind: ExprKind::IndexAccess { receiver, index },
6009                span: self.span(receiver_span.start, close.span.end),
6010            }),
6011            &mut self.fatal,
6012        )
6013    }
6014
6015    fn parse_call_tail(
6016        &mut self,
6017        callee: ExprId,
6018        type_args: Option<Vec<TypeAnnotation>>,
6019    ) -> Option<ExprId> {
6020        self.advance();
6021        let args = if matches!(self.peek().kind, TokenKind::RightParen) {
6022            Vec::new()
6023        } else {
6024            self.parse_call_args()?
6025        };
6026        if !matches!(self.peek().kind, TokenKind::RightParen) {
6027            self.error_at_peek("expected `)`");
6028            return None;
6029        }
6030        let close = self.advance();
6031
6032        let callee_span = parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
6033        parse_arena_result(
6034            self.ast.try_push_expr(Expr {
6035                kind: ExprKind::Call {
6036                    callee,
6037                    type_args,
6038                    args,
6039                },
6040                span: self.span(callee_span.start, close.span.end),
6041            }),
6042            &mut self.fatal,
6043        )
6044    }
6045
6046    // Caller restores position + diagnostics on `None` so `<` is re-interpreted as comparison.
6047    fn try_parse_generic_call_tail(&mut self, callee: ExprId) -> Option<ExprId> {
6048        let type_args = self.try_parse_type_args_only()?;
6049        if !matches!(self.peek().kind, TokenKind::LeftParen) {
6050            return None;
6051        }
6052        self.parse_call_tail(callee, Some(type_args))
6053    }
6054
6055    fn try_parse_type_args_only(&mut self) -> Option<Vec<TypeAnnotation>> {
6056        if !matches!(self.peek().kind, TokenKind::LessThan) {
6057            return self.invariant_failure("type argument parser expected `<`");
6058        }
6059        self.advance();
6060
6061        if matches!(
6062            self.peek().kind,
6063            TokenKind::GreaterThan | TokenKind::GreaterEquals
6064        ) {
6065            return None;
6066        }
6067
6068        let mut type_args = Vec::new();
6069        loop {
6070            let ta = self.parse_type_annotation()?;
6071            type_args.push(ta);
6072            match self.peek().kind {
6073                TokenKind::Comma => {
6074                    self.advance();
6075                    if matches!(
6076                        self.peek().kind,
6077                        TokenKind::GreaterThan | TokenKind::GreaterEquals
6078                    ) {
6079                        self.consume_type_close()?;
6080                        break;
6081                    }
6082                }
6083                TokenKind::GreaterThan | TokenKind::GreaterEquals => {
6084                    self.consume_type_close()?;
6085                    break;
6086                }
6087                _ => return None,
6088            }
6089        }
6090        Some(type_args)
6091    }
6092
6093    fn parse_call_args(&mut self) -> Option<Vec<ExprId>> {
6094        let mut args = Vec::new();
6095        loop {
6096            let spread = matches!(self.peek().kind, TokenKind::DotDotDot);
6097            if spread {
6098                self.advance();
6099            }
6100            let arg = self.parse_expression()?;
6101            if spread {
6102                self.ast.call_spread_args.insert(arg);
6103            }
6104            args.push(arg);
6105            match self.peek().kind {
6106                TokenKind::Comma => {
6107                    self.advance();
6108                    if matches!(self.peek().kind, TokenKind::RightParen) {
6109                        return Some(args);
6110                    }
6111                }
6112                TokenKind::RightParen => return Some(args),
6113                _ => {
6114                    self.error_at_peek("expected `,` or `)`");
6115                    return None;
6116                }
6117            }
6118        }
6119    }
6120
6121    /// `<T>expr`, TypeScript's original cast syntax, lowered to the same
6122    /// [`ExprKind::As`] node `expr as T` produces — so it is checked and emitted
6123    /// identically, including the runtime `ref.test` our `as` performs.
6124    ///
6125    /// The operand is a unary expression, which is what binds `<T>-1` as a cast of
6126    /// `-1` rather than a cast of `1` that is then negated.
6127    fn parse_angle_cast(&mut self) -> Option<ExprId> {
6128        let open = self.advance();
6129        let ty = self.parse_type_annotation()?;
6130        if !matches!(self.peek().kind, TokenKind::GreaterThan) {
6131            self.error_at_peek("expected `>` to close a type assertion");
6132            return None;
6133        }
6134        self.advance();
6135        let expr = self.parse_unary()?;
6136        let end = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
6137            .span
6138            .end;
6139        parse_arena_result(
6140            self.ast.try_push_expr(Expr {
6141                kind: ExprKind::As { expr, ty },
6142                span: self.span(open.span.start, end),
6143            }),
6144            &mut self.fatal,
6145        )
6146    }
6147
6148    fn parse_atom(&mut self) -> Option<ExprId> {
6149        match self.peek().kind {
6150            // `<T>expr` — the older spelling of `expr as T`, lowered to the same node.
6151            // Unambiguous only because this is an atom's first token: a `<` that means
6152            // comparison always has a left operand, and one that opens a generic
6153            // argument list always follows a callee, so neither reaches here.
6154            TokenKind::LessThan => return self.parse_angle_cast(),
6155            TokenKind::LeftParen => return self.parse_paren(),
6156            // `{` here is an object literal; statement-start `{` is dispatched to
6157            // `parse_block` before `parse_atom` runs, so the two stay disjoint.
6158            TokenKind::LeftBrace => return self.parse_object_literal(),
6159            TokenKind::LeftBracket => return self.parse_array_literal(),
6160            TokenKind::TemplateHead(_) => return self.parse_template_literal(),
6161            TokenKind::This => return self.parse_this_or_super(true),
6162            TokenKind::Super => return self.parse_this_or_super(false),
6163            TokenKind::Function => return self.parse_function_expression(),
6164            _ => {}
6165        }
6166        if !matches!(
6167            self.peek().kind,
6168            TokenKind::NumberLiteral(_)
6169                | TokenKind::BigIntLiteral(_)
6170                | TokenKind::StringLiteral(_)
6171                | TokenKind::TemplateNoSubstitution(_)
6172                | TokenKind::BooleanLiteral(_)
6173                | TokenKind::NullLiteral
6174                | TokenKind::Identifier
6175                | TokenKind::RegexLiteral { .. }
6176        ) {
6177            self.error_at_peek("expected expression");
6178            return None;
6179        }
6180        let tok = self.advance();
6181        let span = tok.span;
6182        let kind = match tok.kind {
6183            TokenKind::NumberLiteral(v) => ExprKind::Number(v),
6184            TokenKind::BigIntLiteral(s) => ExprKind::BigInt(s),
6185            TokenKind::StringLiteral(s) => ExprKind::String(s),
6186            TokenKind::TemplateNoSubstitution(s) => ExprKind::String(s),
6187            TokenKind::BooleanLiteral(b) => ExprKind::Boolean(b),
6188            TokenKind::NullLiteral => ExprKind::Null,
6189            TokenKind::Identifier => ExprKind::Identifier(Ident {
6190                name: self.source[span.start as usize..span.end as usize].to_string(),
6191                span,
6192            }),
6193            TokenKind::RegexLiteral { source, flags } => ExprKind::Regex { source, flags },
6194            // No cursor movement occurs between the accepted-kind check and advance.
6195            _ => unreachable!("advance preserves the accepted expression token"),
6196        };
6197        parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
6198    }
6199
6200    /// Ordinary functions establish a `this` boundary and exclude an enclosing
6201    /// class's `super`; arrows inherit the current parsing context.
6202    fn parse_this_or_super(&mut self, is_this: bool) -> Option<ExprId> {
6203        let tok = self.advance();
6204        let span = tok.span;
6205        if !is_this && self.class_member_body_depth == 0 {
6206            self.error_at_with_help(
6207                span,
6208                "`super` is only valid inside a class method or constructor body",
6209                vec![
6210                    "call `super(...)` in a subclass constructor or `super.method(...)` \
6211                     in a subclass method"
6212                        .to_string(),
6213                ],
6214            );
6215        }
6216        let kind = if is_this
6217            && self.class_member_body_depth == 0
6218            && self.function_expression_body_depth == 0
6219        {
6220            ExprKind::ThisOutsideReceiver
6221        } else if is_this {
6222            ExprKind::This
6223        } else {
6224            ExprKind::Super
6225        };
6226        parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
6227    }
6228
6229    // parse_atom dispatches immediately after matching TemplateHead.
6230    // The remaining template tokens are still parsed and diagnosed normally.
6231    fn parse_template_literal(&mut self) -> Option<ExprId> {
6232        let head_tok = self.advance();
6233        let TokenKind::TemplateHead(head) = head_tok.kind else {
6234            unreachable!("template dispatch requires TemplateHead");
6235        };
6236        let start = head_tok.span.start;
6237        let mut parts: Vec<String> = vec![head];
6238        let mut exprs: Vec<ExprId> = Vec::new();
6239        let mut substitution_spans: Vec<Span> = Vec::new();
6240        // The token before a substitution ends just past its `${`; the one after
6241        // starts at its `}`.
6242        let mut substitution_start = head_tok.span.end.saturating_sub(2);
6243        loop {
6244            let expr = self.parse_expression()?;
6245            exprs.push(expr);
6246            let closing = self.peek().span.start;
6247            let substitution_span = self.span(substitution_start, closing.saturating_add(1));
6248            match self.peek().kind.clone() {
6249                TokenKind::TemplateMiddle(s) => {
6250                    let tok = self.advance();
6251                    substitution_spans.push(substitution_span);
6252                    substitution_start = tok.span.end.saturating_sub(2);
6253                    parts.push(s);
6254                }
6255                TokenKind::TemplateTail(s) => {
6256                    let tok = self.advance();
6257                    substitution_spans.push(substitution_span);
6258                    parts.push(s);
6259                    return parse_arena_result(
6260                        self.ast.try_push_expr(Expr {
6261                            kind: ExprKind::TemplateLiteral {
6262                                parts,
6263                                exprs,
6264                                substitution_spans,
6265                            },
6266                            span: self.span(start, tok.span.end),
6267                        }),
6268                        &mut self.fatal,
6269                    );
6270                }
6271                _ => {
6272                    self.error_at_peek("expected `}` to close template interpolation");
6273                    return None;
6274                }
6275            }
6276        }
6277    }
6278
6279    fn parse_object_literal(&mut self) -> Option<ExprId> {
6280        let open = self.advance();
6281
6282        let mut members: Vec<ObjectLiteralMember> = Vec::new();
6283        if !matches!(self.peek().kind, TokenKind::RightBrace) {
6284            loop {
6285                if matches!(self.peek().kind, TokenKind::DotDotDot) {
6286                    let dots = self.advance();
6287                    let value = self.parse_expression()?;
6288                    let value_span =
6289                        parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
6290                    members.push(ObjectLiteralMember::Spread {
6291                        value,
6292                        span: self.span(dots.span.start, value_span.end),
6293                    });
6294                } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
6295                    self.advance();
6296                    let key = self.parse_expression()?;
6297                    if !matches!(self.peek().kind, TokenKind::RightBracket) {
6298                        self.error_at_peek("expected `]` after computed property key");
6299                        return None;
6300                    }
6301                    self.advance();
6302                    if !matches!(self.peek().kind, TokenKind::Colon) {
6303                        self.error_at_peek("expected `:` after computed property key; computed methods are not supported");
6304                        return None;
6305                    }
6306                    self.advance();
6307                    let value = self.parse_expression()?;
6308                    members.push(ObjectLiteralMember::Computed { key, value });
6309                } else {
6310                    let field = self.parse_object_literal_field()?;
6311                    // Spread members are dynamic — only literal fields participate in
6312                    // the duplicate-key check.
6313                    if let Some(existing) = members.iter().find_map(|m| match m {
6314                        ObjectLiteralMember::Field(f) if f.name.name == field.name.name => Some(f),
6315                        _ => None,
6316                    }) {
6317                        self.diagnostics.push(Diagnostic {
6318                            severity: Severity::Error,
6319                            span: field.name.span,
6320                            message: format!(
6321                                "duplicate field `{}` in object literal",
6322                                field.name.name
6323                            ),
6324                            help: vec![],
6325                            notes: vec![(existing.name.span, "first defined here".into())],
6326                        });
6327                        // Keep parsing so the user sees all duplicates at once.
6328                    }
6329                    members.push(ObjectLiteralMember::Field(field));
6330                }
6331                if self.at_inserted_semicolon()
6332                    && matches!(self.peek_at(1).kind, TokenKind::RightBrace)
6333                {
6334                    self.advance();
6335                }
6336                match self.peek().kind {
6337                    TokenKind::Comma => {
6338                        self.advance();
6339                        if matches!(self.peek().kind, TokenKind::RightBrace) {
6340                            break;
6341                        }
6342                    }
6343                    TokenKind::RightBrace => break,
6344                    _ => {
6345                        self.error_at_peek("expected `,` or `}`");
6346                        return None;
6347                    }
6348                }
6349            }
6350        }
6351
6352        if !matches!(self.peek().kind, TokenKind::RightBrace) {
6353            self.error_at_peek("expected `}`");
6354            return None;
6355        }
6356        let close = self.advance();
6357
6358        parse_arena_result(
6359            self.ast.try_push_expr(Expr {
6360                kind: ExprKind::ObjectLiteral { members },
6361                span: self.span(open.span.start, close.span.end),
6362            }),
6363            &mut self.fatal,
6364        )
6365    }
6366
6367    fn parse_object_literal_field(&mut self) -> Option<ObjectLiteralField> {
6368        self.reject_async_member_modifier();
6369        let (name, shorthandable) = match self.peek().kind.clone() {
6370            TokenKind::Identifier => {
6371                let tok = self.advance();
6372                (self.ident_from_token(&tok), true)
6373            }
6374            kind if is_property_name(&kind) => {
6375                let tok = self.advance();
6376                (self.ident_from_token(&tok), false)
6377            }
6378            TokenKind::StringLiteral(_) | TokenKind::NumberLiteral(_) => {
6379                let tok = self.advance();
6380                (self.property_ident_from_token(&tok), false)
6381            }
6382            _ => {
6383                self.error_at_peek("expected field name");
6384                return None;
6385            }
6386        };
6387
6388        // `{ m(x: T): R { … } }` is shorthand for `{ m: (x: T): R => { … } }`. Method
6389        // shorthand carries no `this` of its own here, for the same reason a function
6390        // expression does not, so the arrow is an exact lowering. Checked before the
6391        // `:` branch and independently of `shorthandable`, because a keyword or string
6392        // key can name a method too.
6393        if matches!(self.peek().kind, TokenKind::LeftParen) {
6394            let value = self.parse_method_shorthand_body(name.span)?;
6395            return Some(ObjectLiteralField { name, value });
6396        }
6397
6398        if !matches!(self.peek().kind, TokenKind::Colon) {
6399            // `{ x }` is shorthand for `{ x: x }`. Only identifier keys can use
6400            // it — a string key like `{ "x" }` has no binding to reference.
6401            if shorthandable {
6402                let value = parse_arena_result(
6403                    self.ast.try_push_expr(Expr {
6404                        kind: ExprKind::Identifier(name.clone()),
6405                        span: name.span,
6406                    }),
6407                    &mut self.fatal,
6408                )?;
6409                return Some(ObjectLiteralField { name, value });
6410            }
6411            self.error_at_peek("expected `:` after field name");
6412            return None;
6413        }
6414        self.advance();
6415
6416        let value = self.parse_expression()?;
6417        Some(ObjectLiteralField { name, value })
6418    }
6419
6420    /// The `(params): R { … }` tail of an object-literal method, lowered to an arrow.
6421    /// `name_span` is the key's span, so the arrow spans the whole member.
6422    fn parse_method_shorthand_body(&mut self, name_span: Span) -> Option<ExprId> {
6423        self.advance();
6424
6425        let params = if matches!(self.peek().kind, TokenKind::RightParen) {
6426            Vec::new()
6427        } else {
6428            self.parse_arrow_param_list()?
6429        };
6430        if !matches!(self.peek().kind, TokenKind::RightParen) {
6431            self.error_at_peek("expected `)`");
6432            return None;
6433        }
6434        self.advance();
6435
6436        let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
6437            self.advance();
6438            self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
6439        } else {
6440            (None, None)
6441        };
6442
6443        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
6444            self.error_at_peek("expected `{` to open the method body");
6445            return None;
6446        }
6447        let block = self.parse_outer_this_boundary(Self::parse_block)?;
6448        let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
6449            .span
6450            .end;
6451
6452        parse_arena_result(
6453            self.ast.try_push_expr(Expr {
6454                kind: ExprKind::Arrow {
6455                    params,
6456                    return_type,
6457                    type_predicate,
6458                    body: ArrowBody::Block(block),
6459                },
6460                span: self.span(name_span.start, end),
6461            }),
6462            &mut self.fatal,
6463        )
6464    }
6465
6466    fn parse_array_literal(&mut self) -> Option<ExprId> {
6467        let open = self.advance();
6468
6469        let mut elements: Vec<ArrayLiteralElement> = Vec::new();
6470        if !matches!(self.peek().kind, TokenKind::RightBracket) {
6471            loop {
6472                let element = if matches!(self.peek().kind, TokenKind::DotDotDot) {
6473                    let dots = self.advance();
6474                    let value = self.parse_expression()?;
6475                    let value_span =
6476                        parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
6477                    ArrayLiteralElement::Spread {
6478                        value,
6479                        span: self.span(dots.span.start, value_span.end),
6480                    }
6481                } else {
6482                    ArrayLiteralElement::Value(self.parse_expression()?)
6483                };
6484                elements.push(element);
6485                match self.peek().kind {
6486                    TokenKind::Comma => {
6487                        self.advance();
6488                        if matches!(self.peek().kind, TokenKind::RightBracket) {
6489                            break;
6490                        }
6491                    }
6492                    TokenKind::RightBracket => break,
6493                    _ => {
6494                        self.error_at_peek("expected `,` or `]`");
6495                        return None;
6496                    }
6497                }
6498            }
6499        }
6500
6501        if !matches!(self.peek().kind, TokenKind::RightBracket) {
6502            self.error_at_peek("expected `]`");
6503            return None;
6504        }
6505        let close = self.advance();
6506
6507        parse_arena_result(
6508            self.ast.try_push_expr(Expr {
6509                kind: ExprKind::ArrayLiteral { elements },
6510                span: self.span(open.span.start, close.span.end),
6511            }),
6512            &mut self.fatal,
6513        )
6514    }
6515
6516    fn parse_paren(&mut self) -> Option<ExprId> {
6517        let open = self.advance();
6518        let inner = self.parse_expression()?;
6519        if !matches!(self.peek().kind, TokenKind::RightParen) {
6520            self.error_at_peek("expected `)`");
6521            return None;
6522        }
6523        let close = self.advance();
6524        let span = self.span(open.span.start, close.span.end);
6525        parse_arena_result(
6526            self.ast.try_push_expr(Expr {
6527                kind: ExprKind::Paren(inner),
6528                span,
6529            }),
6530            &mut self.fatal,
6531        )
6532    }
6533
6534    fn with_recursion_limit<T>(&mut self, parse: impl FnOnce(&mut Self) -> Option<T>) -> Option<T> {
6535        if self.fatal.is_some() {
6536            return None;
6537        }
6538        if self.recursion_limit_span.is_some() || self.recursion_depth >= MAX_PARSE_DEPTH {
6539            self.stop_at_recursion_limit();
6540            return None;
6541        }
6542        self.recursion_depth += 1;
6543        let result = parse(self);
6544        self.recursion_depth -= 1;
6545        result
6546    }
6547
6548    fn stop_at_recursion_limit(&mut self) {
6549        self.recursion_limit_span.get_or_insert(self.peek().span);
6550        // Stop recovery and enclosing block loops as well as recursive calls.
6551        self.pos = self.tokens.len().saturating_sub(1);
6552    }
6553
6554    fn recover(&mut self) {
6555        while !self.is_at_eof() {
6556            match self.peek().kind {
6557                TokenKind::Semicolon => {
6558                    self.advance();
6559                    return;
6560                }
6561                TokenKind::RightBrace
6562                | TokenKind::Let
6563                | TokenKind::Const
6564                | TokenKind::Function
6565                | TokenKind::Export
6566                | TokenKind::If
6567                | TokenKind::While
6568                | TokenKind::Return
6569                | TokenKind::Try
6570                | TokenKind::Throw => return,
6571                _ => {
6572                    self.advance();
6573                }
6574            }
6575        }
6576    }
6577
6578    fn peek(&self) -> &Token {
6579        self.tokens.get(self.pos).unwrap_or(&self.eof)
6580    }
6581
6582    fn peek_at(&self, offset: usize) -> &Token {
6583        self.pos
6584            .checked_add(offset)
6585            .and_then(|index| self.tokens.get(index))
6586            .unwrap_or(&self.eof)
6587    }
6588
6589    /// ASI inserts its `;` as a zero-width token; a written `;` always has width.
6590    fn at_inserted_semicolon(&self) -> bool {
6591        let token = self.peek();
6592        matches!(token.kind, TokenKind::Semicolon) && token.span.start == token.span.end
6593    }
6594
6595    fn advance(&mut self) -> Token {
6596        let tok = self.peek().clone();
6597        if self.pos < self.tokens.len().saturating_sub(1) {
6598            self.pos += 1;
6599        }
6600        tok
6601    }
6602
6603    fn is_at_eof(&self) -> bool {
6604        self.fatal.is_some() || matches!(self.peek().kind, TokenKind::Eof)
6605    }
6606
6607    /// Whether a line break separates the next token from the one before it.
6608    fn line_break_before_peek(&self) -> bool {
6609        self.source_has_line_break(self.prev_token_end(), self.peek().span.start)
6610    }
6611
6612    /// Whether a line break separates the next token from the one after it.
6613    fn line_break_after_peek(&self) -> bool {
6614        self.source_has_line_break(self.peek().span.end, self.peek_at(1).span.start)
6615    }
6616
6617    /// The ASI pass drops newline tokens, so line breaks between tokens are read
6618    /// from the source.
6619    fn source_has_line_break(&self, start: u32, end: u32) -> bool {
6620        self.source
6621            .get(start as usize..end as usize)
6622            .is_some_and(|gap| gap.contains(['\n', '\r']))
6623    }
6624
6625    /// Whether the statement being parsed ends here: at a `;`, or at a line break
6626    /// right after an `as T`. ASI keeps a `[` or `(` on the next line attached, but
6627    /// the type already stopped at the break (see `parse_type_array_inner`), and no
6628    /// expression continues past a type, so TypeScript ends the statement there.
6629    fn at_statement_end(&self) -> bool {
6630        matches!(self.peek().kind, TokenKind::Semicolon)
6631            || (self.last_as_type_end == Some(self.prev_token_end())
6632                && self.line_break_before_peek())
6633    }
6634
6635    /// Consumes the `;` that [`Parser::at_statement_end`] found, if there is one, and
6636    /// returns the statement's end offset.
6637    fn finish_statement(&mut self) -> u32 {
6638        if matches!(self.peek().kind, TokenKind::Semicolon) {
6639            return self.advance().span.end;
6640        }
6641        self.prev_token_end()
6642    }
6643
6644    /// End offset of the most recently consumed token (the body of the file's first
6645    /// token if nothing has been consumed yet).
6646    fn prev_token_end(&self) -> u32 {
6647        self.tokens
6648            .get(self.pos.saturating_sub(1))
6649            .unwrap_or(&self.eof)
6650            .span
6651            .end
6652    }
6653
6654    fn take_leading_doc(&mut self) -> Option<crate::DocComment> {
6655        let parsed = self
6656            .peek()
6657            .leading_doc
6658            .as_ref()
6659            .map(crate::parse_doc_comment)
6660            .transpose();
6661        match parsed {
6662            Ok(doc) => doc,
6663            Err(error) => {
6664                self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6665                None
6666            }
6667        }
6668    }
6669
6670    fn parse_doc(&mut self, raw: &crate::RawDoc) -> Option<crate::DocComment> {
6671        match crate::parse_doc_comment(raw) {
6672            Ok(doc) => Some(doc),
6673            Err(error) => {
6674                self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6675                None
6676            }
6677        }
6678    }
6679
6680    /// JavaScript forbids a `"use strict"` directive in a function whose parameters
6681    /// aren't all plain names: one with a default, a pattern, or `...`. As TypeScript
6682    /// does, each such parameter and the directive are reported.
6683    fn reject_use_strict_with_non_simple_params(&mut self) {
6684        let mut functions: Vec<(Vec<Span>, StmtId)> = Vec::new();
6685        let non_simple = |param: &ParamDecl| {
6686            (param.default.is_some() || param.pattern.is_some() || param.rest).then(|| {
6687                param
6688                    .pattern
6689                    .as_ref()
6690                    .map_or(param.name.span, Binding::span)
6691            })
6692        };
6693        for expr in self.ast.source_expressions() {
6694            if let ExprKind::Arrow {
6695                params,
6696                body: ArrowBody::Block(body),
6697                ..
6698            } = &expr.kind
6699            {
6700                functions.push((params.iter().filter_map(non_simple).collect(), *body));
6701            }
6702        }
6703        for stmt in self.ast.source_statements() {
6704            match &stmt.kind {
6705                StmtKind::Function { params, body, .. } => {
6706                    functions.push((params.iter().filter_map(non_simple).collect(), *body));
6707                }
6708                StmtKind::ClassDecl { members, .. } => {
6709                    for member in members {
6710                        match member {
6711                            ClassMember::Method { params, body, .. }
6712                            | ClassMember::Constructor { params, body, .. } => {
6713                                functions
6714                                    .push((params.iter().filter_map(non_simple).collect(), *body));
6715                            }
6716                            ClassMember::Accessor { param, body, .. } => {
6717                                let params = param.as_deref().and_then(non_simple);
6718                                functions.push((params.into_iter().collect(), *body));
6719                            }
6720                            _ => {}
6721                        }
6722                    }
6723                }
6724                _ => {}
6725            }
6726        }
6727        for (params, body) in functions {
6728            if params.is_empty() {
6729                continue;
6730            }
6731            let Some(directive) = self.use_strict_directive(body) else {
6732                continue;
6733            };
6734            for span in params {
6735                self.error_at(
6736                    span,
6737                    "this parameter is not allowed with a `\"use strict\"` directive",
6738                );
6739            }
6740            self.error_at_with_help(
6741                directive,
6742                "a `\"use strict\"` directive can't be used with a parameter list that has defaults, patterns, or a rest parameter",
6743                vec!["remove the directive; Submilli code is always strict".into()],
6744            );
6745        }
6746    }
6747
6748    /// The span of a `"use strict"` directive in the prologue of a function body:
6749    /// the string-literal statements it starts with. As in TypeScript, the string's
6750    /// value decides, so an escape spelling it counts too; a template doesn't.
6751    fn use_strict_directive(&self, body: StmtId) -> Option<Span> {
6752        let StmtKind::Block(stmts) = &self.ast.try_stmt(body).ok()?.kind else {
6753            return None;
6754        };
6755        for stmt in stmts {
6756            let stmt = self.ast.try_stmt(*stmt).ok()?;
6757            let StmtKind::Expr(expr) = stmt.kind else {
6758                return None;
6759            };
6760            let expr = self.ast.try_expr(expr).ok()?;
6761            let ExprKind::String(value) = &expr.kind else {
6762                return None;
6763            };
6764            if self.source.as_bytes().get(expr.span.start as usize) == Some(&b'`') {
6765                return None;
6766            }
6767            if value == "use strict" {
6768                return Some(stmt.span);
6769            }
6770        }
6771        None
6772    }
6773
6774    fn error_at(&mut self, span: Span, message: impl Into<String>) {
6775        self.diagnostics.push(Diagnostic {
6776            severity: Severity::Error,
6777            span,
6778            message: message.into(),
6779            help: vec![],
6780            notes: vec![],
6781        });
6782    }
6783
6784    /// `==` is `===` here, so `x == null` would silently miss `undefined` — the
6785    /// one case where TypeScript's loose equality differs in a way code relies on.
6786    fn reject_loose_nullish_comparison(
6787        &mut self,
6788        op: BinOp,
6789        lhs: ExprId,
6790        rhs: ExprId,
6791    ) -> Option<()> {
6792        let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
6793        let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
6794        let (nullish, operand, nullish_first) =
6795            match (self.nullish_operand(lhs)?, self.nullish_operand(rhs)?) {
6796                (Some(left), Some(right)) => {
6797                    self.reject_nullish_against_nullish(op, lhs_span, rhs_span, [left, right]);
6798                    return Some(());
6799                }
6800                (None, Some(nullish)) => (nullish, lhs, false),
6801                (Some(nullish), None) => (nullish, rhs, true),
6802                (None, None) => return Some(()),
6803            };
6804        let comparison = LooseComparison {
6805            nullish,
6806            nullish_first,
6807            negated: op == BinOp::NotEq,
6808        };
6809        let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
6810        let x = self.single_line_source(operand_span).unwrap_or("x");
6811        let strict = self.strict_suggestion(&comparison, x);
6812        let either = self.either_suggestion(&comparison, operand, x)?;
6813        let literal = comparison.nullish.literal.text();
6814        // Quote the comparison as written, so `null == z` stays in that order.
6815        let written = self
6816            .single_line_source(self.span(lhs_span.start, rhs_span.end))
6817            .map_or_else(
6818                || {
6819                    let written_op = if comparison.negated { "!=" } else { "==" };
6820                    comparison.ordered(x, written_op, literal)
6821                },
6822                str::to_string,
6823            );
6824        self.error_at_with_help(
6825            self.span(lhs_span.start, rhs_span.end),
6826            format!(
6827                "`{written}` does not also match `{}`: `==` compares like `===`",
6828                comparison.nullish.literal.other().text()
6829            ),
6830            vec![
6831                either,
6832                format!("to test for `{literal}` alone, write `{strict}`"),
6833            ],
6834        );
6835        Some(())
6836    }
6837
6838    /// Both sides are always nullish. TypeScript's `==` treats `null` and
6839    /// `undefined` as equal, so the comparison has one value, which the help
6840    /// names; a `void` operand with effects still runs, as a statement.
6841    fn reject_nullish_against_nullish(
6842        &mut self,
6843        op: BinOp,
6844        lhs: Span,
6845        rhs: Span,
6846        sides: [NullishOperand; 2],
6847    ) {
6848        let span = self.span(lhs.start, rhs.end);
6849        // A comparison written over several lines can't be quoted on one, so
6850        // the message names it without the source.
6851        let subject = self.single_line_source(span).map_or_else(
6852            || "this comparison".to_string(),
6853            |written| format!("`{written}`"),
6854        );
6855        let value = if op == BinOp::NotEq { "false" } else { "true" };
6856        let effects: Vec<String> = sides
6857            .iter()
6858            .filter_map(|side| side.effect)
6859            .map(|effect| {
6860                self.single_line_source(effect.operand).map_or_else(
6861                    || "the `void` operand's expression".to_string(),
6862                    |statement| format!("`{statement};`"),
6863                )
6864            })
6865            .collect();
6866        let help = if effects.is_empty() {
6867            format!("in TypeScript this is always `{value}`; write `{value}`")
6868        } else {
6869            format!(
6870                "in TypeScript this is always `{value}`; run {} {}, then use `{value}`",
6871                effects.join(" and "),
6872                if effects.len() > 1 {
6873                    "each as its own statement"
6874                } else {
6875                    "as its own statement"
6876                }
6877            )
6878        };
6879        self.error_at_with_help(
6880            span,
6881            format!("{subject} compares two nullish values: `==` compares like `===`"),
6882            vec![help],
6883        );
6884    }
6885
6886    /// The strict comparison against the written nullish value. A `void`
6887    /// operand with effects stays as written, so the operand runs once.
6888    fn strict_suggestion(&self, comparison: &LooseComparison, x: &str) -> String {
6889        let eq = comparison.strict_op();
6890        match comparison.nullish.effect {
6891            Some(effect) => {
6892                let void = self.single_line_source(effect.void).unwrap_or("void expr");
6893                comparison.ordered(x, eq, void)
6894            }
6895            None => format!("{x} {eq} {}", comparison.nullish.literal.text()),
6896        }
6897    }
6898
6899    /// "To test for either" names the operand twice, so one with effects is
6900    /// bound to a `const` first, and a `void` operand's expression runs as a
6901    /// statement of its own.
6902    fn either_suggestion(
6903        &mut self,
6904        comparison: &LooseComparison,
6905        operand: ExprId,
6906        x: &str,
6907    ) -> Option<String> {
6908        let mut steps = Vec::new();
6909        let tested = if self.repeats_without_effect(operand)? {
6910            x
6911        } else {
6912            steps.push(format!("bind `{x}` to a `const` (`const value = {x};`)"));
6913            "value"
6914        };
6915        if let Some(effect) = comparison.nullish.effect {
6916            let statement = self.single_line_source(effect.operand).unwrap_or("expr");
6917            let step = format!("run `{statement};` as its own statement");
6918            if comparison.nullish_first {
6919                steps.insert(0, step);
6920            } else {
6921                steps.push(step);
6922            }
6923        }
6924        let eq = comparison.strict_op();
6925        let joiner = if comparison.negated { "&&" } else { "||" };
6926        let both = format!("{tested} {eq} null {joiner} {tested} {eq} undefined");
6927        Some(if steps.is_empty() {
6928            format!("to test for either, write `{both}`")
6929        } else {
6930            format!(
6931                "to test for either, first {}, then write `{both}`",
6932                steps.join(", then ")
6933            )
6934        })
6935    }
6936
6937    /// Whether naming `id` twice evaluates it twice with no effect: a literal,
6938    /// or a reference path.
6939    fn repeats_without_effect(&mut self, id: ExprId) -> Option<bool> {
6940        let unwrapped = self.unparenthesized(id)?;
6941        let expr = parse_arena_result(self.ast.try_expr(unwrapped), &mut self.fatal)?;
6942        if is_literal(&expr.kind) {
6943            return Some(true);
6944        }
6945        if self
6946            .nullish_operand(unwrapped)?
6947            .is_some_and(|nullish| nullish.effect.is_none())
6948        {
6949            return Some(true);
6950        }
6951        self.is_reference_path(id)
6952    }
6953
6954    /// `null`, `undefined` or `void e`, looking through parentheses.
6955    fn nullish_operand(&mut self, id: ExprId) -> Option<Option<NullishOperand>> {
6956        let id = self.unparenthesized(id)?;
6957        let expr = parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?;
6958        let span = expr.span;
6959        Some(match &expr.kind {
6960            ExprKind::Null => Some(NullishOperand {
6961                literal: Nullish::Null,
6962                effect: None,
6963            }),
6964            ExprKind::Identifier(ident) if ident.name == "undefined" => Some(NullishOperand {
6965                literal: Nullish::Undefined,
6966                effect: None,
6967            }),
6968            ExprKind::Void { operand } => {
6969                let operand_id = self.unparenthesized(*operand)?;
6970                let operand =
6971                    parse_arena_result(self.ast.try_expr(operand_id), &mut self.fatal)?.clone();
6972                // A literal or a reference path reads with no effect to keep.
6973                let effect_free =
6974                    is_literal(&operand.kind) || self.is_reference_path(operand_id)?;
6975                let effect = (!effect_free).then_some(VoidEffect {
6976                    void: span,
6977                    operand: operand.span,
6978                });
6979                Some(NullishOperand {
6980                    literal: Nullish::Undefined,
6981                    effect,
6982                })
6983            }
6984            _ => None,
6985        })
6986    }
6987
6988    /// An identifier, `this`, or a `.field` chain on one: naming it twice evaluates
6989    /// it twice with no effect.
6990    fn is_reference_path(&mut self, id: ExprId) -> Option<bool> {
6991        let mut id = id;
6992        loop {
6993            match &parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6994                ExprKind::Identifier(_) | ExprKind::This => return Some(true),
6995                ExprKind::FieldAccess { receiver, .. } => id = *receiver,
6996                ExprKind::Paren(inner) => id = *inner,
6997                _ => return Some(false),
6998            }
6999        }
7000    }
7001
7002    fn unparenthesized(&mut self, id: ExprId) -> Option<ExprId> {
7003        let mut id = id;
7004        loop {
7005            match parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
7006                ExprKind::Paren(inner) => id = inner,
7007                _ => return Some(id),
7008            }
7009        }
7010    }
7011
7012    fn single_line_source(&self, span: Span) -> Option<&'a str> {
7013        self.source
7014            .get(span.start as usize..span.end as usize)
7015            .filter(|text| !text.contains('\n'))
7016    }
7017
7018    fn error_at_with_help(&mut self, span: Span, message: impl Into<String>, help: Vec<String>) {
7019        self.diagnostics.push(Diagnostic {
7020            severity: Severity::Error,
7021            span,
7022            message: message.into(),
7023            help,
7024            notes: vec![],
7025        });
7026    }
7027
7028    fn invariant_failure<T>(&mut self, message: &str) -> Option<T> {
7029        self.fatal.get_or_insert_with(|| CompilerFailure::Internal {
7030            stage: CompilerStage::Parse,
7031            span: None,
7032            message: message.into(),
7033        });
7034        self.pos = self.tokens.len();
7035        None
7036    }
7037
7038    fn error_at_peek(&mut self, message: impl Into<String>) {
7039        let span = self.peek().span;
7040        self.error_at(span, message);
7041    }
7042
7043    fn error_at_peek_with_help(&mut self, message: impl Into<String>, help: Vec<String>) {
7044        let span = self.peek().span;
7045        self.error_at_with_help(span, message, help);
7046    }
7047
7048    fn error_count(&self) -> usize {
7049        self.diagnostics
7050            .iter()
7051            .filter(|d| d.severity == Severity::Error)
7052            .count()
7053    }
7054}
7055
7056pub(crate) fn is_property_name(kind: &TokenKind) -> bool {
7057    matches!(
7058        kind,
7059        TokenKind::Identifier
7060            | TokenKind::BooleanLiteral(_)
7061            | TokenKind::NullLiteral
7062            | TokenKind::Let
7063            | TokenKind::Const
7064            | TokenKind::Function
7065            | TokenKind::If
7066            | TokenKind::Else
7067            | TokenKind::While
7068            | TokenKind::Do
7069            | TokenKind::For
7070            | TokenKind::Break
7071            | TokenKind::Continue
7072            | TokenKind::Return
7073            | TokenKind::Switch
7074            | TokenKind::Case
7075            | TokenKind::Default
7076            | TokenKind::Void
7077            | TokenKind::Interface
7078            | TokenKind::Enum
7079            | TokenKind::In
7080            | TokenKind::Typeof
7081            | TokenKind::Import
7082            | TokenKind::Export
7083            | TokenKind::New
7084            | TokenKind::Try
7085            | TokenKind::Catch
7086            | TokenKind::Finally
7087            | TokenKind::Throw
7088            | TokenKind::Class
7089            | TokenKind::Extends
7090            | TokenKind::Implements
7091            | TokenKind::Super
7092            | TokenKind::This
7093    )
7094}
7095
7096fn is_reserved_identifier_word(kind: &TokenKind) -> bool {
7097    matches!(
7098        kind,
7099        TokenKind::BooleanLiteral(_)
7100            | TokenKind::NullLiteral
7101            | TokenKind::Let
7102            | TokenKind::Const
7103            | TokenKind::Function
7104            | TokenKind::If
7105            | TokenKind::Else
7106            | TokenKind::While
7107            | TokenKind::Do
7108            | TokenKind::For
7109            | TokenKind::Break
7110            | TokenKind::Continue
7111            | TokenKind::Return
7112            | TokenKind::Switch
7113            | TokenKind::Case
7114            | TokenKind::Default
7115            | TokenKind::Export
7116            | TokenKind::Void
7117            | TokenKind::Interface
7118            | TokenKind::Enum
7119            | TokenKind::In
7120            | TokenKind::Typeof
7121            | TokenKind::Import
7122            | TokenKind::New
7123            | TokenKind::Try
7124            | TokenKind::Catch
7125            | TokenKind::Finally
7126            | TokenKind::Throw
7127            | TokenKind::Class
7128            | TokenKind::Extends
7129            | TokenKind::Implements
7130            | TokenKind::Super
7131            | TokenKind::This
7132    )
7133}
7134
7135/// Which statement a `{ … }` specifier list belongs to. An import binds local
7136/// names, which strict mode restricts; an export's are module export names,
7137/// which it doesn't.
7138#[derive(Clone, Copy, PartialEq, Eq)]
7139enum SpecifierList {
7140    Import,
7141    Export,
7142}
7143
7144impl SpecifierList {
7145    fn noun(self) -> &'static str {
7146        match self {
7147            SpecifierList::Import => "import",
7148            SpecifierList::Export => "export",
7149        }
7150    }
7151}
7152
7153/// Words strict mode reserves that Submilli otherwise lexes as identifiers: they
7154/// can't name a binding or a type, but stay valid as property names and modifiers.
7155/// (`implements`, `interface` and `let` are keywords already.)
7156const STRICT_MODE_RESERVED_WORDS: [&str; 6] = [
7157    "package",
7158    "private",
7159    "protected",
7160    "public",
7161    "static",
7162    "yield",
7163];
7164
7165fn reserved_keyword_rename_example(keyword: &str) -> &'static str {
7166    match keyword {
7167        "default" => "defaultValue",
7168        _ => "valueName",
7169    }
7170}
7171
7172fn mixed_logical(prev: BinOp, next: BinOp) -> bool {
7173    let prev_logical = matches!(prev, BinOp::Or | BinOp::And);
7174    let next_logical = matches!(next, BinOp::Or | BinOp::And);
7175    let prev_nullish = matches!(prev, BinOp::NullishCoalesce);
7176    let next_nullish = matches!(next, BinOp::NullishCoalesce);
7177    (prev_logical && next_nullish) || (prev_nullish && next_logical)
7178}
7179
7180/// Spells out both groupings in the order the operators were written. Called
7181/// only for pairs `mixed_logical` accepts, so the non-`??` operator is `||` or `&&`.
7182fn mixed_logical_help(prev: BinOp, next: BinOp) -> String {
7183    let symbol = |op: BinOp| if op == BinOp::And { "&&" } else { "||" };
7184    let (first, second) = if prev == BinOp::NullishCoalesce {
7185        ("??", symbol(next))
7186    } else {
7187        (symbol(prev), "??")
7188    };
7189    format!(
7190        "add parentheses to choose the grouping: `(a {first} b) {second} c` or `a {first} (b {second} c)`"
7191    )
7192}
7193
7194const LOGICAL_AND_PREC: u8 = 2;
7195
7196/// The bindings a lowered `for…in` declares. `#` cannot start a source
7197/// identifier, so neither can clash with a user's name.
7198const FOR_IN_OBJECT: &str = "#for_in_object";
7199const FOR_IN_KEY: &str = "#for_in_key";
7200
7201/// The `for…in` object, held in a variable so it is evaluated once.
7202struct HeldForInObject {
7203    name: Ident,
7204    statement: StmtId,
7205}
7206
7207/// One side of a loose comparison that is always nullish.
7208#[derive(Clone, Copy)]
7209struct NullishOperand {
7210    literal: Nullish,
7211    /// Set for `void e` when `e` is not a literal and may have effects.
7212    effect: Option<VoidEffect>,
7213}
7214
7215#[derive(Clone, Copy, PartialEq, Eq)]
7216enum Nullish {
7217    Null,
7218    Undefined,
7219}
7220
7221impl Nullish {
7222    fn text(self) -> &'static str {
7223        match self {
7224            Nullish::Null => "null",
7225            Nullish::Undefined => "undefined",
7226        }
7227    }
7228
7229    /// The nullish value a loose comparison against `self` fails to match.
7230    fn other(self) -> Self {
7231        match self {
7232            Nullish::Null => Nullish::Undefined,
7233            Nullish::Undefined => Nullish::Null,
7234        }
7235    }
7236}
7237
7238/// A rejected `x == null`-style comparison, as written.
7239struct LooseComparison {
7240    nullish: NullishOperand,
7241    /// Whether the nullish side is written first, as in `null == x`.
7242    nullish_first: bool,
7243    /// `!=` rather than `==`.
7244    negated: bool,
7245}
7246
7247impl LooseComparison {
7248    fn strict_op(&self) -> &'static str {
7249        if self.negated { "!==" } else { "===" }
7250    }
7251
7252    /// `x op nullish`, in the order the comparison was written.
7253    fn ordered(&self, x: &str, op: &str, nullish: &str) -> String {
7254        if self.nullish_first {
7255            format!("{nullish} {op} {x}")
7256        } else {
7257            format!("{x} {op} {nullish}")
7258        }
7259    }
7260}
7261
7262#[derive(Clone, Copy)]
7263struct VoidEffect {
7264    /// The whole `void e`.
7265    void: Span,
7266    /// The `e`, without enclosing parentheses.
7267    operand: Span,
7268}
7269
7270fn is_literal(kind: &ExprKind) -> bool {
7271    matches!(
7272        kind,
7273        ExprKind::Number(_)
7274            | ExprKind::BigInt(_)
7275            | ExprKind::String(_)
7276            | ExprKind::Boolean(_)
7277            | ExprKind::Null
7278    )
7279}
7280
7281/// What `Parser::check_parameter_order` needs from either kind of parameter list.
7282struct ParamOrder<'p> {
7283    optional: bool,
7284    /// Neither optional, defaulted, nor rest.
7285    required: bool,
7286    /// `None` for a destructuring pattern.
7287    name: Option<&'p str>,
7288    span: Span,
7289}
7290
7291impl<'p> ParamOrder<'p> {
7292    fn of_param(param: &'p ParamDecl) -> Self {
7293        let (name, span) = match &param.pattern {
7294            Some(binding) => (None, binding.span()),
7295            None => (Some(param.name.name.as_str()), param.name.span),
7296        };
7297        Self {
7298            optional: param.optional,
7299            required: !param.is_omittable() && !param.rest,
7300            name,
7301            span,
7302        }
7303    }
7304
7305    fn of_field(field: &'p TypeAnnotationField) -> Self {
7306        Self {
7307            optional: field.optional,
7308            required: !field.optional && !field.rest,
7309            name: Some(field.name.name.as_str()),
7310            span: field.name.span,
7311        }
7312    }
7313}
7314
7315/// A binary operator at the cursor, which may span several tokens.
7316struct BinaryToken {
7317    op: BinOp,
7318    precedence: u8,
7319    tokens: usize,
7320}
7321
7322/// `<<`, `>>`, `>>>` or their compound assignments, read from adjacent angle tokens.
7323struct ShiftToken {
7324    op: BinOp,
7325    tokens: usize,
7326    /// The operator ends in `=`: `<<=`, `>>=`, `>>>=`.
7327    assignment: bool,
7328}
7329
7330impl ShiftToken {
7331    fn new(op: BinOp, tokens: usize, assignment: bool) -> Self {
7332        Self {
7333            op,
7334            tokens,
7335            assignment,
7336        }
7337    }
7338}
7339
7340/// Between relational (7) and additive (9) in the `peek_binop` table.
7341const SHIFT_PRECEDENCE: u8 = 8;
7342
7343#[derive(Clone, Copy, PartialEq)]
7344enum TupleElementLabel {
7345    None,
7346    /// `name: T`
7347    Required,
7348    /// `name?: T`
7349    Optional,
7350}
7351
7352fn peek_binop(kind: &TokenKind) -> Option<(BinOp, u8)> {
7353    Some(match kind {
7354        TokenKind::QuestionQuestion => (BinOp::NullishCoalesce, 0),
7355        TokenKind::PipePipe => (BinOp::Or, 1),
7356        TokenKind::AmpAmp => (BinOp::And, LOGICAL_AND_PREC),
7357        TokenKind::Pipe => (BinOp::BitOr, 3),
7358        TokenKind::Caret => (BinOp::BitXor, 4),
7359        TokenKind::Amp => (BinOp::BitAnd, 5),
7360        TokenKind::EqEqEq | TokenKind::EqEq => (BinOp::Eq, 6),
7361        TokenKind::BangEqEq | TokenKind::BangEq => (BinOp::NotEq, 6),
7362        TokenKind::LessThan => (BinOp::Lt, 7),
7363        TokenKind::GreaterThan => (BinOp::Gt, 7),
7364        TokenKind::LessEquals => (BinOp::Le, 7),
7365        TokenKind::GreaterEquals => (BinOp::Ge, 7),
7366        TokenKind::In => (BinOp::In, 7),
7367        // Shifts take `SHIFT_PRECEDENCE` (8); they span several angle tokens, so
7368        // `Parser::peek_shift` recognizes them rather than this table.
7369        TokenKind::Plus => (BinOp::Add, 9),
7370        TokenKind::Minus => (BinOp::Sub, 9),
7371        TokenKind::Star => (BinOp::Mul, 10),
7372        TokenKind::Slash => (BinOp::Div, 10),
7373        TokenKind::Percent => (BinOp::Rem, 10),
7374        // `**` recurses with `prec` not `prec + 1` — that's what makes it right-associative.
7375        TokenKind::StarStar => (BinOp::Pow, 11),
7376        _ => return None,
7377    })
7378}
7379
7380/// After `as` or `satisfies`, a token that can only extend or end an operand, never
7381/// begin a type. `[` counts, as TypeScript reads `delete as [0]` as an index, so a
7382/// tuple cast of a binding named `delete` needs parentheses.
7383fn cannot_start_type(kind: &TokenKind) -> bool {
7384    matches!(
7385        kind,
7386        TokenKind::Dot
7387            | TokenKind::QuestionDot
7388            | TokenKind::LeftBracket
7389            | TokenKind::Bang
7390            | TokenKind::Semicolon
7391            | TokenKind::RightParen
7392            | TokenKind::RightBrace
7393            | TokenKind::Comma
7394            | TokenKind::Eof
7395    )
7396}
7397
7398/// The lowest precedence the right operand of `op` may contain. `**` is
7399/// right-associative, so it recurses at its own level. An operand of `??` may
7400/// not be an unparenthesized `||` / `&&` expression, so it stops below them too.
7401fn right_operand_min_prec(op: BinOp, prec: u8) -> u8 {
7402    match op {
7403        BinOp::Pow => prec,
7404        BinOp::NullishCoalesce => LOGICAL_AND_PREC + 1,
7405        _ => prec + 1,
7406    }
7407}
7408
7409fn compound_op_for_token(kind: &TokenKind) -> Option<BinOp> {
7410    Some(match kind {
7411        TokenKind::PlusEquals => BinOp::Add,
7412        TokenKind::MinusEquals => BinOp::Sub,
7413        TokenKind::StarEquals => BinOp::Mul,
7414        TokenKind::SlashEquals => BinOp::Div,
7415        TokenKind::PercentEquals => BinOp::Rem,
7416        TokenKind::StarStarEquals => BinOp::Pow,
7417        TokenKind::AmpEquals => BinOp::BitAnd,
7418        TokenKind::PipeEquals => BinOp::BitOr,
7419        TokenKind::CaretEquals => BinOp::BitXor,
7420        _ => return None,
7421    })
7422}
7423
7424fn is_assign_lookahead(kind: &TokenKind) -> bool {
7425    matches!(kind, TokenKind::Equals) || compound_op_for_token(kind).is_some()
7426}
7427
7428/// Parser productions use `None` for recovery; arena errors also latch a fatal
7429/// cause so speculative parsing cannot turn them into ordinary syntax errors.
7430fn parse_arena_result<T>(
7431    result: Result<T, crate::arena::ArenaError>,
7432    fatal: &mut Option<CompilerFailure>,
7433) -> Option<T> {
7434    match result {
7435        Ok(value) => Some(value),
7436        Err(error) => {
7437            fatal.get_or_insert_with(|| error.into_compiler_failure(CompilerStage::Parse));
7438            None
7439        }
7440    }
7441}
7442
7443/// Whether a rest parameter's annotation names an array: `T[]`, or `readonly T[]`.
7444fn is_rest_array_annotation(ty: &crate::TypeAnnotation) -> bool {
7445    use crate::ast::TypeAnnotationKind;
7446    match &ty.kind {
7447        TypeAnnotationKind::Array(_) => true,
7448        TypeAnnotationKind::Readonly(inner) => matches!(inner.kind, TypeAnnotationKind::Array(_)),
7449        _ => false,
7450    }
7451}
7452
7453#[cfg(test)]
7454mod tests {
7455    use super::{MAX_ERRORS, MAX_PARSE_DEPTH, Parser, parse};
7456    use crate::source::Sources;
7457    use crate::{Asi, Ast, ExprKind, FileId, ImportKind, StmtKind, Token, TokenKind, diagnostics};
7458
7459    const F: FileId = FileId(0);
7460
7461    #[test]
7462    fn for_in_destructuring_reports_the_invalid_head() {
7463        for declaration in ["let ", "const ", ""] {
7464            for pattern in [
7465                "[a, b]",
7466                "{a, b}",
7467                "[a, ...rest]",
7468                "{a: [b]}",
7469                "[a = 'x' in {}]",
7470                "{a = (1 < 2)}",
7471                "{['a']: a}",
7472            ] {
7473                let source = format!("for ({declaration}{pattern} in []) {{}}");
7474                let (_, diagnostics) = parse_str(&source);
7475                assert_eq!(diagnostics.len(), 1, "{source}: {diagnostics:?}");
7476                let diagnostic = diagnostics.first().expect("a pattern is invalid in for…in");
7477                assert_eq!(
7478                    diagnostic.message,
7479                    "the left-hand side of `for…in` cannot be a destructuring pattern",
7480                    "{source}: {diagnostics:?}"
7481                );
7482                assert_eq!(
7483                    diagnostic.span.start as usize,
7484                    source.find(pattern).unwrap()
7485                );
7486                assert!(diagnostic.help.iter().any(|h| h.contains("obj[k]")));
7487                assert!(
7488                    diagnostic
7489                        .help
7490                        .iter()
7491                        .any(|h| h.contains("Object.entries(obj)"))
7492                );
7493                assert!(
7494                    !diagnostics
7495                        .iter()
7496                        .any(|d| d.message.contains("initializer"))
7497                );
7498            }
7499        }
7500    }
7501
7502    #[test]
7503    fn unfinished_for_in_patterns_report_syntax_errors() {
7504        for source in ["for (let [a in []", "for (const {a in []", "for ([a"] {
7505            let (_, diagnostics) = parse_str(source);
7506            assert!(!diagnostics.is_empty(), "{source}");
7507        }
7508    }
7509
7510    #[test]
7511    fn for_in_destructuring_recovery_preserves_loop_boundaries() {
7512        for body in ["{}", "{ const [local] = [1]; break; }", ";"] {
7513            let source = format!(
7514                "function main(): void {{
7515                    for (let [a] in []) {{ for (const [b] in {{}}) {body} }}
7516                    for (const {{a}} in {{}}) {body}
7517                    for (let [a] in []) for (const [b] in {{}}) {body}
7518                    const after = 1;
7519                }}"
7520            );
7521            let (ast, diagnostics) = parse_str(&source);
7522            assert_eq!(diagnostics.len(), 5, "{source}: {diagnostics:?}");
7523            assert!(
7524                diagnostics.iter().all(|d| d.message
7525                    == "the left-hand side of `for…in` cannot be a destructuring pattern")
7526            );
7527            let main = ast.try_stmt(ast.top_level[0]).unwrap();
7528            let StmtKind::Function { body, .. } = main.kind else {
7529                panic!("expected main function");
7530            };
7531            let StmtKind::Block(ref statements) = ast.try_stmt(body).unwrap().kind else {
7532                panic!("expected function body");
7533            };
7534            assert_eq!(statements.len(), 4, "all sibling statements are preserved");
7535            assert!(matches!(
7536                ast.try_stmt(statements[3]).unwrap().kind,
7537                StmtKind::Const { .. }
7538            ));
7539        }
7540    }
7541
7542    #[test]
7543    fn for_in_head_scanning_handles_deep_and_unfinished_patterns_iteratively() {
7544        for opening in ['[', '{'] {
7545            let closing = if opening == '[' { ']' } else { '}' };
7546            let pattern = opening.to_string().repeat(4096) + &closing.to_string().repeat(4096);
7547            let source = format!("{pattern} in obj");
7548            let parser = parser_from_source(&source);
7549            let in_at = parser.scan_for_in_head().expect("balanced pattern head");
7550            assert!(matches!(parser.tokens[in_at].kind, TokenKind::In));
7551            let source = format!("let {pattern} of obj");
7552            assert!(parser_from_source(&source).scan_for_of_shape());
7553
7554            let source = opening.to_string().repeat(4096);
7555            assert!(parser_from_source(&source).scan_for_in_head().is_none());
7556            let source = format!("let {source}");
7557            assert!(!parser_from_source(&source).scan_for_of_shape());
7558        }
7559    }
7560
7561    #[test]
7562    fn index_key_type_does_not_disrupt_initializer_parsing() {
7563        let (ast, diagnostics) =
7564            parse_str("const byId: { [id: boolean]: string } = {}; const next = 1;");
7565        assert!(
7566            diagnostics.is_empty(),
7567            "key-domain errors belong to typechecking: {diagnostics:?}"
7568        );
7569        assert_eq!(ast.top_level.len(), 2);
7570    }
7571
7572    #[test]
7573    fn malformed_direct_token_streams_return_typed_failures() {
7574        use crate::compiler_error::CompilerFailure;
7575        for tokens in [
7576            Vec::new(),
7577            vec![Token::new(
7578                TokenKind::Identifier,
7579                crate::Span::new(F, 0, 2).unwrap(),
7580            )],
7581            vec![
7582                Token::new(
7583                    TokenKind::Identifier,
7584                    crate::Span {
7585                        file: F,
7586                        start: 1,
7587                        end: 2,
7588                    },
7589                ),
7590                Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
7591            ],
7592            vec![
7593                Token::new(TokenKind::Eof, crate::Span::at(F)),
7594                Token::new(TokenKind::Eof, crate::Span::at(F)),
7595            ],
7596            vec![
7597                Token::new(
7598                    TokenKind::Identifier,
7599                    crate::Span {
7600                        file: F,
7601                        start: 2,
7602                        end: 0,
7603                    },
7604                ),
7605                Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
7606            ],
7607            vec![Token::new(TokenKind::Eof, crate::Span::at(FileId(9)))],
7608        ] {
7609            let error = super::parse_checked("é", tokens, F).expect_err("invalid token metadata");
7610            assert!(matches!(
7611                error.fatal,
7612                Some(CompilerFailure::Internal { .. })
7613            ));
7614        }
7615    }
7616
7617    #[test]
7618    fn malformed_documentation_metadata_returns_a_fatal_error() {
7619        let source = "/** hello */ function main(): void {}";
7620        let valid = tokens_of(source);
7621        for doc in [
7622            crate::RawDoc {
7623                text: "/** hi */".into(),
7624                span: crate::Span {
7625                    file: F,
7626                    start: u32::MAX,
7627                    end: u32::MAX,
7628                },
7629            },
7630            crate::RawDoc {
7631                text: "/** different */".into(),
7632                span: crate::Span::new(F, 0, 12).unwrap(),
7633            },
7634            crate::RawDoc {
7635                text: "/** hello */".into(),
7636                span: crate::Span::new(FileId(9), 0, 12).unwrap(),
7637            },
7638        ] {
7639            let mut tokens = valid.clone();
7640            tokens[0].leading_doc = Some(doc);
7641            let error = super::parse_checked(source, tokens, F).expect_err("invalid doc metadata");
7642            assert!(error.to_string().contains("documentation metadata"));
7643        }
7644        assert!(super::parse_checked(source, valid, F).is_ok());
7645    }
7646
7647    #[test]
7648    fn invalid_parser_dispatch_survives_diagnostic_rollback() {
7649        let mut parser = parser_from_source("let x = 1;");
7650        assert!(parser.try_parse_type_args_only().is_none());
7651        parser.diagnostics.clear();
7652        assert!(parser.fatal.is_some());
7653        assert!(parser.parse_expression().is_none());
7654    }
7655
7656    #[test]
7657    fn invalid_arena_read_stops_parser_and_survives_diagnostic_rollback() {
7658        use crate::{
7659            ExprId,
7660            compiler_error::{CompilerFailure, CompilerStage},
7661        };
7662        let mut parser = parser_from_source("let x = 1;");
7663        assert!(parser.statement_kind_for(ExprId(u32::MAX)).is_none());
7664        assert!(matches!(
7665            parser.fatal,
7666            Some(CompilerFailure::Internal {
7667                stage: CompilerStage::Parse,
7668                span: None,
7669                ..
7670            })
7671        ));
7672        parser.diagnostics.clear();
7673        let position = parser.pos;
7674        assert!(parser.parse_statement().is_none());
7675        parser.recover();
7676        assert_eq!(parser.pos, position);
7677        assert!(parser.ast.expr_ids().unwrap().next().is_none());
7678    }
7679
7680    fn tokens_of(source: &str) -> Vec<Token> {
7681        let mut asi = Asi::new(source, crate::FileId(0));
7682        let mut tokens = Vec::new();
7683        loop {
7684            let tok = asi.next_token();
7685            let is_eof = matches!(tok.kind, TokenKind::Eof);
7686            tokens.push(tok);
7687            if is_eof {
7688                break;
7689            }
7690        }
7691        let diags = asi.into_diagnostics();
7692        assert!(diags.is_empty(), "unexpected lexer diagnostics: {diags:?}");
7693        tokens
7694    }
7695
7696    fn parse_str(source: &str) -> (Ast, Vec<crate::Diagnostic>) {
7697        parse(source, tokens_of(source), crate::FileId(0))
7698    }
7699
7700    fn parser_from_source(source: &str) -> Parser<'_> {
7701        Parser {
7702            source,
7703            tokens: tokens_of(source),
7704            split_type_tokens: Vec::new(),
7705            file: F,
7706            pos: 0,
7707            ast: Ast::new(),
7708            diagnostics: Vec::new(),
7709            block_depth: 0,
7710            class_member_body_depth: 0,
7711            function_expression_body_depth: 0,
7712            recursion_depth: 0,
7713            recursion_limit_span: None,
7714            resume_at_next_member: false,
7715            last_as_type_end: None,
7716            trailing_declarators: Vec::new(),
7717            eof: Token::new(
7718                TokenKind::Eof,
7719                crate::Span::new(F, source.len() as u32, source.len() as u32).unwrap(),
7720            ),
7721            fatal: None,
7722        }
7723    }
7724
7725    #[test]
7726    fn recursion_budget_accepts_boundary_and_restores_depth() {
7727        let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH - 1));
7728        let mut parser = parser_from_source(&source);
7729        assert!(parser.parse_unary().is_some());
7730        assert_eq!(parser.recursion_depth, 0);
7731        assert!(parser.recursion_limit_span.is_none());
7732
7733        let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH));
7734        let mut parser = parser_from_source(&source);
7735        assert!(parser.parse_unary().is_none());
7736        assert_eq!(parser.recursion_depth, 0);
7737        assert!(parser.recursion_limit_span.is_some());
7738    }
7739
7740    #[test]
7741    fn parse_empty_input() {
7742        let (ast, diags) = parse_str("");
7743        assert!(diags.is_empty());
7744        assert!(ast.top_level.is_empty());
7745    }
7746
7747    #[test]
7748    fn any_type_is_rejected() {
7749        // `any` is not supported — the parser rejects it in every type position
7750        // (annotation, cast target, array/generic element) and points to the fix.
7751        for src in [
7752            "function main(): void { const x: any = 1; }",
7753            "function main(): void { const x = 1 as any; }",
7754            "function main(): void { const x: any[] = []; }",
7755        ] {
7756            let (_ast, diags) = parse_str(src);
7757            assert!(
7758                diags
7759                    .iter()
7760                    .any(|d| d.message.contains("`any` is not supported")),
7761                "expected an `any` rejection for {src:?}, got: {diags:?}",
7762            );
7763            assert!(
7764                diags
7765                    .iter()
7766                    .any(|d| d.help.iter().any(|h| h.contains("`unknown`"))),
7767                "expected the fix-shape help for {src:?}, got: {diags:?}",
7768            );
7769        }
7770    }
7771
7772    #[test]
7773    fn any_catch_binding_help_suggests_catch_forms() {
7774        // `unknown` is not a valid catch binding type, so the general `any`
7775        // help would send the reader to a second error.
7776        let (_ast, diags) = parse_str("function main(): void { try { } catch (e: any) { } }");
7777        let any_diags: Vec<_> = diags
7778            .iter()
7779            .filter(|d| d.message.contains("`any` is not supported"))
7780            .collect();
7781        assert_eq!(any_diags.len(), 1, "got: {diags:?}");
7782        assert!(
7783            any_diags[0].help.iter().any(|h| h.contains("`catch (e)`")),
7784            "got: {diags:?}"
7785        );
7786        assert!(
7787            !any_diags[0].help.iter().any(|h| h.contains("`unknown`")),
7788            "got: {diags:?}"
7789        );
7790    }
7791
7792    #[test]
7793    fn arrow_return_type_scanner_matches_type_grammar() {
7794        // Every form the annotation grammar accepts must also be accepted by
7795        // the arrow-disambiguator's lookahead (scan_past_type_annotation):
7796        // a miss there turns a valid arrow into `expected expression`.
7797        // When adding a type form to parse_type_array, add a row here.
7798        for ty in [
7799            "number",
7800            "void",
7801            "null",
7802            "ns.Foo",
7803            "Map<string, number>",
7804            "ns.Foo<string>[]",
7805            "number[]",
7806            "[number, number]",
7807            "[number, [string, boolean]]",
7808            "[number, string][]",
7809            "{ a: number }",
7810            "{ a: number }[]",
7811            "\"on\" | \"off\"",
7812            "1 | 2",
7813            "Point | null",
7814            "| number | string",
7815            "(n: number) => number",
7816            "(n: number) => [number, number]",
7817            "readonly number[]",
7818            "readonly number[][]",
7819            "readonly [number, string]",
7820            "readonly number[] | null",
7821            "[first: number, second: string]",
7822            "[new: number, string]",
7823            "keyof { a: number }",
7824        ] {
7825            let annotation = format!("function f(): void {{ const a: {ty} = x; }}");
7826            let (_ast, diags) = parse_str(&annotation);
7827            assert!(
7828                diags.is_empty(),
7829                "annotation grammar rejected {ty:?}: {diags:?}",
7830            );
7831
7832            let arrow = format!("function f(): void {{ const g = (): {ty} => x; }}");
7833            let (_ast, diags) = parse_str(&arrow);
7834            assert!(
7835                diags.is_empty(),
7836                "arrow lookahead rejected return type {ty:?}: {diags:?}",
7837            );
7838        }
7839
7840        // Type-predicate returns ride the same scanner.
7841        let (_ast, diags) =
7842            parse_str("function f(): void { const g = (v: unknown): v is string => x; }");
7843        assert!(
7844            diags.is_empty(),
7845            "arrow lookahead rejected predicate return: {diags:?}",
7846        );
7847    }
7848
7849    #[test]
7850    fn bare_semicolons_are_silent_empty_statements() {
7851        let (ast, diags) = parse_str(";");
7852        assert!(diags.is_empty());
7853        assert!(ast.top_level.is_empty());
7854    }
7855
7856    #[test]
7857    fn parse_single_non_expression_token_errors() {
7858        let (ast, diags) = parse_str("}");
7859        assert_eq!(diags.len(), 1);
7860        assert_eq!(diags[0].message, "expected expression");
7861        assert!(ast.top_level.is_empty());
7862    }
7863
7864    #[test]
7865    fn recovery_across_many_bad_starts() {
7866        let (ast, diags) = parse_str("} } } x;");
7867        assert_eq!(diags.len(), 3);
7868        for d in &diags {
7869            assert_eq!(d.message, "expected expression");
7870        }
7871        assert_eq!(ast.top_level.len(), 1);
7872    }
7873
7874    #[test]
7875    fn recovery_consumes_semicolon() {
7876        let mut p = parser_from_source("foo ;");
7877        p.error_at_peek("test");
7878        p.recover();
7879        assert!(matches!(
7880            p.peek().kind,
7881            TokenKind::Semicolon | TokenKind::Eof
7882        ));
7883        assert!(matches!(p.peek().kind, TokenKind::Eof));
7884    }
7885
7886    #[test]
7887    fn recovery_stops_at_right_brace() {
7888        let mut p = parser_from_source("foo }");
7889        p.error_at_peek("test");
7890        p.recover();
7891        assert!(matches!(p.peek().kind, TokenKind::RightBrace));
7892    }
7893
7894    #[test]
7895    fn recovery_stops_at_let_keyword() {
7896        let mut p = parser_from_source("foo let x");
7897        p.error_at_peek("test");
7898        p.recover();
7899        assert!(matches!(p.peek().kind, TokenKind::Let));
7900    }
7901
7902    #[test]
7903    fn recovery_stops_at_function_keyword() {
7904        let mut p = parser_from_source("foo function bar");
7905        p.error_at_peek("test");
7906        p.recover();
7907        assert!(matches!(p.peek().kind, TokenKind::Function));
7908    }
7909
7910    #[test]
7911    fn error_cap_at_20() {
7912        let source = "} ".repeat(25);
7913        let (_, diags) = parse_str(&source);
7914        assert_eq!(diags.len(), MAX_ERRORS);
7915    }
7916
7917    #[test]
7918    fn driver_makes_progress_past_unmatched_brace() {
7919        let (ast, diags) = parse_str("} x;");
7920        assert_eq!(diags.len(), 1);
7921        assert_eq!(diags[0].message, "expected expression");
7922        assert_eq!(ast.top_level.len(), 1);
7923    }
7924
7925    #[test]
7926    fn snapshot_multi_error_render() {
7927        let source = "foo bar;\nbaz qux;\nhello world;";
7928        let (_, diags) = parse_str(source);
7929        let (sources, _) = Sources::single("script.subm", source).unwrap();
7930        let rendered: String = diags
7931            .iter()
7932            .map(|d| diagnostics::render(d, &sources))
7933            .collect::<Vec<_>>()
7934            .join("\n");
7935        insta::assert_snapshot!(rendered);
7936    }
7937
7938    fn single_stmt(ast: &Ast) -> &crate::Stmt {
7939        assert_eq!(ast.top_level.len(), 1);
7940        ast.try_stmt(ast.top_level[0]).unwrap()
7941    }
7942
7943    fn expr_of_single_stmt(ast: &Ast) -> &crate::Expr {
7944        let stmt = single_stmt(ast);
7945        match stmt.kind {
7946            crate::StmtKind::Expr(id) => ast.try_expr(id).unwrap(),
7947            _ => panic!("expected expression statement, got {:?}", stmt.kind),
7948        }
7949    }
7950
7951    #[test]
7952    fn parse_number_literal() {
7953        let (ast, diags) = parse_str("42;");
7954        assert!(diags.is_empty());
7955        let expr = expr_of_single_stmt(&ast);
7956        assert_eq!(expr.kind, crate::ExprKind::Number(42.0));
7957        assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
7958    }
7959
7960    #[test]
7961    fn parse_string_literal() {
7962        let (ast, diags) = parse_str(r#""s";"#);
7963        assert!(diags.is_empty());
7964        let expr = expr_of_single_stmt(&ast);
7965        assert_eq!(expr.kind, crate::ExprKind::String("s".to_string()));
7966        assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7967    }
7968
7969    #[test]
7970    fn parse_boolean_true() {
7971        let (ast, diags) = parse_str("true;");
7972        assert!(diags.is_empty());
7973        let expr = expr_of_single_stmt(&ast);
7974        assert_eq!(expr.kind, crate::ExprKind::Boolean(true));
7975        assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7976    }
7977
7978    #[test]
7979    fn parse_boolean_false() {
7980        let (ast, diags) = parse_str("false;");
7981        assert!(diags.is_empty());
7982        let expr = expr_of_single_stmt(&ast);
7983        assert_eq!(expr.kind, crate::ExprKind::Boolean(false));
7984        assert_eq!(expr.span, crate::Span::new(F, 0, 5).unwrap());
7985    }
7986
7987    #[test]
7988    fn parse_null_literal() {
7989        let (ast, diags) = parse_str("null;");
7990        assert!(diags.is_empty());
7991        let expr = expr_of_single_stmt(&ast);
7992        assert_eq!(expr.kind, crate::ExprKind::Null);
7993        assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7994    }
7995
7996    #[test]
7997    fn parse_identifier() {
7998        let (ast, diags) = parse_str("x;");
7999        assert!(diags.is_empty());
8000        let expr = expr_of_single_stmt(&ast);
8001        assert!(matches!(expr.kind, crate::ExprKind::Identifier(_)));
8002        assert_eq!(expr.span, crate::Span::new(F, 0, 1).unwrap());
8003    }
8004
8005    #[test]
8006    fn reserved_keywords_remain_invalid_declaration_names() {
8007        let (_ast, diags) = parse_str("function default(): void {}");
8008        assert_eq!(diags.len(), 1, "{diags:#?}");
8009        assert_eq!(
8010            diags[0].message,
8011            "`default` is a reserved keyword and can't be used as a name"
8012        );
8013        assert!(
8014            diags[0]
8015                .help
8016                .iter()
8017                .any(|h| h == "rename it, for example: `defaultValue`"),
8018            "expected rename help, got: {diags:?}"
8019        );
8020    }
8021
8022    #[test]
8023    fn eval_and_arguments_cannot_name_bindings() {
8024        let bindings = [
8025            ("let arguments = 1;", "arguments"),
8026            ("const [eval] = [1];", "eval"),
8027            ("const { a: eval } = { a: 1 };", "eval"),
8028            ("const { eval } = { eval: 1 };", "eval"),
8029            ("const [...arguments] = [1];", "arguments"),
8030            ("function eval(): void {}", "eval"),
8031            ("function f(arguments: number): void {}", "arguments"),
8032            ("const f = arguments => arguments;", "arguments"),
8033            ("const f = (...eval: number[]) => 1;", "eval"),
8034            ("const f = function eval(): void {};", "eval"),
8035            ("for (const eval of [1]) {}", "eval"),
8036            ("try {} catch (arguments) {}", "arguments"),
8037            ("type F = (eval: number) => void;", "eval"),
8038            ("type G = (...arguments: number[]) => void;", "arguments"),
8039            ("type H = { [eval: string]: number };", "eval"),
8040        ];
8041        for (source, word) in bindings {
8042            let (_ast, diags) = parse_str(source);
8043            assert!(
8044                diags
8045                    .iter()
8046                    .any(|d| d.message == format!("`{word}` can't name a binding in strict mode")),
8047                "{source}: {diags:#?}"
8048            );
8049        }
8050    }
8051
8052    #[test]
8053    fn eval_and_arguments_still_name_types_and_properties() {
8054        for source in [
8055            "class eval {}",
8056            "interface arguments { eval: number }",
8057            "type eval = number;",
8058            "enum arguments { eval }",
8059            "const o = { eval: 1, arguments: 2 };",
8060            "import { eval } from \"./m\";",
8061        ] {
8062            let (_ast, diags) = parse_str(source);
8063            assert!(diags.is_empty(), "{source}: {diags:#?}");
8064        }
8065    }
8066
8067    #[test]
8068    fn reserved_keywords_remain_invalid_parameter_names() {
8069        let (_ast, diags) = parse_str("function f(default: string): void {}");
8070        assert_eq!(diags.len(), 1, "{diags:#?}");
8071        assert_eq!(
8072            diags[0].message,
8073            "`default` is a reserved keyword and can't be used as a name"
8074        );
8075        assert!(
8076            diags[0]
8077                .help
8078                .iter()
8079                .any(|h| h == "rename it, for example: `defaultValue`"),
8080            "expected rename help, got: {diags:?}"
8081        );
8082    }
8083
8084    #[test]
8085    fn contextual_keywords_are_valid_names() {
8086        for src in [
8087            "const type = \"x\";",
8088            "const from = 1;",
8089            "let of = 2;",
8090            "const as = 3;",
8091            "const is = 4;",
8092            "function f(from: string): void {}",
8093        ] {
8094            let (_ast, diags) = parse_str(src);
8095            assert!(
8096                diags.is_empty(),
8097                "unexpected diagnostics for {src:?}: {diags:#?}"
8098            );
8099        }
8100    }
8101
8102    #[test]
8103    fn type_alias_dispatch_table() {
8104        let (ast, diags) = parse_str("type X = number;");
8105        assert!(diags.is_empty(), "{diags:#?}");
8106        assert!(matches!(
8107            ast.try_stmt(ast.top_level[0]).unwrap().kind,
8108            crate::StmtKind::TypeAliasDecl { .. }
8109        ));
8110
8111        // A binding named `type` keeps expression/assignment statements working.
8112        for src in ["type = 5;", "type;", "type(1);", "type < 3;", "type.foo;"] {
8113            let (ast, diags) = parse_str(src);
8114            assert!(
8115                diags.is_empty(),
8116                "unexpected diagnostics for {src:?}: {diags:#?}"
8117            );
8118            assert!(
8119                !matches!(
8120                    ast.try_stmt(ast.top_level[0]).unwrap().kind,
8121                    crate::StmtKind::TypeAliasDecl { .. }
8122                ),
8123                "{src:?} must not parse as a type alias"
8124            );
8125        }
8126
8127        let (_ast, diags) = parse_str("type X;");
8128        assert!(
8129            diags
8130                .iter()
8131                .any(|d| d.message == "expected `=` after type alias name"),
8132            "bare `type X` should commit to the alias diagnostic, got: {diags:#?}"
8133        );
8134    }
8135
8136    #[test]
8137    fn export_type_alias_dispatch() {
8138        let (ast, diags) = parse_str("export type X = number;");
8139        assert!(diags.is_empty(), "{diags:#?}");
8140        assert_eq!(ast.exported_decls.len(), 1);
8141        assert!(matches!(
8142            ast.try_stmt(ast.exported_decls[0].stmt).unwrap().kind,
8143            crate::StmtKind::TypeAliasDecl { .. }
8144        ));
8145
8146        let (_ast, diags) = parse_str("export type = 5;");
8147        assert!(
8148            diags
8149                .iter()
8150                .any(|d| d.message == "expected a declaration after `export`"),
8151            "`export type = 5` must not become an exported assignment, got: {diags:#?}"
8152        );
8153    }
8154
8155    #[test]
8156    fn parse_paren_simple() {
8157        let (ast, diags) = parse_str("(x);");
8158        assert!(diags.is_empty());
8159        let expr = expr_of_single_stmt(&ast);
8160        let crate::ExprKind::Paren(inner_id) = expr.kind else {
8161            panic!("expected Paren, got {:?}", expr.kind);
8162        };
8163        assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
8164        let inner = ast.try_expr(inner_id).unwrap();
8165        assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
8166        assert_eq!(inner.span, crate::Span::new(F, 1, 2).unwrap());
8167    }
8168
8169    #[test]
8170    fn parse_paren_nested() {
8171        let (ast, diags) = parse_str("((x));");
8172        assert!(diags.is_empty());
8173        let outer = expr_of_single_stmt(&ast);
8174        let crate::ExprKind::Paren(mid_id) = outer.kind else {
8175            panic!("expected outer Paren");
8176        };
8177        assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
8178        let mid = ast.try_expr(mid_id).unwrap();
8179        let crate::ExprKind::Paren(inner_id) = mid.kind else {
8180            panic!("expected inner Paren");
8181        };
8182        assert_eq!(mid.span, crate::Span::new(F, 1, 4).unwrap());
8183        let inner = ast.try_expr(inner_id).unwrap();
8184        assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
8185        assert_eq!(inner.span, crate::Span::new(F, 2, 3).unwrap());
8186    }
8187
8188    #[test]
8189    fn parse_sequence_of_atoms() {
8190        let (ast, diags) = parse_str(r#"42; "hi"; true; null; x; (x);"#);
8191        assert!(diags.is_empty());
8192        assert_eq!(ast.top_level.len(), 6);
8193        insta::assert_debug_snapshot!(ast);
8194    }
8195
8196    #[test]
8197    fn missing_semicolon_after_expression() {
8198        let (ast, diags) = parse_str("42 43");
8199        assert_eq!(diags.len(), 1);
8200        assert_eq!(diags[0].message, "expected `;` after expression");
8201        assert!(ast.top_level.is_empty());
8202    }
8203
8204    #[test]
8205    fn empty_paren_diagnoses() {
8206        let (ast, diags) = parse_str("();");
8207        assert_eq!(diags.len(), 1);
8208        assert_eq!(diags[0].message, "expected expression");
8209        assert!(ast.top_level.is_empty());
8210    }
8211
8212    #[test]
8213    fn unterminated_paren_diagnoses() {
8214        let (ast, diags) = parse_str("(x;");
8215        assert_eq!(diags.len(), 1);
8216        assert_eq!(diags[0].message, "expected `)`");
8217        assert!(ast.top_level.is_empty());
8218    }
8219
8220    fn binary(e: &crate::Expr) -> (crate::BinOp, crate::ExprId, crate::ExprId) {
8221        match e.kind {
8222            crate::ExprKind::Binary { op, lhs, rhs } => (op, lhs, rhs),
8223            _ => panic!("expected Binary, got {:?}", e.kind),
8224        }
8225    }
8226
8227    #[test]
8228    fn parse_binary_add() {
8229        let (ast, diags) = parse_str("a + b;");
8230        assert!(diags.is_empty());
8231        let outer = expr_of_single_stmt(&ast);
8232        let (op, lhs, rhs) = binary(outer);
8233        assert_eq!(op, crate::BinOp::Add);
8234        assert!(matches!(
8235            ast.try_expr(lhs).unwrap().kind,
8236            crate::ExprKind::Identifier(_)
8237        ));
8238        assert!(matches!(
8239            ast.try_expr(rhs).unwrap().kind,
8240            crate::ExprKind::Identifier(_)
8241        ));
8242        assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
8243    }
8244
8245    #[test]
8246    fn bitwise_precedence_and_shift_adjacency() {
8247        let (ast, diags) = parse_str("a | b ^ c & d == e >> f + g;");
8248        assert!(diags.is_empty(), "{diags:?}");
8249        let (op, _, mut rhs) = binary(expr_of_single_stmt(&ast));
8250        assert_eq!(op, crate::BinOp::BitOr);
8251        for expected in [
8252            crate::BinOp::BitXor,
8253            crate::BinOp::BitAnd,
8254            crate::BinOp::Eq,
8255            crate::BinOp::Shr,
8256            crate::BinOp::Add,
8257        ] {
8258            let (op, _, next) = binary(ast.try_expr(rhs).unwrap());
8259            assert_eq!(op, expected);
8260            rhs = next;
8261        }
8262        for source in ["a > > b;", "a >/*gap*/> b;", "a < < b;"] {
8263            let (_, diags) = parse_str(source);
8264            assert!(!diags.is_empty(), "{source}");
8265        }
8266    }
8267
8268    #[test]
8269    fn bitwise_shift_assignment_span() {
8270        let (ast, diags) = parse_str("a >>>= b;");
8271        assert!(diags.is_empty(), "{diags:?}");
8272        let crate::StmtKind::CompoundAssign { op, op_span, .. } = single_stmt(&ast).kind else {
8273            panic!("expected assignment");
8274        };
8275        assert_eq!(op, crate::BinOp::UnsignedShr);
8276        assert_eq!(op_span, crate::Span::new(F, 2, 6).unwrap());
8277    }
8278
8279    #[test]
8280    fn precedence_mul_binds_tighter_than_add() {
8281        let (ast, diags) = parse_str("a + b * c;");
8282        assert!(diags.is_empty());
8283        let outer = expr_of_single_stmt(&ast);
8284        let (op, lhs, rhs) = binary(outer);
8285        assert_eq!(op, crate::BinOp::Add);
8286        assert!(matches!(
8287            ast.try_expr(lhs).unwrap().kind,
8288            crate::ExprKind::Identifier(_)
8289        ));
8290        let (rop, rlhs, rrhs) = binary(ast.try_expr(rhs).unwrap());
8291        assert_eq!(rop, crate::BinOp::Mul);
8292        assert!(matches!(
8293            ast.try_expr(rlhs).unwrap().kind,
8294            crate::ExprKind::Identifier(_)
8295        ));
8296        assert!(matches!(
8297            ast.try_expr(rrhs).unwrap().kind,
8298            crate::ExprKind::Identifier(_)
8299        ));
8300    }
8301
8302    #[test]
8303    fn precedence_mul_left_of_add() {
8304        let (ast, diags) = parse_str("a * b + c;");
8305        assert!(diags.is_empty());
8306        let outer = expr_of_single_stmt(&ast);
8307        let (op, lhs, rhs) = binary(outer);
8308        assert_eq!(op, crate::BinOp::Add);
8309        let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
8310        assert_eq!(lop, crate::BinOp::Mul);
8311        assert!(matches!(
8312            ast.try_expr(rhs).unwrap().kind,
8313            crate::ExprKind::Identifier(_)
8314        ));
8315    }
8316
8317    #[test]
8318    fn left_associative_same_precedence() {
8319        let (ast, diags) = parse_str("a - b - c;");
8320        assert!(diags.is_empty());
8321        let outer = expr_of_single_stmt(&ast);
8322        let (op, lhs, rhs) = binary(outer);
8323        assert_eq!(op, crate::BinOp::Sub);
8324        let (lop, llhs, lrhs) = binary(ast.try_expr(lhs).unwrap());
8325        assert_eq!(lop, crate::BinOp::Sub);
8326        assert!(matches!(
8327            ast.try_expr(llhs).unwrap().kind,
8328            crate::ExprKind::Identifier(_)
8329        ));
8330        assert!(matches!(
8331            ast.try_expr(lrhs).unwrap().kind,
8332            crate::ExprKind::Identifier(_)
8333        ));
8334        assert!(matches!(
8335            ast.try_expr(rhs).unwrap().kind,
8336            crate::ExprKind::Identifier(_)
8337        ));
8338    }
8339
8340    #[test]
8341    fn equality_inside_logical() {
8342        let (ast, diags) = parse_str("a === b && c === d;");
8343        assert!(diags.is_empty());
8344        let outer = expr_of_single_stmt(&ast);
8345        let (op, lhs, rhs) = binary(outer);
8346        assert_eq!(op, crate::BinOp::And);
8347        let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
8348        assert_eq!(lop, crate::BinOp::Eq);
8349        let (rop, ..) = binary(ast.try_expr(rhs).unwrap());
8350        assert_eq!(rop, crate::BinOp::Eq);
8351    }
8352
8353    #[test]
8354    fn double_equal_is_same_variant_as_triple() {
8355        let (ast, _) = parse_str("a == b;");
8356        let outer = expr_of_single_stmt(&ast);
8357        let (op, ..) = binary(outer);
8358        assert_eq!(op, crate::BinOp::Eq);
8359    }
8360
8361    #[test]
8362    fn bang_eq_is_not_eq() {
8363        let (ast, _) = parse_str("a != b;");
8364        let outer = expr_of_single_stmt(&ast);
8365        let (op, ..) = binary(outer);
8366        assert_eq!(op, crate::BinOp::NotEq);
8367    }
8368
8369    #[test]
8370    fn comparison_has_higher_prec_than_logical() {
8371        let (ast, _) = parse_str("a < b && c;");
8372        let outer = expr_of_single_stmt(&ast);
8373        let (op, lhs, _) = binary(outer);
8374        assert_eq!(op, crate::BinOp::And);
8375        let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
8376        assert_eq!(lop, crate::BinOp::Lt);
8377    }
8378
8379    #[test]
8380    fn full_precedence_chain_snapshot() {
8381        let (ast, diags) = parse_str("a || b && c === d < e + f * g;");
8382        assert!(diags.is_empty());
8383        insta::assert_debug_snapshot!(ast);
8384    }
8385
8386    #[test]
8387    fn paren_overrides_precedence() {
8388        let (ast, _) = parse_str("(a + b) * c;");
8389        let outer = expr_of_single_stmt(&ast);
8390        let (op, lhs, rhs) = binary(outer);
8391        assert_eq!(op, crate::BinOp::Mul);
8392        let crate::ExprKind::Paren(inner_id) = ast.try_expr(lhs).unwrap().kind else {
8393            panic!("expected Paren on lhs");
8394        };
8395        let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
8396        assert_eq!(inner_op, crate::BinOp::Add);
8397        assert!(matches!(
8398            ast.try_expr(rhs).unwrap().kind,
8399            crate::ExprKind::Identifier(_)
8400        ));
8401    }
8402
8403    #[test]
8404    fn binary_missing_rhs_diagnoses() {
8405        let (ast, diags) = parse_str("a +;");
8406        assert_eq!(diags.len(), 1);
8407        assert_eq!(diags[0].message, "expected expression");
8408        assert!(ast.top_level.is_empty());
8409    }
8410
8411    fn unary(e: &crate::Expr) -> (crate::UnOp, crate::ExprId) {
8412        match e.kind {
8413            crate::ExprKind::Unary { op, operand } => (op, operand),
8414            _ => panic!("expected Unary, got {:?}", e.kind),
8415        }
8416    }
8417
8418    #[test]
8419    fn parse_unary_not() {
8420        let (ast, _) = parse_str("!x;");
8421        let outer = expr_of_single_stmt(&ast);
8422        let (op, operand) = unary(outer);
8423        assert_eq!(op, crate::UnOp::Not);
8424        assert!(matches!(
8425            ast.try_expr(operand).unwrap().kind,
8426            crate::ExprKind::Identifier(_)
8427        ));
8428        assert_eq!(outer.span, crate::Span::new(F, 0, 2).unwrap());
8429    }
8430
8431    #[test]
8432    fn parse_unary_neg() {
8433        let (ast, _) = parse_str("-x;");
8434        let (op, _) = unary(expr_of_single_stmt(&ast));
8435        assert_eq!(op, crate::UnOp::Neg);
8436    }
8437
8438    #[test]
8439    fn parse_unary_pos() {
8440        let (ast, _) = parse_str("+x;");
8441        let (op, _) = unary(expr_of_single_stmt(&ast));
8442        assert_eq!(op, crate::UnOp::Pos);
8443    }
8444
8445    #[test]
8446    fn repeated_unary_not() {
8447        let (ast, _) = parse_str("!!x;");
8448        let outer = expr_of_single_stmt(&ast);
8449        let (op, operand) = unary(outer);
8450        assert_eq!(op, crate::UnOp::Not);
8451        let (inner_op, inner_operand) = unary(ast.try_expr(operand).unwrap());
8452        assert_eq!(inner_op, crate::UnOp::Not);
8453        assert!(matches!(
8454            ast.try_expr(inner_operand).unwrap().kind,
8455            crate::ExprKind::Identifier(_)
8456        ));
8457    }
8458
8459    #[test]
8460    fn unary_binds_tighter_than_binary() {
8461        let (ast, _) = parse_str("-x + 1;");
8462        let outer = expr_of_single_stmt(&ast);
8463        let (op, lhs, rhs) = binary(outer);
8464        assert_eq!(op, crate::BinOp::Add);
8465        let (unop, _) = unary(ast.try_expr(lhs).unwrap());
8466        assert_eq!(unop, crate::UnOp::Neg);
8467        assert_eq!(
8468            ast.try_expr(rhs).unwrap().kind,
8469            crate::ExprKind::Number(1.0)
8470        );
8471    }
8472
8473    #[test]
8474    fn unary_over_paren() {
8475        let (ast, _) = parse_str("-(x + 1);");
8476        let outer = expr_of_single_stmt(&ast);
8477        let (op, operand) = unary(outer);
8478        assert_eq!(op, crate::UnOp::Neg);
8479        let crate::ExprKind::Paren(inner_id) = ast.try_expr(operand).unwrap().kind else {
8480            panic!("expected Paren");
8481        };
8482        let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
8483        assert_eq!(inner_op, crate::BinOp::Add);
8484    }
8485
8486    #[test]
8487    fn parse_is_as_expression_rejected() {
8488        let (_, diags) = parse_str("x is number;");
8489        assert!(!diags.is_empty(), "expected parse error for `x is T`");
8490    }
8491
8492    fn as_cast(e: &crate::Expr) -> (crate::ExprId, &crate::TypeAnnotation) {
8493        match &e.kind {
8494            crate::ExprKind::As { expr, ty } => (*expr, ty),
8495            other => panic!("expected ExprKind::As, got {other:?}"),
8496        }
8497    }
8498
8499    #[test]
8500    fn parse_instanceof_class() {
8501        let (ast, diags) = parse_str("x instanceof Foo;");
8502        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8503        let outer = expr_of_single_stmt(&ast);
8504        let crate::ExprKind::InstanceOf { value, ty } = &outer.kind else {
8505            panic!("expected ExprKind::InstanceOf, got {:?}", outer.kind);
8506        };
8507        assert!(matches!(
8508            ast.try_expr(*value).unwrap().kind,
8509            crate::ExprKind::Identifier(_)
8510        ));
8511        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8512    }
8513
8514    #[test]
8515    fn parse_instanceof_binds_tighter_than_logical_and() {
8516        // `a instanceof B && c` parses as `(a instanceof B) && c`.
8517        let (ast, diags) = parse_str("a instanceof B && c;");
8518        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8519        let outer = expr_of_single_stmt(&ast);
8520        let (op, lhs, rhs) = binary(outer);
8521        assert_eq!(op, crate::BinOp::And);
8522        assert!(matches!(
8523            ast.try_expr(lhs).unwrap().kind,
8524            crate::ExprKind::InstanceOf { .. }
8525        ));
8526        assert!(matches!(
8527            ast.try_expr(rhs).unwrap().kind,
8528            crate::ExprKind::Identifier(_)
8529        ));
8530    }
8531
8532    /// `<T>x` produces the same node as `x as T`, so everything downstream of the
8533    /// parser sees one construct rather than two.
8534    #[test]
8535    fn parse_angle_cast_lowers_to_the_as_node() {
8536        let (ast, diags) = parse_str("<number>x;");
8537        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8538        let outer = expr_of_single_stmt(&ast);
8539        let (inner, ty) = as_cast(outer);
8540        assert!(matches!(
8541            ast.try_expr(inner).unwrap().kind,
8542            crate::ExprKind::Identifier(_)
8543        ));
8544        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8545    }
8546
8547    /// A `<` that means comparison always has a left operand, so it never reaches
8548    /// the cast branch. This is the regression the cast syntax could most easily
8549    /// have caused.
8550    #[test]
8551    fn a_less_than_comparison_is_not_read_as_a_cast() {
8552        let (ast, diags) = parse_str("a < b;");
8553        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8554        let e = expr_of_single_stmt(&ast);
8555        assert!(matches!(e.kind, crate::ExprKind::Binary { .. }));
8556    }
8557
8558    #[test]
8559    fn parse_typeof_in_type_position() {
8560        let (_ast, diags) = parse_str("let a: typeof b = 1;");
8561        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8562    }
8563
8564    #[test]
8565    fn parse_typeof_with_a_dotted_path() {
8566        let (_ast, diags) = parse_str("let a: typeof b.c.d = 1;");
8567        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8568    }
8569
8570    #[test]
8571    fn parse_as_primitive() {
8572        let (ast, diags) = parse_str("x as number;");
8573        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8574        let outer = expr_of_single_stmt(&ast);
8575        let (inner, ty) = as_cast(outer);
8576        assert!(matches!(
8577            ast.try_expr(inner).unwrap().kind,
8578            crate::ExprKind::Identifier(_)
8579        ));
8580        match &ty.kind {
8581            crate::TypeAnnotationKind::Name { args, .. } => {
8582                assert!(args.is_empty());
8583            }
8584            other => panic!("expected Name target, got {other:?}"),
8585        }
8586        assert_eq!(outer.span, crate::Span::new(F, 0, 11).unwrap());
8587    }
8588
8589    #[test]
8590    fn parse_as_object_literal_type() {
8591        let (ast, diags) = parse_str("x as { a: number };");
8592        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8593        let outer = expr_of_single_stmt(&ast);
8594        let (_, ty) = as_cast(outer);
8595        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Object { .. }));
8596    }
8597
8598    #[test]
8599    fn parse_as_array_short() {
8600        let (ast, diags) = parse_str("x as string[];");
8601        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8602        let outer = expr_of_single_stmt(&ast);
8603        let (_, ty) = as_cast(outer);
8604        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
8605    }
8606
8607    #[test]
8608    fn parse_as_array_generic() {
8609        let (ast, diags) = parse_str("x as T[];");
8610        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8611        let outer = expr_of_single_stmt(&ast);
8612        let (_, ty) = as_cast(outer);
8613        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
8614    }
8615
8616    #[test]
8617    fn parse_as_generic_instantiation() {
8618        let (ast, diags) = parse_str("x as Foo<Bar>;");
8619        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8620        let outer = expr_of_single_stmt(&ast);
8621        let (_, ty) = as_cast(outer);
8622        match &ty.kind {
8623            crate::TypeAnnotationKind::Name { args, .. } => {
8624                assert_eq!(args.len(), 1, "Foo<Bar> has one type arg");
8625            }
8626            other => panic!("expected Name with type args, got {other:?}"),
8627        }
8628    }
8629
8630    #[test]
8631    fn parse_as_precedence_higher_than_additive() {
8632        let (ast, diags) = parse_str("x as number + 1;");
8633        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8634        let outer = expr_of_single_stmt(&ast);
8635        let (op, lhs, rhs) = binary(outer);
8636        assert_eq!(op, crate::BinOp::Add);
8637        assert!(matches!(
8638            ast.try_expr(lhs).unwrap().kind,
8639            crate::ExprKind::As { .. }
8640        ));
8641        assert!(matches!(
8642            ast.try_expr(rhs).unwrap().kind,
8643            crate::ExprKind::Number(_)
8644        ));
8645    }
8646
8647    #[test]
8648    fn parse_as_precedence_lower_than_unary() {
8649        let (ast, diags) = parse_str("!x as boolean;");
8650        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8651        let outer = expr_of_single_stmt(&ast);
8652        let (inner, _) = as_cast(outer);
8653        assert!(matches!(
8654            ast.try_expr(inner).unwrap().kind,
8655            crate::ExprKind::Unary {
8656                op: crate::UnOp::Not,
8657                ..
8658            }
8659        ));
8660    }
8661
8662    #[test]
8663    fn parse_as_left_associative_chain() {
8664        let (ast, diags) = parse_str("x as A as B;");
8665        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8666        let outer = expr_of_single_stmt(&ast);
8667        let (inner, _outer_ty) = as_cast(outer);
8668        let (innermost, _inner_ty) = as_cast(ast.try_expr(inner).unwrap());
8669        assert!(matches!(
8670            ast.try_expr(innermost).unwrap().kind,
8671            crate::ExprKind::Identifier(_)
8672        ));
8673    }
8674
8675    #[test]
8676    fn parse_as_inside_call_arg() {
8677        let (ast, diags) = parse_str("f(x as T);");
8678        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8679        let outer = expr_of_single_stmt(&ast);
8680        match &outer.kind {
8681            crate::ExprKind::Call { args, .. } => {
8682                assert_eq!(args.len(), 1);
8683                assert!(matches!(
8684                    ast.try_expr(args[0]).unwrap().kind,
8685                    crate::ExprKind::As { .. }
8686                ));
8687            }
8688            other => panic!("expected Call, got {other:?}"),
8689        }
8690    }
8691
8692    #[test]
8693    fn parse_as_after_field_access() {
8694        let (ast, diags) = parse_str("obj.field as T;");
8695        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8696        let outer = expr_of_single_stmt(&ast);
8697        let (inner, _) = as_cast(outer);
8698        assert!(matches!(
8699            ast.try_expr(inner).unwrap().kind,
8700            crate::ExprKind::FieldAccess { .. }
8701        ));
8702    }
8703
8704    #[test]
8705    fn parse_type_guard_return_annotation_on_function() {
8706        let src = "function isCircle(s: Shape): s is Circle { return true; }";
8707        let (ast, diags) = parse_str(src);
8708        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8709        let stmt = single_stmt(&ast);
8710        match stmt.kind {
8711            crate::StmtKind::Function {
8712                ref return_type,
8713                ref type_predicate,
8714                ..
8715            } => {
8716                assert!(
8717                    return_type.is_none(),
8718                    "predicate form leaves return_type empty",
8719                );
8720                let tp = type_predicate.as_ref().expect("predicate parsed");
8721                assert_eq!(tp.param.name, "s");
8722                assert!(matches!(
8723                    tp.asserted.kind,
8724                    crate::TypeAnnotationKind::Name { .. }
8725                ));
8726                assert_eq!(tp.span, crate::Span::new(F, 29, 40).unwrap());
8727            }
8728            _ => panic!("expected Function"),
8729        }
8730    }
8731
8732    #[test]
8733    fn parse_type_guard_return_annotation_on_arrow() {
8734        let src = "const f = (s: Shape): s is Circle => true;";
8735        let (ast, diags) = parse_str(src);
8736        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8737        let value = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
8738            crate::StmtKind::Const { value, .. } => *value,
8739            _ => panic!("expected const"),
8740        };
8741        match &ast.try_expr(value).unwrap().kind {
8742            crate::ExprKind::Arrow {
8743                return_type,
8744                type_predicate,
8745                ..
8746            } => {
8747                assert!(return_type.is_none());
8748                let tp = type_predicate.as_ref().expect("predicate parsed");
8749                assert_eq!(tp.param.name, "s");
8750            }
8751            other => panic!("expected Arrow, got {other:?}"),
8752        }
8753    }
8754
8755    #[test]
8756    fn parse_plain_return_type_unaffected_by_predicate_parser() {
8757        let src = "function f(s: Shape): boolean { return true; }";
8758        let (ast, diags) = parse_str(src);
8759        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8760        let stmt = single_stmt(&ast);
8761        match stmt.kind {
8762            crate::StmtKind::Function {
8763                ref return_type,
8764                ref type_predicate,
8765                ..
8766            } => {
8767                assert!(return_type.is_some(), "plain form has return_type");
8768                assert!(type_predicate.is_none(), "plain form has no type_predicate",);
8769            }
8770            _ => panic!("expected Function"),
8771        }
8772    }
8773
8774    #[test]
8775    fn parse_type_guard_rejects_non_return_position() {
8776        let (_, diags) = parse_str("let x: y is Foo = null;");
8777        assert!(
8778            !diags.is_empty(),
8779            "expected parse error for `is` outside return-type position",
8780        );
8781    }
8782
8783    #[test]
8784    fn parse_typeof_prefix() {
8785        let (ast, diags) = parse_str("typeof x;");
8786        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8787        let outer = expr_of_single_stmt(&ast);
8788        let crate::ExprKind::Typeof { operand } = outer.kind else {
8789            panic!("expected Typeof, got {:?}", outer.kind);
8790        };
8791        assert!(matches!(
8792            ast.try_expr(operand).unwrap().kind,
8793            crate::ExprKind::Identifier(_)
8794        ));
8795        assert_eq!(outer.span, crate::Span::new(F, 0, 8).unwrap());
8796    }
8797
8798    #[test]
8799    fn parse_typeof_in_equality() {
8800        let (ast, diags) = parse_str(r#"typeof x === "number";"#);
8801        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8802        let outer = expr_of_single_stmt(&ast);
8803        let (op, lhs, rhs) = binary(outer);
8804        assert_eq!(op, crate::BinOp::Eq);
8805        assert!(matches!(
8806            ast.try_expr(lhs).unwrap().kind,
8807            crate::ExprKind::Typeof { .. }
8808        ));
8809        assert!(matches!(
8810            ast.try_expr(rhs).unwrap().kind,
8811            crate::ExprKind::String(ref s) if s == "number"
8812        ));
8813    }
8814
8815    #[test]
8816    fn parse_let_without_type() {
8817        let (ast, diags) = parse_str("let x = 1;");
8818        assert!(diags.is_empty());
8819        let stmt = single_stmt(&ast);
8820        assert_eq!(stmt.span, crate::Span::new(F, 0, 10).unwrap());
8821        match stmt.kind {
8822            crate::StmtKind::Let {
8823                ref name,
8824                ref ty,
8825                value,
8826                ..
8827            } => {
8828                assert_eq!(name.name, "x");
8829                assert_eq!(name.span, crate::Span::new(F, 4, 5).unwrap());
8830                assert!(ty.is_none());
8831                assert_eq!(
8832                    ast.try_expr(value).unwrap().kind,
8833                    crate::ExprKind::Number(1.0)
8834                );
8835            }
8836            _ => panic!("expected Let"),
8837        }
8838    }
8839
8840    #[test]
8841    fn parse_const_with_type_and_string() {
8842        let (ast, diags) = parse_str(r#"const y: string = "hi";"#);
8843        assert!(diags.is_empty());
8844        let stmt = single_stmt(&ast);
8845        match stmt.kind {
8846            crate::StmtKind::Const {
8847                ref name,
8848                ref ty,
8849                value,
8850                ..
8851            } => {
8852                assert_eq!(name.name, "y");
8853                assert_eq!(name.span, crate::Span::new(F, 6, 7).unwrap());
8854                let ty = ty.as_ref().expect("expected type annotation");
8855                assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8856                assert_eq!(ty.span, crate::Span::new(F, 9, 15).unwrap());
8857                assert_eq!(
8858                    ast.try_expr(value).unwrap().kind,
8859                    crate::ExprKind::String("hi".to_string())
8860                );
8861            }
8862            _ => panic!("expected Const"),
8863        }
8864    }
8865
8866    #[test]
8867    fn parse_let_with_type_and_binary_initializer() {
8868        let (ast, diags) = parse_str("let z: number = 1 + 2;");
8869        assert!(diags.is_empty());
8870        let stmt = single_stmt(&ast);
8871        match stmt.kind {
8872            crate::StmtKind::Let { ref ty, value, .. } => {
8873                assert!(ty.is_some());
8874                let (op, ..) = binary(ast.try_expr(value).unwrap());
8875                assert_eq!(op, crate::BinOp::Add);
8876            }
8877            _ => panic!("expected Let"),
8878        }
8879    }
8880
8881    #[test]
8882    fn void_type_annotation_is_accepted_syntactically() {
8883        let (ast, diags) = parse_str("let v: void = null;");
8884        assert!(diags.is_empty());
8885        let stmt = single_stmt(&ast);
8886        match stmt.kind {
8887            crate::StmtKind::Let { ref ty, .. } => {
8888                let ty = ty.as_ref().unwrap();
8889                assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8890                assert_eq!(ty.span, crate::Span::new(F, 7, 11).unwrap());
8891            }
8892            _ => panic!("expected Let"),
8893        }
8894    }
8895
8896    #[test]
8897    fn const_missing_initializer_diagnoses() {
8898        let (ast, diags) = parse_str("const x;");
8899        assert_eq!(diags.len(), 1);
8900        assert_eq!(
8901            diags[0].message,
8902            "`const` declaration requires an initializer"
8903        );
8904        assert!(ast.top_level.is_empty());
8905    }
8906
8907    #[test]
8908    fn parse_const_object_shorthand_pattern() {
8909        let (ast, diags) = parse_str("const { a, b } = obj;");
8910        assert!(diags.is_empty(), "unexpected: {diags:?}");
8911        let stmt = single_stmt(&ast);
8912        match stmt.kind {
8913            crate::StmtKind::ConstPattern {
8914                binding:
8915                    crate::Binding::Object {
8916                        ref fields,
8917                        ref rest,
8918                        ..
8919                    },
8920                ..
8921            } => {
8922                assert_eq!(fields.len(), 2);
8923                assert_eq!(fields[0].source.name, "a");
8924                assert_eq!(fields[0].local.name, "a");
8925                assert_eq!(fields[1].source.name, "b");
8926                assert_eq!(fields[1].local.name, "b");
8927                assert!(rest.is_none());
8928            }
8929            _ => panic!("expected ConstPattern Object, got {:?}", stmt.kind),
8930        }
8931    }
8932
8933    #[test]
8934    fn parse_const_object_pattern_keyword_source_key() {
8935        let (ast, diags) = parse_str("const { type: statusType, default: fallback } = obj;");
8936        assert!(diags.is_empty(), "unexpected: {diags:?}");
8937        let stmt = single_stmt(&ast);
8938        let crate::StmtKind::ConstPattern {
8939            binding: crate::Binding::Object { ref fields, .. },
8940            ..
8941        } = stmt.kind
8942        else {
8943            panic!("expected object const pattern");
8944        };
8945
8946        let names: Vec<(&str, &str)> = fields
8947            .iter()
8948            .map(|f| (f.source.name.as_str(), f.local.name.as_str()))
8949            .collect();
8950        assert_eq!(names, vec![("type", "statusType"), ("default", "fallback")]);
8951    }
8952
8953    #[test]
8954    fn parse_const_object_pattern_keyword_shorthand_rejected() {
8955        let (_ast, diags) = parse_str("const { default } = obj;");
8956        assert!(
8957            diags
8958                .iter()
8959                .any(|d| d.message == "expected `:` after keyword field name in object pattern"),
8960            "expected keyword-shorthand diagnostic, got: {diags:?}"
8961        );
8962    }
8963
8964    #[test]
8965    fn parse_const_object_renamed_pattern() {
8966        let (ast, diags) = parse_str("const { a: x, b: y } = obj;");
8967        assert!(diags.is_empty(), "unexpected: {diags:?}");
8968        match single_stmt(&ast).kind {
8969            crate::StmtKind::ConstPattern {
8970                binding: crate::Binding::Object { ref fields, .. },
8971                ..
8972            } => {
8973                assert_eq!(fields[0].source.name, "a");
8974                assert_eq!(fields[0].local.name, "x");
8975                assert_eq!(fields[1].source.name, "b");
8976                assert_eq!(fields[1].local.name, "y");
8977            }
8978            ref other => panic!("expected ConstPattern Object, got {other:?}"),
8979        }
8980    }
8981
8982    #[test]
8983    fn parse_const_object_with_rest() {
8984        let (ast, diags) = parse_str("const { a, ...rest } = obj;");
8985        assert!(diags.is_empty(), "unexpected: {diags:?}");
8986        match single_stmt(&ast).kind {
8987            crate::StmtKind::ConstPattern {
8988                binding:
8989                    crate::Binding::Object {
8990                        ref fields,
8991                        ref rest,
8992                        ..
8993                    },
8994                ..
8995            } => {
8996                assert_eq!(fields.len(), 1);
8997                assert_eq!(fields[0].source.name, "a");
8998                let r = rest.as_ref().expect("expected rest");
8999                assert_eq!(r.name, "rest");
9000            }
9001            ref other => panic!("expected ConstPattern Object, got {other:?}"),
9002        }
9003    }
9004
9005    #[test]
9006    fn parse_const_array_pattern() {
9007        let (ast, diags) = parse_str("const [x, y] = arr;");
9008        assert!(diags.is_empty(), "unexpected: {diags:?}");
9009        match single_stmt(&ast).kind {
9010            crate::StmtKind::ConstPattern {
9011                binding:
9012                    crate::Binding::Array {
9013                        ref elems,
9014                        ref rest,
9015                        ..
9016                    },
9017                ..
9018            } => {
9019                assert_eq!(elems.len(), 2);
9020                assert_eq!(elems[0].as_ref().unwrap().name, "x");
9021                assert_eq!(elems[1].as_ref().unwrap().name, "y");
9022                assert!(rest.is_none());
9023            }
9024            ref other => panic!("expected ConstPattern Array, got {other:?}"),
9025        }
9026    }
9027
9028    #[test]
9029    fn parse_const_array_with_hole_and_rest() {
9030        let (ast, diags) = parse_str("const [, x, ...rest] = arr;");
9031        assert!(diags.is_empty(), "unexpected: {diags:?}");
9032        match single_stmt(&ast).kind {
9033            crate::StmtKind::ConstPattern {
9034                binding:
9035                    crate::Binding::Array {
9036                        ref elems,
9037                        ref rest,
9038                        ..
9039                    },
9040                ..
9041            } => {
9042                assert_eq!(elems.len(), 2);
9043                assert!(elems[0].is_none(), "first slot should be a hole");
9044                assert_eq!(elems[1].as_ref().unwrap().name, "x");
9045                assert_eq!(rest.as_ref().unwrap().name, "rest");
9046            }
9047            ref other => panic!("expected ConstPattern Array, got {other:?}"),
9048        }
9049    }
9050
9051    #[test]
9052    fn parse_let_object_pattern() {
9053        let (ast, diags) = parse_str("let { a, b } = obj;");
9054        assert!(diags.is_empty(), "unexpected: {diags:?}");
9055        assert!(matches!(
9056            single_stmt(&ast).kind,
9057            crate::StmtKind::LetPattern {
9058                binding: crate::Binding::Object { .. },
9059                ..
9060            }
9061        ));
9062    }
9063
9064    #[test]
9065    fn parse_function_param_object_pattern() {
9066        let src = "function f({ a, b }: T): void { return; }";
9067        let (ast, diags) = parse_str(src);
9068        assert!(diags.is_empty(), "unexpected: {diags:?}");
9069        match single_stmt(&ast).kind {
9070            crate::StmtKind::Function { ref params, .. } => {
9071                assert_eq!(params.len(), 1);
9072                let p = &params[0];
9073                assert!(p.pattern.is_some(), "expected pattern param");
9074                assert_eq!(p.name.name, "", "pattern placeholder uses empty name");
9075                match p.pattern.as_ref().unwrap() {
9076                    crate::Binding::Object { fields, .. } => {
9077                        assert_eq!(fields.len(), 2);
9078                        assert_eq!(fields[0].source.name, "a");
9079                    }
9080                    other => panic!("expected Object binding, got {other:?}"),
9081                }
9082            }
9083            ref other => panic!("expected Function, got {other:?}"),
9084        }
9085    }
9086
9087    #[test]
9088    fn parse_arrow_param_array_pattern() {
9089        let src = "const g = ([x, y]: number[]): number => x + y;";
9090        let (_ast, diags) = parse_str(src);
9091        assert!(diags.is_empty(), "unexpected: {diags:?}");
9092    }
9093
9094    #[test]
9095    fn parse_destructure_nested_rejected() {
9096        let (_ast, diags) = parse_str("const { a: { b } } = obj;");
9097        assert!(
9098            diags
9099                .iter()
9100                .any(|d| d.message.contains("nested destructuring")),
9101            "expected nested-destructuring diagnostic, got: {diags:?}",
9102        );
9103    }
9104
9105    #[test]
9106    fn parse_destructure_array_nested_rejected() {
9107        let (_ast, diags) = parse_str("const [[a]] = arr;");
9108        assert!(
9109            diags
9110                .iter()
9111                .any(|d| d.message.contains("nested destructuring")),
9112            "expected nested-destructuring diagnostic, got: {diags:?}",
9113        );
9114    }
9115
9116    #[test]
9117    fn parse_destructure_pattern_defaults() {
9118        let (ast, diags) = parse_str("const { a = 1, b: c = 2 } = obj;");
9119        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9120        let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
9121            panic!("expected pattern")
9122        };
9123        let crate::Binding::Object { fields, .. } = binding else {
9124            panic!("expected object")
9125        };
9126        assert_eq!(fields.len(), 2);
9127        assert!(fields.iter().all(|field| field.default.is_some()));
9128        assert_eq!(fields[1].local.name, "c");
9129    }
9130
9131    #[test]
9132    fn parse_destructure_param_default() {
9133        let (ast, diags) = parse_str("function f({ a = 1 }: T = obj): void { return; }");
9134        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9135        let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
9136            panic!("expected function")
9137        };
9138        assert!(params[0].default.is_some());
9139        assert!(params[0].pattern.is_some());
9140    }
9141
9142    #[test]
9143    fn parse_destructure_rest_not_last_rejected() {
9144        let (_ast, diags) = parse_str("const { ...rest, a } = obj;");
9145        assert!(
9146            diags
9147                .iter()
9148                .any(|d| d.message.contains("rest element must be the last element")),
9149            "expected rest-not-last diagnostic, got: {diags:?}",
9150        );
9151    }
9152
9153    #[test]
9154    fn parse_destructure_empty_object_rejected() {
9155        let (_ast, diags) = parse_str("const { } = obj;");
9156        assert!(
9157            diags
9158                .iter()
9159                .any(|d| d.message.contains("empty object destructuring pattern")),
9160            "expected empty-pattern diagnostic, got: {diags:?}",
9161        );
9162    }
9163
9164    #[test]
9165    fn parse_ternary_simple() {
9166        let (ast, diags) = parse_str("const x: number = a ? 1 : 2;");
9167        assert!(diags.is_empty(), "unexpected: {diags:?}");
9168        let stmt = single_stmt(&ast);
9169        let crate::StmtKind::Const { value, .. } = stmt.kind else {
9170            panic!("expected Const, got {:?}", stmt.kind);
9171        };
9172        assert!(matches!(
9173            ast.try_expr(value).unwrap().kind,
9174            crate::ExprKind::Ternary { .. }
9175        ));
9176    }
9177
9178    #[test]
9179    fn parse_ternary_nested_right_assoc() {
9180        let (ast, diags) = parse_str("const x: number = a ? 1 : b ? 2 : 3;");
9181        assert!(diags.is_empty(), "unexpected: {diags:?}");
9182        let stmt = single_stmt(&ast);
9183        let crate::StmtKind::Const { value, .. } = stmt.kind else {
9184            panic!("expected Const");
9185        };
9186        let crate::ExprKind::Ternary { else_, .. } = ast.try_expr(value).unwrap().kind else {
9187            panic!("expected outer Ternary");
9188        };
9189        assert!(matches!(
9190            ast.try_expr(else_).unwrap().kind,
9191            crate::ExprKind::Ternary { .. }
9192        ));
9193    }
9194
9195    #[test]
9196    fn parse_ternary_missing_colon_diagnoses() {
9197        let (_, diags) = parse_str("const x: number = a ? 1;");
9198        assert!(
9199            diags
9200                .iter()
9201                .any(|d| d.message.contains("expected `:` to complete ternary")),
9202            "expected colon diagnostic, got: {diags:?}",
9203        );
9204    }
9205
9206    #[test]
9207    fn parse_nullish_coalesce_binop() {
9208        let (ast, diags) = parse_str("const x: number = a ?? 1;");
9209        assert!(diags.is_empty(), "unexpected: {diags:?}");
9210        let stmt = single_stmt(&ast);
9211        let crate::StmtKind::Const { value, .. } = stmt.kind else {
9212            panic!("expected Const");
9213        };
9214        match ast.try_expr(value).unwrap().kind {
9215            crate::ExprKind::Binary { op, .. } => {
9216                assert_eq!(op, crate::BinOp::NullishCoalesce);
9217            }
9218            ref other => panic!("expected Binary NullishCoalesce, got {other:?}"),
9219        }
9220    }
9221
9222    #[test]
9223    fn parse_nullish_left_associative() {
9224        let (ast, diags) = parse_str("const x: number = a ?? b ?? c;");
9225        assert!(diags.is_empty(), "unexpected: {diags:?}");
9226        let stmt = single_stmt(&ast);
9227        let crate::StmtKind::Const { value, .. } = stmt.kind else {
9228            panic!("expected Const");
9229        };
9230        let crate::ExprKind::Binary { lhs, .. } = ast.try_expr(value).unwrap().kind else {
9231            panic!("expected outer Binary");
9232        };
9233        match ast.try_expr(lhs).unwrap().kind {
9234            crate::ExprKind::Binary { op, .. } => {
9235                assert_eq!(op, crate::BinOp::NullishCoalesce);
9236            }
9237            ref other => panic!("expected inner Binary, got {other:?}"),
9238        }
9239    }
9240
9241    #[test]
9242    fn parse_pow_is_right_associative() {
9243        let (ast, diags) = parse_str("const x: number = 2 ** 3 ** 2;");
9244        assert!(diags.is_empty(), "unexpected: {diags:?}");
9245        let stmt = single_stmt(&ast);
9246        let crate::StmtKind::Const { value, .. } = stmt.kind else {
9247            panic!("expected Const");
9248        };
9249        let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
9250            panic!("expected outer Binary");
9251        };
9252        assert_eq!(op, crate::BinOp::Pow);
9253        match ast.try_expr(rhs).unwrap().kind {
9254            crate::ExprKind::Binary { op: inner_op, .. } => {
9255                assert_eq!(inner_op, crate::BinOp::Pow);
9256            }
9257            ref other => panic!("expected inner Pow Binary on RHS, got {other:?}"),
9258        }
9259    }
9260
9261    #[test]
9262    fn parse_pow_higher_than_multiply() {
9263        let (ast, diags) = parse_str("const x: number = 2 * 3 ** 2;");
9264        assert!(diags.is_empty(), "unexpected: {diags:?}");
9265        let stmt = single_stmt(&ast);
9266        let crate::StmtKind::Const { value, .. } = stmt.kind else {
9267            panic!("expected Const");
9268        };
9269        let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
9270            panic!("expected outer Binary");
9271        };
9272        assert_eq!(op, crate::BinOp::Mul);
9273        match ast.try_expr(rhs).unwrap().kind {
9274            crate::ExprKind::Binary { op: inner_op, .. } => {
9275                assert_eq!(inner_op, crate::BinOp::Pow);
9276            }
9277            ref other => panic!("expected Pow on the RHS of *, got {other:?}"),
9278        }
9279    }
9280
9281    #[test]
9282    fn parse_compound_assign_ident() {
9283        let (ast, diags) = parse_str("function main(): void { let x: number = 0; x += 5; }");
9284        assert!(diags.is_empty(), "unexpected: {diags:?}");
9285        let func = ast.top_level.iter().find_map(|sid| {
9286            if let crate::StmtKind::Function { body, .. } = &ast.try_stmt(*sid).unwrap().kind {
9287                Some(*body)
9288            } else {
9289                None
9290            }
9291        });
9292        let crate::StmtKind::Block(stmts) = &ast.try_stmt(func.expect("main")).unwrap().kind else {
9293            panic!("expected function body to be a block");
9294        };
9295        let target = stmts
9296            .iter()
9297            .find_map(|sid| match &ast.try_stmt(*sid).unwrap().kind {
9298                crate::StmtKind::CompoundAssign { target, op, .. } => {
9299                    Some((target.name.clone(), *op))
9300                }
9301                _ => None,
9302            });
9303        let (name, op) = target.expect("expected a CompoundAssign in the body");
9304        assert_eq!(name, "x");
9305        assert_eq!(op, crate::BinOp::Add);
9306    }
9307
9308    #[test]
9309    fn parse_nullish_mixed_with_or_rejected() {
9310        let (_, diags) = parse_str("const x: number = a || b ?? c;");
9311        assert!(
9312            diags
9313                .iter()
9314                .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
9315            "expected mixing diagnostic, got: {diags:?}",
9316        );
9317    }
9318
9319    #[test]
9320    fn parse_nullish_mixed_with_and_rejected() {
9321        let (_, diags) = parse_str("const x: number = a && b ?? c;");
9322        assert!(
9323            diags
9324                .iter()
9325                .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
9326            "expected mixing diagnostic, got: {diags:?}",
9327        );
9328    }
9329
9330    #[test]
9331    fn parse_nullish_followed_by_logical_rejected() {
9332        for (source, help) in [
9333            (
9334                "const x: number = a ?? b || c;",
9335                "`(a ?? b) || c` or `a ?? (b || c)`",
9336            ),
9337            (
9338                "const x: number = a ?? b && c;",
9339                "`(a ?? b) && c` or `a ?? (b && c)`",
9340            ),
9341            (
9342                "const x: number = a ?? b ?? c || d;",
9343                "`(a ?? b) || c` or `a ?? (b || c)`",
9344            ),
9345            // One error per chain, however many mixes it has.
9346            (
9347                "const x: number = a || b ?? c || d;",
9348                "`(a || b) ?? c` or `a || (b ?? c)`",
9349            ),
9350        ] {
9351            let (_, diags) = parse_str(source);
9352            assert_eq!(diags.len(), 1, "diags for {source:?}: {diags:?}");
9353            assert!(
9354                diags[0].message.contains("mixing `??` with `||` / `&&`"),
9355                "message for {source:?}: {diags:?}",
9356            );
9357            assert!(
9358                diags[0].help.iter().any(|h| h.contains(help)),
9359                "help for {source:?}: {diags:?}",
9360            );
9361        }
9362    }
9363
9364    #[test]
9365    fn parse_nullish_operand_keeps_tighter_operators() {
9366        // `a ?? (b == c)`: only `||` and `&&` are excluded from a `??` operand.
9367        let (ast, diags) = parse_str("const x: boolean = a ?? b == c;");
9368        assert!(diags.is_empty(), "unexpected: {diags:?}");
9369        let stmt = single_stmt(&ast);
9370        let crate::StmtKind::Const { value, .. } = stmt.kind else {
9371            panic!("expected Const");
9372        };
9373        let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
9374            panic!("expected outer Binary");
9375        };
9376        assert_eq!(op, crate::BinOp::NullishCoalesce);
9377        assert!(matches!(
9378            ast.try_expr(rhs).unwrap().kind,
9379            crate::ExprKind::Binary {
9380                op: crate::BinOp::Eq,
9381                ..
9382            }
9383        ));
9384    }
9385
9386    #[test]
9387    fn parse_nullish_with_parens_ok() {
9388        let (_, diags) = parse_str("const x: number = (a || b) ?? c;");
9389        assert!(diags.is_empty(), "unexpected: {diags:?}");
9390    }
9391
9392    #[test]
9393    fn parse_optional_field_chain() {
9394        let (ast, diags) = parse_str("const x: number = a?.b;");
9395        assert!(diags.is_empty(), "unexpected: {diags:?}");
9396        let stmt = single_stmt(&ast);
9397        let crate::StmtKind::Const { value, .. } = stmt.kind else {
9398            panic!("expected Const");
9399        };
9400        match ast.try_expr(value).unwrap().kind {
9401            crate::ExprKind::OptionalChain { ref parts, .. } => {
9402                assert_eq!(parts.len(), 1);
9403                match &parts[0] {
9404                    crate::ChainPart::Field { name, optional, .. } => {
9405                        assert_eq!(name.name, "b");
9406                        assert!(*optional);
9407                    }
9408                    other => panic!("expected Field, got {other:?}"),
9409                }
9410            }
9411            ref other => panic!("expected OptionalChain, got {other:?}"),
9412        }
9413    }
9414
9415    #[test]
9416    fn parse_optional_chain_continues_after_first_dot() {
9417        let (ast, diags) = parse_str("const x: number = a?.b.c;");
9418        assert!(diags.is_empty(), "unexpected: {diags:?}");
9419        let stmt = single_stmt(&ast);
9420        let crate::StmtKind::Const { value, .. } = stmt.kind else {
9421            panic!("expected Const");
9422        };
9423        match ast.try_expr(value).unwrap().kind {
9424            crate::ExprKind::OptionalChain { ref parts, .. } => {
9425                assert_eq!(parts.len(), 2);
9426                if let crate::ChainPart::Field { optional, .. } = &parts[0] {
9427                    assert!(*optional);
9428                }
9429                if let crate::ChainPart::Field { optional, .. } = &parts[1] {
9430                    assert!(!*optional);
9431                }
9432            }
9433            ref other => panic!("expected OptionalChain, got {other:?}"),
9434        }
9435    }
9436
9437    #[test]
9438    fn parse_optional_chain_multiple_short_circuits() {
9439        let (ast, diags) = parse_str("const x: number = a?.b?.c;");
9440        assert!(diags.is_empty(), "unexpected: {diags:?}");
9441        let stmt = single_stmt(&ast);
9442        let crate::StmtKind::Const { value, .. } = stmt.kind else {
9443            panic!("expected Const");
9444        };
9445        let crate::ExprKind::OptionalChain { parts, .. } = &ast.try_expr(value).unwrap().kind
9446        else {
9447            panic!("expected OptionalChain");
9448        };
9449        assert_eq!(parts.len(), 2);
9450        for p in parts {
9451            if let crate::ChainPart::Field { optional, .. } = p {
9452                assert!(*optional, "both parts should be optional");
9453            }
9454        }
9455    }
9456
9457    #[test]
9458    fn parse_optional_call_and_index() {
9459        let (_, diags) = parse_str("const x: number = f?.();");
9460        assert!(diags.is_empty(), "unexpected: {diags:?}");
9461        let (_, diags) = parse_str("const y: number = arr?.[0];");
9462        assert!(diags.is_empty(), "unexpected: {diags:?}");
9463    }
9464
9465    #[test]
9466    fn parse_optional_chain_terminates_at_questionmark_for_ternary() {
9467        let (ast, diags) = parse_str("const x: number = a?.b ? c : d;");
9468        assert!(diags.is_empty(), "unexpected: {diags:?}");
9469        let stmt = single_stmt(&ast);
9470        let crate::StmtKind::Const { value, .. } = stmt.kind else {
9471            panic!("expected Const");
9472        };
9473        let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
9474            panic!(
9475                "expected top-level Ternary, got {:?}",
9476                ast.try_expr(value).unwrap().kind
9477            );
9478        };
9479        assert!(matches!(
9480            ast.try_expr(cond).unwrap().kind,
9481            crate::ExprKind::OptionalChain { .. }
9482        ));
9483    }
9484
9485    #[test]
9486    fn parse_nullish_below_ternary() {
9487        let (ast, diags) = parse_str("const x: number = a ?? b ? c : d;");
9488        assert!(diags.is_empty(), "unexpected: {diags:?}");
9489        let stmt = single_stmt(&ast);
9490        let crate::StmtKind::Const { value, .. } = stmt.kind else {
9491            panic!("expected Const");
9492        };
9493        let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
9494            panic!("expected top-level Ternary");
9495        };
9496        match ast.try_expr(cond).unwrap().kind {
9497            crate::ExprKind::Binary { op, .. } => {
9498                assert_eq!(op, crate::BinOp::NullishCoalesce);
9499            }
9500            ref other => panic!("expected Binary ??, got {other:?}"),
9501        }
9502    }
9503
9504    #[test]
9505    fn let_missing_initializer_starts_undefined() {
9506        let (ast, diags) = parse_str("let x;");
9507        assert!(diags.is_empty());
9508        assert_eq!(ast.implicit_initializers.len(), 1);
9509        assert_eq!(ast.top_level.len(), 1);
9510    }
9511
9512    #[test]
9513    fn missing_identifier_after_let_diagnoses() {
9514        let (ast, diags) = parse_str("let = 1;");
9515        assert_eq!(diags.len(), 1);
9516        assert_eq!(diags[0].message, "expected identifier after `let`/`const`");
9517        assert!(ast.top_level.is_empty());
9518    }
9519
9520    #[test]
9521    fn missing_type_after_colon_diagnoses() {
9522        let (ast, diags) = parse_str("let x: = 1;");
9523        assert_eq!(diags.len(), 1);
9524        assert_eq!(diags[0].message, "expected type");
9525        assert!(ast.top_level.is_empty());
9526    }
9527
9528    #[test]
9529    fn missing_semicolon_after_declaration_diagnoses() {
9530        let (ast, diags) = parse_str("let x = 1 y;");
9531        assert_eq!(diags.len(), 1);
9532        assert_eq!(diags[0].message, "expected `;` after declaration");
9533        assert!(ast.top_level.is_empty());
9534    }
9535
9536    #[test]
9537    fn expression_statements_still_parse() {
9538        let (ast, diags) = parse_str("42;");
9539        assert!(diags.is_empty());
9540        assert_eq!(ast.top_level.len(), 1);
9541    }
9542
9543    #[test]
9544    fn snapshot_mixed_declarations() {
9545        let (ast, diags) =
9546            parse_str(r#"let x = 1; const y: string = "hi"; let z: number = 1 + 2;"#);
9547        assert!(diags.is_empty());
9548        assert_eq!(ast.top_level.len(), 3);
9549        insta::assert_debug_snapshot!(ast);
9550    }
9551
9552    #[test]
9553    fn parse_function_typical() {
9554        let (ast, diags) = parse_str("function f(a: number, b: string): boolean { }");
9555        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9556        let stmt = single_stmt(&ast);
9557        match stmt.kind {
9558            crate::StmtKind::Function {
9559                ref name,
9560                ref generics,
9561                ref params,
9562                ref return_type,
9563                body,
9564                ..
9565            } => {
9566                assert_eq!(name.name, "f");
9567                assert_eq!(name.span, crate::Span::new(F, 9, 10).unwrap());
9568                assert!(generics.is_empty());
9569                assert_eq!(params.len(), 2);
9570                assert_eq!(params[0].name.name, "a");
9571                assert_eq!(params[0].name.span, crate::Span::new(F, 11, 12).unwrap());
9572                let p0_ty = params[0]
9573                    .ty
9574                    .as_ref()
9575                    .expect("function-decl param requires annotation");
9576                assert!(matches!(p0_ty.kind, crate::TypeAnnotationKind::Name { .. }));
9577                assert_eq!(p0_ty.span, crate::Span::new(F, 14, 20).unwrap()); // `number`
9578                assert_eq!(params[1].name.name, "b");
9579                assert_eq!(params[1].name.span, crate::Span::new(F, 22, 23).unwrap());
9580                let p1_ty = params[1]
9581                    .ty
9582                    .as_ref()
9583                    .expect("function-decl param requires annotation");
9584                assert_eq!(p1_ty.span, crate::Span::new(F, 25, 31).unwrap()); // `string`
9585                let return_type = return_type.as_ref().expect("plain return type");
9586                assert!(matches!(
9587                    return_type.kind,
9588                    crate::TypeAnnotationKind::Name { .. }
9589                ));
9590                assert_eq!(return_type.span, crate::Span::new(F, 34, 41).unwrap()); // `boolean`
9591                let block = ast.try_stmt(body).unwrap();
9592                assert!(matches!(block.kind, crate::StmtKind::Block(ref v) if v.is_empty()));
9593            }
9594            _ => panic!("expected Function"),
9595        }
9596    }
9597
9598    #[test]
9599    fn parse_function_with_doc_comment() {
9600        let (ast, diags) = parse_str(
9601            "/**\n * Sum two numbers.\n * @param a First.\n * @param b Second.\n */\nfunction add(a: number, b: number): number { return a + b; }",
9602        );
9603        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9604        let stmt = single_stmt(&ast);
9605        match &stmt.kind {
9606            crate::StmtKind::Function { doc, .. } => {
9607                let d = doc.as_ref().expect("doc attached");
9608                assert_eq!(d.summary, "Sum two numbers.");
9609                assert_eq!(d.params.len(), 2);
9610                assert_eq!(d.params[0].name, "a");
9611                assert_eq!(d.params[1].name, "b");
9612            }
9613            _ => panic!("expected Function"),
9614        }
9615    }
9616
9617    #[test]
9618    fn parse_interface_member_docs() {
9619        let (ast, diags) = parse_str(
9620            "interface Foo {\n  /** First method. */ first(): void;\n  /** A field. */ field: number;\n}",
9621        );
9622        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9623        let stmt = single_stmt(&ast);
9624        match &stmt.kind {
9625            crate::StmtKind::InterfaceDecl { members, .. } => {
9626                assert_eq!(members.len(), 2);
9627                match &members[0] {
9628                    crate::InterfaceMember::Method { doc, .. } => {
9629                        assert_eq!(doc.as_ref().unwrap().summary, "First method.");
9630                    }
9631                    _ => panic!("expected Method first"),
9632                }
9633                match &members[1] {
9634                    crate::InterfaceMember::Property { doc, .. } => {
9635                        assert_eq!(doc.as_ref().unwrap().summary, "A field.");
9636                    }
9637                    _ => panic!("expected Property second"),
9638                }
9639            }
9640            _ => panic!("expected InterfaceDecl"),
9641        }
9642    }
9643
9644    #[test]
9645    fn parse_numeric_enum_all_implicit() {
9646        let (ast, diags) = parse_str("enum Direction { Up, Down, Left, Right }");
9647        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9648        let stmt = single_stmt(&ast);
9649        match &stmt.kind {
9650            crate::StmtKind::EnumDecl { name, members, doc } => {
9651                assert_eq!(name.name, "Direction");
9652                assert!(doc.is_none());
9653                assert_eq!(members.len(), 4);
9654                for (m, expected) in members.iter().zip(["Up", "Down", "Left", "Right"]) {
9655                    assert_eq!(m.name.name, expected);
9656                    assert!(m.value.is_none(), "expected no initializer on {expected}");
9657                }
9658            }
9659            _ => panic!("expected EnumDecl"),
9660        }
9661    }
9662
9663    #[test]
9664    fn parse_string_enum_all_explicit() {
9665        let (ast, diags) =
9666            parse_str("enum Status { Active = \"active\", Inactive = \"inactive\" }");
9667        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9668        let stmt = single_stmt(&ast);
9669        match &stmt.kind {
9670            crate::StmtKind::EnumDecl { name, members, .. } => {
9671                assert_eq!(name.name, "Status");
9672                assert_eq!(members.len(), 2);
9673                match &members[0].value {
9674                    Some(crate::EnumInitializer::String { value, .. }) => {
9675                        assert_eq!(value, "active");
9676                    }
9677                    other => panic!("expected String init, got {other:?}"),
9678                }
9679                match &members[1].value {
9680                    Some(crate::EnumInitializer::String { value, .. }) => {
9681                        assert_eq!(value, "inactive");
9682                    }
9683                    other => panic!("expected String init, got {other:?}"),
9684                }
9685            }
9686            _ => panic!("expected EnumDecl"),
9687        }
9688    }
9689
9690    #[test]
9691    fn parse_numeric_enum_with_negative_init() {
9692        let (ast, diags) = parse_str("enum D { Up = 1, Down = -1 }");
9693        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9694        let stmt = single_stmt(&ast);
9695        match &stmt.kind {
9696            crate::StmtKind::EnumDecl { members, .. } => {
9697                assert_eq!(members.len(), 2);
9698                match &members[0].value {
9699                    Some(crate::EnumInitializer::Number { value, .. }) => {
9700                        assert_eq!(*value, 1.0);
9701                    }
9702                    other => panic!("expected Number init, got {other:?}"),
9703                }
9704                match &members[1].value {
9705                    Some(crate::EnumInitializer::Number { value, .. }) => {
9706                        assert_eq!(*value, -1.0);
9707                    }
9708                    other => panic!("expected Number init, got {other:?}"),
9709                }
9710            }
9711            _ => panic!("expected EnumDecl"),
9712        }
9713    }
9714
9715    #[test]
9716    fn parse_enum_mixed_kinds_is_parse_error() {
9717        let (_ast, diags) = parse_str("enum Mix { A = 1, B = \"two\" }");
9718        assert!(
9719            diags
9720                .iter()
9721                .any(|d| d.message.contains("mixed numeric and string")),
9722            "expected mixed-kind diagnostic, got: {diags:?}"
9723        );
9724    }
9725
9726    #[test]
9727    fn parse_enum_member_docs() {
9728        let (ast, diags) = parse_str(
9729            "enum D {\n  /** The first one. */ A = 1,\n  /** The second one. */ B = 2,\n}",
9730        );
9731        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9732        let stmt = single_stmt(&ast);
9733        match &stmt.kind {
9734            crate::StmtKind::EnumDecl { members, .. } => {
9735                assert_eq!(members.len(), 2);
9736                assert_eq!(
9737                    members[0].doc.as_ref().expect("first doc").summary,
9738                    "The first one."
9739                );
9740                assert_eq!(
9741                    members[1].doc.as_ref().expect("second doc").summary,
9742                    "The second one."
9743                );
9744            }
9745            _ => panic!("expected EnumDecl"),
9746        }
9747    }
9748
9749    #[test]
9750    fn parse_enum_trailing_comma_ok() {
9751        let (ast, diags) = parse_str("enum D { A, B, }");
9752        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9753        let stmt = single_stmt(&ast);
9754        match &stmt.kind {
9755            crate::StmtKind::EnumDecl { members, .. } => assert_eq!(members.len(), 2),
9756            _ => panic!("expected EnumDecl"),
9757        }
9758    }
9759
9760    #[test]
9761    fn parse_function_empty_params() {
9762        let (ast, diags) = parse_str("function noop(): void { }");
9763        assert!(diags.is_empty());
9764        let stmt = single_stmt(&ast);
9765        match stmt.kind {
9766            crate::StmtKind::Function { ref params, .. } => assert!(params.is_empty()),
9767            _ => panic!("expected Function"),
9768        }
9769    }
9770
9771    #[test]
9772    fn parse_generic_function_single_param() {
9773        let (ast, diags) = parse_str("function identity<T>(x: T): T { return x; }");
9774        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9775        let stmt = single_stmt(&ast);
9776        match stmt.kind {
9777            crate::StmtKind::Function {
9778                ref name,
9779                ref generics,
9780                ref params,
9781                ..
9782            } => {
9783                assert_eq!(name.name, "identity");
9784                assert_eq!(generics.len(), 1);
9785                assert_eq!(generics[0].name, "T");
9786                assert_eq!(params.len(), 1);
9787                assert_eq!(params[0].name.name, "x");
9788            }
9789            _ => panic!("expected Function"),
9790        }
9791    }
9792
9793    #[test]
9794    fn parse_generic_function_multiple_params() {
9795        let (ast, diags) = parse_str("function pair<T, U>(a: T, b: U): T { return a; }");
9796        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9797        let stmt = single_stmt(&ast);
9798        match stmt.kind {
9799            crate::StmtKind::Function { ref generics, .. } => {
9800                assert_eq!(generics.len(), 2);
9801                assert_eq!(generics[0].name, "T");
9802                assert_eq!(generics[1].name, "U");
9803            }
9804            _ => panic!("expected Function"),
9805        }
9806    }
9807
9808    #[test]
9809    fn parse_non_generic_function_has_empty_generics() {
9810        let (ast, diags) = parse_str("function noop(): void { }");
9811        assert!(diags.is_empty());
9812        let stmt = single_stmt(&ast);
9813        match stmt.kind {
9814            crate::StmtKind::Function { ref generics, .. } => assert!(generics.is_empty()),
9815            _ => panic!("expected Function"),
9816        }
9817    }
9818
9819    #[test]
9820    fn parse_empty_generic_list_diagnoses() {
9821        let (_ast, diags) = parse_str("function f<>(): void { }");
9822        assert!(
9823            diags
9824                .iter()
9825                .any(|d| d.message.contains("empty generic parameter list")),
9826            "expected empty-generic-list diagnostic, got: {diags:?}",
9827        );
9828    }
9829
9830    #[test]
9831    fn parse_trailing_comma_in_generic_list_ok() {
9832        let (ast, diags) = parse_str("function f<T,>(): void { }");
9833        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9834        match single_stmt(&ast).kind {
9835            crate::StmtKind::Function { ref generics, .. } => assert_eq!(generics.len(), 1),
9836            _ => panic!("expected Function"),
9837        }
9838    }
9839
9840    #[test]
9841    fn parse_generic_call_single_type_arg() {
9842        let (ast, diags) = parse_str("identity<number>(42);");
9843        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9844        let stmt = single_stmt(&ast);
9845        let crate::StmtKind::Expr(call_id) = stmt.kind else {
9846            panic!("expected Expr stmt");
9847        };
9848        match &ast.try_expr(call_id).unwrap().kind {
9849            crate::ExprKind::Call {
9850                type_args, args, ..
9851            } => {
9852                let ta = type_args.as_ref().expect("type_args set for generic call");
9853                assert_eq!(ta.len(), 1);
9854                assert_eq!(args.len(), 1);
9855            }
9856            other => panic!("expected Call, got {other:?}"),
9857        }
9858    }
9859
9860    #[test]
9861    fn parse_generic_call_trailing_comma_ok() {
9862        let (ast, diags) = parse_str("identity<number,>(42);");
9863        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9864        let stmt = single_stmt(&ast);
9865        let crate::StmtKind::Expr(call_id) = stmt.kind else {
9866            panic!("expected Expr stmt");
9867        };
9868        match &ast.try_expr(call_id).unwrap().kind {
9869            crate::ExprKind::Call {
9870                type_args, args, ..
9871            } => {
9872                assert_eq!(type_args.as_ref().expect("type_args set").len(), 1);
9873                assert_eq!(args.len(), 1);
9874            }
9875            other => panic!("expected Call, got {other:?}"),
9876        }
9877    }
9878
9879    #[test]
9880    fn parse_generic_call_multi_type_args() {
9881        let (ast, diags) = parse_str("pair<number, string>(1, \"x\");");
9882        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9883        let stmt = single_stmt(&ast);
9884        let crate::StmtKind::Expr(call_id) = stmt.kind else {
9885            panic!("expected Expr stmt");
9886        };
9887        match &ast.try_expr(call_id).unwrap().kind {
9888            crate::ExprKind::Call { type_args, .. } => {
9889                let ta = type_args.as_ref().expect("type_args set for generic call");
9890                assert_eq!(ta.len(), 2);
9891            }
9892            other => panic!("expected Call, got {other:?}"),
9893        }
9894    }
9895
9896    #[test]
9897    fn parse_comparison_chain_unaffected_by_generic_lookahead() {
9898        let (ast, diags) = parse_str("let z = a < b > c;");
9899        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9900        assert!(
9901            !ast_contains_call_with_type_args(&ast),
9902            "should not have parsed any generic call",
9903        );
9904    }
9905
9906    #[test]
9907    fn parse_lt_with_paren_rhs_is_comparison_not_generic() {
9908        let (ast, diags) = parse_str("let z = a < (b);");
9909        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9910        assert!(!ast_contains_call_with_type_args(&ast));
9911    }
9912
9913    #[test]
9914    fn parse_generic_call_no_args() {
9915        let (ast, diags) = parse_str("none<number>();");
9916        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9917        let stmt = single_stmt(&ast);
9918        let crate::StmtKind::Expr(call_id) = stmt.kind else {
9919            panic!("expected Expr stmt");
9920        };
9921        match &ast.try_expr(call_id).unwrap().kind {
9922            crate::ExprKind::Call {
9923                type_args, args, ..
9924            } => {
9925                assert_eq!(type_args.as_ref().unwrap().len(), 1);
9926                assert!(args.is_empty());
9927            }
9928            other => panic!("expected Call, got {other:?}"),
9929        }
9930    }
9931
9932    fn ast_contains_call_with_type_args(ast: &crate::Ast) -> bool {
9933        for id in 0..ast.top_level.len() {
9934            let stmt = ast.try_stmt(ast.top_level[id]).unwrap();
9935            if walk_stmt_for_typed_call(ast, stmt) {
9936                return true;
9937            }
9938        }
9939        false
9940    }
9941
9942    fn walk_stmt_for_typed_call(ast: &crate::Ast, stmt: &crate::Stmt) -> bool {
9943        match &stmt.kind {
9944            crate::StmtKind::Let { value, .. } | crate::StmtKind::Const { value, .. } => {
9945                walk_expr_for_typed_call(ast, *value)
9946            }
9947            crate::StmtKind::Expr(e) => walk_expr_for_typed_call(ast, *e),
9948            crate::StmtKind::Block(stmts) => stmts
9949                .iter()
9950                .any(|sid| walk_stmt_for_typed_call(ast, ast.try_stmt(*sid).unwrap())),
9951            _ => false,
9952        }
9953    }
9954
9955    fn walk_expr_for_typed_call(ast: &crate::Ast, id: crate::ExprId) -> bool {
9956        match &ast.try_expr(id).unwrap().kind {
9957            crate::ExprKind::Call {
9958                type_args: Some(_), ..
9959            } => true,
9960            crate::ExprKind::Call { callee, args, .. } => {
9961                walk_expr_for_typed_call(ast, *callee)
9962                    || args.iter().any(|a| walk_expr_for_typed_call(ast, *a))
9963            }
9964            crate::ExprKind::Binary { lhs, rhs, .. } => {
9965                walk_expr_for_typed_call(ast, *lhs) || walk_expr_for_typed_call(ast, *rhs)
9966            }
9967            crate::ExprKind::Unary { operand, .. } => walk_expr_for_typed_call(ast, *operand),
9968            crate::ExprKind::Paren(e) => walk_expr_for_typed_call(ast, *e),
9969            crate::ExprKind::FieldAccess { receiver, .. } => {
9970                walk_expr_for_typed_call(ast, *receiver)
9971            }
9972            crate::ExprKind::IndexAccess { receiver, index } => {
9973                walk_expr_for_typed_call(ast, *receiver) || walk_expr_for_typed_call(ast, *index)
9974            }
9975            _ => false,
9976        }
9977    }
9978
9979    #[test]
9980    fn parse_function_body_with_statements() {
9981        let (ast, diags) = parse_str("function g(x: number): number { let y = x; y; }");
9982        assert!(diags.is_empty());
9983        let stmt = single_stmt(&ast);
9984        match stmt.kind {
9985            crate::StmtKind::Function { body, .. } => {
9986                let block = ast.try_stmt(body).unwrap();
9987                match block.kind {
9988                    crate::StmtKind::Block(ref v) => assert_eq!(v.len(), 2),
9989                    _ => panic!("expected Block"),
9990                }
9991            }
9992            _ => panic!("expected Function"),
9993        }
9994    }
9995
9996    #[test]
9997    fn function_missing_return_type_diagnoses() {
9998        let (ast, diags) = parse_str("function f() { }");
9999        assert!(!diags.is_empty());
10000        assert_eq!(diags[0].message, "expected `:` and return type");
10001        assert!(ast.top_level.is_empty());
10002    }
10003
10004    #[test]
10005    fn method_missing_return_type_still_reads_the_body() {
10006        let (_ast, diags) = parse_str(
10007            "class Box {\n  async run() {\n    const x = await f();\n    return x;\n  }\n  ok(): void {}\n}",
10008        );
10009        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
10010        assert_eq!(
10011            messages,
10012            [
10013                "`async` functions are not supported",
10014                "expected `:` and return type",
10015                "`await` is not supported"
10016            ],
10017            "{diags:?}"
10018        );
10019        assert_eq!(
10020            diags[1].help,
10021            ["write the return type: `run(…): T { … }`, with `void` when it returns nothing"]
10022        );
10023    }
10024
10025    #[test]
10026    fn an_unclosed_method_body_leaves_the_next_declaration_alone() {
10027        let (ast, diags) =
10028            parse_str("class Box {\n  a() { return 1;\n}\nfunction main(): number { return 0; }");
10029        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
10030        assert_eq!(
10031            messages,
10032            [
10033                "expected `:` and return type",
10034                "expected `}` to close class"
10035            ],
10036            "{diags:?}"
10037        );
10038        assert_eq!(ast.top_level.len(), 1, "`main` is still declared");
10039    }
10040
10041    #[test]
10042    fn every_declaration_after_an_unclosed_method_body_is_kept() {
10043        for next in [
10044            "class B { ok(): number { return 2; } }",
10045            "interface I { a: number; }",
10046            "enum E { A }",
10047            "type X = number;",
10048            "import * as fs from \"submilli:fs\";",
10049            "async function g(): number { return 1; }",
10050        ] {
10051            let source = format!(
10052                "class A {{\n  bad() {{\n    return 1;\n}}\n{next}\nfunction main(): void {{}}"
10053            );
10054            let (ast, diags) = parse_str(&source);
10055            let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
10056            let expected_async = next.starts_with("async");
10057            let mut expected = vec![
10058                "expected `:` and return type",
10059                "expected `}` to close class",
10060            ];
10061            if expected_async {
10062                expected.push("`async` functions are not supported");
10063            }
10064            assert_eq!(messages, expected, "{next}: {diags:?}");
10065            assert_eq!(
10066                ast.top_level.len(),
10067                2,
10068                "{next} and `main` are kept: {diags:?}"
10069            );
10070        }
10071    }
10072
10073    #[test]
10074    fn a_failed_statement_leaves_the_next_declaration_to_be_parsed() {
10075        for (source, kept) in [
10076            ("let x = 1 +\nimport { exists } from \"submilli:fs\";", 1),
10077            ("const y =\nclass C {}\nfunction main(): void {}", 2),
10078            (
10079                "interface I {\n  a: number;\nimport * as fs from \"submilli:fs\";",
10080                1,
10081            ),
10082        ] {
10083            let (ast, diags) = parse_str(source);
10084            assert_eq!(diags.len(), 1, "{source}: {diags:?}");
10085            assert_eq!(ast.top_level.len(), kept, "{source}: {diags:?}");
10086        }
10087    }
10088
10089    #[test]
10090    fn a_dynamic_import_after_a_failed_statement_reports_once() {
10091        for source in [
10092            "const m = import(\"submilli:fs\");\nclass C {}",
10093            "const d = import.meta.dirname;\nclass C {}",
10094        ] {
10095            let (_ast, diags) = parse_str(source);
10096            assert_eq!(diags.len(), 1, "{source}: {diags:?}");
10097        }
10098    }
10099
10100    #[test]
10101    fn members_named_like_declarations_after_a_bad_method_stay_members() {
10102        let (_ast, diags) = parse_str(
10103            "class A {\n  bad() {}\n  type: number = 1;\n  function(): void {}\n  declare(): void {}\n  async(): void {}\n}",
10104        );
10105        assert_eq!(diags.len(), 1, "{diags:?}");
10106    }
10107
10108    #[test]
10109    fn every_member_after_a_bad_method_is_still_read() {
10110        let (_ast, diags) = parse_str(
10111            "class A {\n  a() { return 1; }\n  x: number = 1;\n  get g(): number { return 1; }\n  constructor() {}\n  b() {}\n  ok(): void {}\n}\nclass B {\n  c() {}\n}",
10112        );
10113        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
10114        assert_eq!(messages, ["expected `:` and return type"; 3], "{diags:?}");
10115    }
10116
10117    #[test]
10118    fn a_member_failing_after_a_type_literal_reports_once() {
10119        let (_ast, diags) = parse_str("class A {\n  x: { a: number } 5\n}");
10120        assert_eq!(diags.len(), 1, "{diags:?}");
10121    }
10122
10123    #[test]
10124    fn multiple_declarators_become_one_statement_each() {
10125        let (ast, diags) = parse_str("let a = 0, b: string = \"s\", [c] = [1];");
10126        assert!(diags.is_empty(), "{diags:?}");
10127        let kinds: Vec<&crate::StmtKind> = ast
10128            .top_level
10129            .iter()
10130            .map(|id| &ast.try_stmt(*id).unwrap().kind)
10131            .collect();
10132        assert!(
10133            matches!(
10134                kinds.as_slice(),
10135                [
10136                    crate::StmtKind::Let { name: a, ty: None, .. },
10137                    crate::StmtKind::Let { name: b, ty: Some(_), .. },
10138                    crate::StmtKind::LetPattern { .. },
10139                ] if a.name == "a" && b.name == "b"
10140            ),
10141            "{kinds:?}"
10142        );
10143    }
10144
10145    #[test]
10146    fn declarators_keep_their_own_spans() {
10147        let source = "const a = 1, b = 2;";
10148        let (ast, diags) = parse_str(source);
10149        assert!(diags.is_empty(), "{diags:?}");
10150        let spans: Vec<&str> = ast
10151            .top_level
10152            .iter()
10153            .map(|id| {
10154                let span = ast.try_stmt(*id).unwrap().span;
10155                &source[span.start as usize..span.end as usize]
10156            })
10157            .collect();
10158        assert_eq!(spans, ["const a = 1", "b = 2;"]);
10159    }
10160
10161    #[test]
10162    fn a_declarator_missing_its_initializer_is_reported_at_it() {
10163        let (_ast, diags) = parse_str("const a = 1, b;");
10164        assert_eq!(diags.len(), 1, "{diags:?}");
10165        assert_eq!(
10166            diags[0].message,
10167            "`const` declaration requires an initializer"
10168        );
10169        assert_eq!(diags[0].span.start, 14, "{diags:?}");
10170        let (_ast, diags) = parse_str("let a = 1, ;");
10171        assert_eq!(
10172            diags[0].message, "expected identifier after `let`/`const`",
10173            "{diags:?}"
10174        );
10175    }
10176
10177    #[test]
10178    fn a_typed_declarator_without_a_value_may_precede_another() {
10179        let (ast, diags) = parse_str("let a: number | undefined, b = 1;");
10180        assert!(diags.is_empty(), "{diags:?}");
10181        assert_eq!(ast.top_level.len(), 2);
10182    }
10183
10184    #[test]
10185    fn declarators_stay_in_their_block() {
10186        let (ast, diags) = parse_str("function f(): void { let a = 1, b = 2; }\nlet c = 3;");
10187        assert!(diags.is_empty(), "{diags:?}");
10188        assert_eq!(
10189            ast.top_level.len(),
10190            2,
10191            "the block's declarators don't leak out"
10192        );
10193    }
10194
10195    #[test]
10196    fn every_exported_declarator_is_exported() {
10197        let (ast, diags) = parse_str("export const a = 1, b = 2;");
10198        assert!(diags.is_empty(), "{diags:?}");
10199        assert_eq!(ast.top_level.len(), 2);
10200        let exported: Vec<crate::StmtId> =
10201            ast.exported_decls.iter().map(|decl| decl.stmt).collect();
10202        assert_eq!(exported, ast.top_level);
10203    }
10204
10205    #[test]
10206    fn a_for_head_holds_every_declarator() {
10207        let (ast, diags) = parse_str("for (let i = 0, j = 10; i < j; i++, j--) {}");
10208        assert!(diags.is_empty(), "{diags:?}");
10209        let crate::StmtKind::For { init, update, .. } = &single_stmt(&ast).kind else {
10210            panic!("expected For");
10211        };
10212        assert_eq!(init.len(), 2);
10213        let (ast2, diags) = parse_str("for (i = 0, j = 10; i < j; i++) {}");
10214        assert!(diags.is_empty(), "{diags:?}");
10215        let crate::StmtKind::For { init: assigns, .. } = &single_stmt(&ast2).kind else {
10216            panic!("expected For");
10217        };
10218        assert_eq!(assigns.len(), 2);
10219        let update = ast.try_stmt(update.unwrap()).unwrap();
10220        assert!(
10221            matches!(&update.kind, crate::StmtKind::Block(stmts) if stmts.len() == 2),
10222            "{update:?}"
10223        );
10224    }
10225
10226    #[test]
10227    fn missing_return_type_still_reads_the_body() {
10228        let (_ast, diags) = parse_str(
10229            "async function main() {\n  const x = await f();\n  if (x) { return 1; }\n  return 2;\n}\nfunction g(): void {}",
10230        );
10231        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
10232        assert_eq!(
10233            messages,
10234            [
10235                "`async` functions are not supported",
10236                "expected `:` and return type",
10237                "`await` is not supported"
10238            ],
10239            "{diags:?}"
10240        );
10241        assert_eq!(
10242            diags[1].help,
10243            [
10244                "write the return type: `function main(…): T { … }`, with `void` when it returns nothing"
10245            ]
10246        );
10247    }
10248
10249    #[test]
10250    fn function_missing_param_type_diagnoses() {
10251        let (ast, diags) = parse_str("function f(a): void { }");
10252        assert!(!diags.is_empty());
10253        assert_eq!(diags[0].message, "parameter requires a type annotation");
10254        assert!(ast.top_level.is_empty());
10255    }
10256
10257    #[test]
10258    fn function_missing_name_diagnoses() {
10259        let (ast, diags) = parse_str("function (x: number): void { }");
10260        assert!(!diags.is_empty());
10261        assert_eq!(diags[0].message, "expected function name");
10262        assert!(ast.top_level.is_empty());
10263    }
10264
10265    #[test]
10266    fn function_trailing_param_comma_ok() {
10267        let (ast, diags) = parse_str("function f(a: number,): void { }");
10268        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10269        match single_stmt(&ast).kind {
10270            crate::StmtKind::Function { ref params, .. } => assert_eq!(params.len(), 1),
10271            _ => panic!("expected Function"),
10272        }
10273    }
10274
10275    #[test]
10276    fn function_missing_open_brace_diagnoses() {
10277        let (ast, diags) = parse_str("function f(): void");
10278        assert_eq!(diags.len(), 1);
10279        assert_eq!(diags[0].message, "expected `{`");
10280        assert!(ast.top_level.is_empty());
10281    }
10282
10283    #[test]
10284    fn snapshot_function_declaration() {
10285        let (ast, diags) =
10286            parse_str("function add(a: number, b: number): number { let sum = a + b; sum; }");
10287        assert!(diags.is_empty());
10288        insta::assert_debug_snapshot!(ast);
10289    }
10290
10291    #[test]
10292    fn rest_param_basic_parses() {
10293        let (_, diags) = parse_str("function sum(...nums: number[]): number { return 0; }");
10294        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
10295    }
10296
10297    #[test]
10298    fn rest_param_with_fixed_prefix_parses() {
10299        let (_, diags) =
10300            parse_str("function tag(label: string, ...vals: number[]): string { return label; }");
10301        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
10302    }
10303
10304    #[test]
10305    fn rest_param_in_arrow_parses() {
10306        let (_, diags) = parse_str("const f = (...n: number[]) => n.length;");
10307        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
10308    }
10309
10310    #[test]
10311    fn rest_param_in_function_type_annotation_parses() {
10312        let (_, diags) =
10313            parse_str("let f: (a: number, ...rest: string[]) => void = (a, ...r) => {};");
10314        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
10315    }
10316
10317    #[test]
10318    fn rest_param_not_last_rejected() {
10319        let (_, diags) = parse_str("function f(...xs: number[], y: number): void {}");
10320        assert!(
10321            diags.iter().any(|d| d
10322                .message
10323                .contains("rest parameter must be the last parameter")),
10324            "expected 'must be last' diagnostic, got {diags:?}",
10325        );
10326    }
10327
10328    #[test]
10329    fn rest_param_with_default_rejected() {
10330        let (_, diags) = parse_str("function f(...xs: number[] = []): void {}");
10331        assert!(
10332            diags.iter().any(|d| d
10333                .message
10334                .contains("rest parameter cannot have a default value")),
10335            "expected default-rejection diagnostic, got {diags:?}",
10336        );
10337    }
10338
10339    #[test]
10340    fn rest_param_without_annotation_rejected() {
10341        let (_, diags) = parse_str("function f(...xs): void {}");
10342        assert!(
10343            diags.iter().any(|d| d
10344                .message
10345                .contains("rest parameter requires a type annotation")),
10346            "expected missing-annotation diagnostic, got {diags:?}",
10347        );
10348    }
10349
10350    #[test]
10351    fn rest_param_destructured_rejected() {
10352        let (_, diags) = parse_str("function f(...{a, b}): void {}");
10353        assert!(
10354            diags
10355                .iter()
10356                .any(|d| d.message.contains("rest parameter cannot be destructured")),
10357            "expected destructured-rejection diagnostic, got {diags:?}",
10358        );
10359    }
10360
10361    #[test]
10362    fn rest_param_in_function_type_with_non_array_rejected() {
10363        let (_, diags) = parse_str("let f: (...xs: number) => void = () => {};");
10364        assert!(
10365            diags
10366                .iter()
10367                .any(|d| d.message.contains("rest parameter type must be an array")),
10368            "expected non-array-type diagnostic, got {diags:?}",
10369        );
10370    }
10371
10372    fn call(e: &crate::Expr) -> (crate::ExprId, &[crate::ExprId]) {
10373        match e.kind {
10374            crate::ExprKind::Call {
10375                callee, ref args, ..
10376            } => (callee, args.as_slice()),
10377            _ => panic!("expected Call, got {:?}", e.kind),
10378        }
10379    }
10380
10381    #[test]
10382    fn parse_call_no_args() {
10383        let (ast, diags) = parse_str("f();");
10384        assert!(diags.is_empty());
10385        let outer = expr_of_single_stmt(&ast);
10386        let (callee, args) = call(outer);
10387        assert!(matches!(
10388            ast.try_expr(callee).unwrap().kind,
10389            crate::ExprKind::Identifier(_)
10390        ));
10391        assert!(args.is_empty());
10392        assert_eq!(outer.span, crate::Span::new(F, 0, 3).unwrap());
10393    }
10394
10395    #[test]
10396    fn parse_call_one_arg() {
10397        let (ast, diags) = parse_str("f(1);");
10398        assert!(diags.is_empty());
10399        let outer = expr_of_single_stmt(&ast);
10400        let (callee, args) = call(outer);
10401        assert!(matches!(
10402            ast.try_expr(callee).unwrap().kind,
10403            crate::ExprKind::Identifier(_)
10404        ));
10405        assert_eq!(args.len(), 1);
10406        assert_eq!(
10407            ast.try_expr(args[0]).unwrap().kind,
10408            crate::ExprKind::Number(1.0)
10409        );
10410    }
10411
10412    #[test]
10413    fn parse_call_three_args() {
10414        let (ast, diags) = parse_str("f(1, 2, 3);");
10415        assert!(diags.is_empty());
10416        let outer = expr_of_single_stmt(&ast);
10417        let (_callee, args) = call(outer);
10418        assert_eq!(args.len(), 3);
10419        assert_eq!(
10420            ast.try_expr(args[0]).unwrap().kind,
10421            crate::ExprKind::Number(1.0)
10422        );
10423        assert_eq!(
10424            ast.try_expr(args[1]).unwrap().kind,
10425            crate::ExprKind::Number(2.0)
10426        );
10427        assert_eq!(
10428            ast.try_expr(args[2]).unwrap().kind,
10429            crate::ExprKind::Number(3.0)
10430        );
10431    }
10432
10433    #[test]
10434    fn parse_call_nested() {
10435        let (ast, diags) = parse_str("f(g(x));");
10436        assert!(diags.is_empty());
10437        let outer = expr_of_single_stmt(&ast);
10438        let (_callee, args) = call(outer);
10439        assert_eq!(args.len(), 1);
10440        let (_inner_callee, inner_args) = call(ast.try_expr(args[0]).unwrap());
10441        assert_eq!(inner_args.len(), 1);
10442        assert!(matches!(
10443            ast.try_expr(inner_args[0]).unwrap().kind,
10444            crate::ExprKind::Identifier(_)
10445        ));
10446    }
10447
10448    #[test]
10449    fn parse_curried_call() {
10450        let (ast, diags) = parse_str("f()(x);");
10451        assert!(diags.is_empty());
10452        let outer = expr_of_single_stmt(&ast);
10453        let (callee, args) = call(outer);
10454        assert_eq!(args.len(), 1);
10455        let (_inner_callee, inner_args) = call(ast.try_expr(callee).unwrap());
10456        assert!(inner_args.is_empty());
10457    }
10458
10459    #[test]
10460    fn unary_binds_looser_than_call() {
10461        let (ast, diags) = parse_str("-f();");
10462        assert!(diags.is_empty());
10463        let outer = expr_of_single_stmt(&ast);
10464        let (op, operand) = unary(outer);
10465        assert_eq!(op, crate::UnOp::Neg);
10466        let (_callee, args) = call(ast.try_expr(operand).unwrap());
10467        assert!(args.is_empty());
10468    }
10469
10470    #[test]
10471    fn call_inside_binary() {
10472        let (ast, diags) = parse_str("f() + 1;");
10473        assert!(diags.is_empty());
10474        let outer = expr_of_single_stmt(&ast);
10475        let (op, lhs, rhs) = binary(outer);
10476        assert_eq!(op, crate::BinOp::Add);
10477        let (_callee, args) = call(ast.try_expr(lhs).unwrap());
10478        assert!(args.is_empty());
10479        assert_eq!(
10480            ast.try_expr(rhs).unwrap().kind,
10481            crate::ExprKind::Number(1.0)
10482        );
10483    }
10484
10485    #[test]
10486    fn call_with_complex_args() {
10487        let (ast, diags) = parse_str("f(1 + 2, g(x));");
10488        assert!(diags.is_empty());
10489        let outer = expr_of_single_stmt(&ast);
10490        let (_callee, args) = call(outer);
10491        assert_eq!(args.len(), 2);
10492        let (op, ..) = binary(ast.try_expr(args[0]).unwrap());
10493        assert_eq!(op, crate::BinOp::Add);
10494        let (_, inner_args) = call(ast.try_expr(args[1]).unwrap());
10495        assert_eq!(inner_args.len(), 1);
10496    }
10497
10498    #[test]
10499    fn call_trailing_comma_ok() {
10500        let (ast, diags) = parse_str("f(1, 2,);");
10501        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10502        let (_callee, args) = call(expr_of_single_stmt(&ast));
10503        assert_eq!(args.len(), 2);
10504    }
10505
10506    #[test]
10507    fn unclosed_call_diagnoses() {
10508        let (ast, diags) = parse_str("f(1, 2");
10509        assert!(!diags.is_empty());
10510        assert!(diags[0].message == "expected `,` or `)`" || diags[0].message == "expected `)`");
10511        assert!(ast.top_level.is_empty());
10512    }
10513
10514    #[test]
10515    fn call_expression_statement() {
10516        let (ast, diags) = parse_str(r#"greet("world");"#);
10517        assert!(diags.is_empty());
10518        let outer = expr_of_single_stmt(&ast);
10519        let (callee, args) = call(outer);
10520        assert!(matches!(
10521            ast.try_expr(callee).unwrap().kind,
10522            crate::ExprKind::Identifier(_)
10523        ));
10524        assert_eq!(args.len(), 1);
10525        assert_eq!(
10526            ast.try_expr(args[0]).unwrap().kind,
10527            crate::ExprKind::String("world".to_string())
10528        );
10529    }
10530
10531    #[test]
10532    fn snapshot_multi_call_program() {
10533        let (ast, diags) = parse_str("f(); g(1); h(f(2), 3);");
10534        assert!(diags.is_empty());
10535        assert_eq!(ast.top_level.len(), 3);
10536        insta::assert_debug_snapshot!(ast);
10537    }
10538
10539    #[test]
10540    fn parse_if_simple() {
10541        let (ast, diags) = parse_str("if (a) { }");
10542        assert!(diags.is_empty());
10543        let stmt = single_stmt(&ast);
10544        match stmt.kind {
10545            crate::StmtKind::If {
10546                condition,
10547                then_block,
10548                else_block,
10549            } => {
10550                assert!(matches!(
10551                    ast.try_expr(condition).unwrap().kind,
10552                    crate::ExprKind::Identifier(_)
10553                ));
10554                assert!(matches!(
10555                    ast.try_stmt(then_block).unwrap().kind,
10556                    crate::StmtKind::Block(ref v) if v.is_empty()
10557                ));
10558                assert!(else_block.is_none());
10559            }
10560            _ => panic!("expected If"),
10561        }
10562    }
10563
10564    #[test]
10565    fn parse_if_else() {
10566        let (ast, diags) = parse_str("if (a) { } else { }");
10567        assert!(diags.is_empty());
10568        let stmt = single_stmt(&ast);
10569        match stmt.kind {
10570            crate::StmtKind::If { else_block, .. } => {
10571                let else_id = else_block.expect("expected else block");
10572                assert!(matches!(
10573                    ast.try_stmt(else_id).unwrap().kind,
10574                    crate::StmtKind::Block(ref v) if v.is_empty()
10575                ));
10576            }
10577            _ => panic!("expected If"),
10578        }
10579    }
10580
10581    #[test]
10582    fn parse_else_if_chain() {
10583        let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
10584        assert!(diags.is_empty());
10585        let stmt = single_stmt(&ast);
10586        let crate::StmtKind::If {
10587            else_block: Some(inner_id),
10588            ..
10589        } = stmt.kind
10590        else {
10591            panic!("expected outer If with else_block");
10592        };
10593        let inner = ast.try_stmt(inner_id).unwrap();
10594        let crate::StmtKind::If {
10595            else_block: Some(tail_id),
10596            ..
10597        } = inner.kind
10598        else {
10599            panic!("expected inner If for `else if`");
10600        };
10601        assert!(matches!(
10602            ast.try_stmt(tail_id).unwrap().kind,
10603            crate::StmtKind::Block(_)
10604        ));
10605    }
10606
10607    #[test]
10608    fn parse_if_with_body_statements() {
10609        let (ast, diags) = parse_str("if (a) { let x = 1; }");
10610        assert!(diags.is_empty());
10611        let stmt = single_stmt(&ast);
10612        match stmt.kind {
10613            crate::StmtKind::If { then_block, .. } => {
10614                let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind
10615                else {
10616                    panic!("expected Block")
10617                };
10618                assert_eq!(body.len(), 1);
10619            }
10620            _ => panic!("expected If"),
10621        }
10622    }
10623
10624    #[test]
10625    fn parse_if_braceless_body() {
10626        let (ast, diags) = parse_str("if (a) return b;");
10627        assert!(diags.is_empty(), "{diags:?}");
10628        let stmt = single_stmt(&ast);
10629        let crate::StmtKind::If {
10630            then_block,
10631            else_block,
10632            ..
10633        } = stmt.kind
10634        else {
10635            panic!("expected If");
10636        };
10637        let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10638            panic!("braceless body should wrap in a Block");
10639        };
10640        assert_eq!(body.len(), 1);
10641        assert!(matches!(
10642            ast.try_stmt(body[0]).unwrap().kind,
10643            crate::StmtKind::Return(_)
10644        ));
10645        assert!(else_block.is_none());
10646    }
10647
10648    #[test]
10649    fn parse_braceless_dangling_else_binds_nearest_if() {
10650        // `else` attaches to the inner `if`, not the outer one.
10651        let (ast, diags) = parse_str("if (a) if (b) x(); else y();");
10652        assert!(diags.is_empty(), "{diags:?}");
10653        let crate::StmtKind::If {
10654            then_block,
10655            else_block: outer_else,
10656            ..
10657        } = single_stmt(&ast).kind
10658        else {
10659            panic!("expected outer If");
10660        };
10661        assert!(outer_else.is_none(), "else must bind to the inner if");
10662        let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10663            panic!("expected wrapped Block");
10664        };
10665        let crate::StmtKind::If {
10666            else_block: Some(_),
10667            ..
10668        } = ast.try_stmt(body[0]).unwrap().kind
10669        else {
10670            panic!("inner If should own the else");
10671        };
10672    }
10673
10674    #[test]
10675    fn parse_while_braceless_body() {
10676        let (ast, diags) = parse_str("while (a) x();");
10677        assert!(diags.is_empty(), "{diags:?}");
10678        let crate::StmtKind::While { body, .. } = single_stmt(&ast).kind else {
10679            panic!("expected While");
10680        };
10681        assert!(matches!(
10682            ast.try_stmt(body).unwrap().kind,
10683            crate::StmtKind::Block(ref v) if v.len() == 1
10684        ));
10685    }
10686
10687    #[test]
10688    fn parse_loop_with_empty_body() {
10689        for src in [
10690            "while (a());",
10691            "for (const x of xs);",
10692            "for (let i = 0; i < 3; i++);",
10693            "do ; while (a());",
10694        ] {
10695            let (ast, diags) = parse_str(src);
10696            assert!(diags.is_empty(), "{src}: {diags:?}");
10697            let body = match single_stmt(&ast).kind {
10698                crate::StmtKind::While { body, .. }
10699                | crate::StmtKind::DoWhile { body, .. }
10700                | crate::StmtKind::For { body, .. }
10701                | crate::StmtKind::ForOf { body, .. } => body,
10702                ref other => panic!("{src}: expected a loop, got {other:?}"),
10703            };
10704            assert!(
10705                matches!(ast.try_stmt(body).unwrap().kind, crate::StmtKind::Block(ref v) if v.is_empty()),
10706                "{src}"
10707            );
10708        }
10709    }
10710
10711    #[test]
10712    fn parse_if_with_empty_body_is_an_error() {
10713        let (_ast, diags) = parse_str("if (a);");
10714        assert_eq!(diags[0].message, "`if` has an empty body");
10715    }
10716
10717    #[test]
10718    fn parse_empty_branch_body_reports_once() {
10719        let (_ast, diags) = parse_str("if (a) ; else b();");
10720        assert_eq!(diags.len(), 1, "{diags:?}");
10721        assert_eq!(diags[0].message, "`if` has an empty body");
10722        let (_ast, diags) = parse_str("if (a) b(); else ;");
10723        assert_eq!(diags.len(), 1, "{diags:?}");
10724        assert_eq!(diags[0].message, "`else` has an empty body");
10725        // One diagnostic per stray `;`, and the `else if` chain still parses.
10726        let (_ast, diags) = parse_str("if (a) ; else if (b) ; else ;");
10727        let messages: Vec<_> = diags.iter().map(|d| d.message.as_str()).collect();
10728        assert_eq!(
10729            messages,
10730            [
10731                "`if` has an empty body",
10732                "`if` has an empty body",
10733                "`else` has an empty body"
10734            ]
10735        );
10736    }
10737
10738    #[test]
10739    fn parse_for_of_braceless_body() {
10740        let (ast, diags) = parse_str("for (const x of xs) f(x);");
10741        assert!(diags.is_empty(), "{diags:?}");
10742        let crate::StmtKind::ForOf { body, .. } = single_stmt(&ast).kind else {
10743            panic!("expected ForOf");
10744        };
10745        assert!(matches!(
10746            ast.try_stmt(body).unwrap().kind,
10747            crate::StmtKind::Block(ref v) if v.len() == 1
10748        ));
10749    }
10750
10751    #[test]
10752    fn parse_nested_if() {
10753        let (ast, diags) = parse_str("if (a) { if (b) { } }");
10754        assert!(diags.is_empty());
10755        let outer = single_stmt(&ast);
10756        let crate::StmtKind::If { then_block, .. } = outer.kind else {
10757            panic!("expected outer If");
10758        };
10759        let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10760            panic!("expected Block")
10761        };
10762        assert_eq!(body.len(), 1);
10763        assert!(matches!(
10764            ast.try_stmt(body[0]).unwrap().kind,
10765            crate::StmtKind::If { .. }
10766        ));
10767    }
10768
10769    #[test]
10770    fn if_missing_open_paren_diagnoses() {
10771        let (ast, diags) = parse_str("if a { }");
10772        assert!(!diags.is_empty());
10773        assert_eq!(diags[0].message, "expected `(`");
10774        assert!(ast.top_level.is_empty());
10775    }
10776
10777    #[test]
10778    fn if_missing_close_paren_diagnoses() {
10779        let (ast, diags) = parse_str("if (a { }");
10780        assert!(!diags.is_empty());
10781        assert_eq!(diags[0].message, "expected `)`");
10782        assert!(ast.top_level.is_empty());
10783    }
10784
10785    #[test]
10786    fn if_braceless_body_parses() {
10787        let (ast, diags) = parse_str("if (a) x;");
10788        assert!(diags.is_empty(), "{diags:?}");
10789        let crate::StmtKind::If { then_block, .. } = single_stmt(&ast).kind else {
10790            panic!("expected If");
10791        };
10792        assert!(matches!(
10793            ast.try_stmt(then_block).unwrap().kind,
10794            crate::StmtKind::Block(ref v) if v.len() == 1
10795        ));
10796    }
10797
10798    #[test]
10799    fn snapshot_else_if_chain() {
10800        let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
10801        assert!(diags.is_empty());
10802        insta::assert_debug_snapshot!(ast);
10803    }
10804
10805    #[test]
10806    fn parse_while_simple() {
10807        let (ast, diags) = parse_str("while (a) { }");
10808        assert!(diags.is_empty());
10809        let stmt = single_stmt(&ast);
10810        match stmt.kind {
10811            crate::StmtKind::While { condition, body } => {
10812                assert!(matches!(
10813                    ast.try_expr(condition).unwrap().kind,
10814                    crate::ExprKind::Identifier(_)
10815                ));
10816                assert!(matches!(
10817                    ast.try_stmt(body).unwrap().kind,
10818                    crate::StmtKind::Block(ref v) if v.is_empty()
10819                ));
10820            }
10821            _ => panic!("expected While"),
10822        }
10823    }
10824
10825    #[test]
10826    fn parse_while_with_body() {
10827        let (ast, diags) = parse_str("while (a) { x; y; }");
10828        assert!(diags.is_empty());
10829        let stmt = single_stmt(&ast);
10830        let crate::StmtKind::While { body, .. } = stmt.kind else {
10831            panic!("expected While");
10832        };
10833        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10834            panic!("expected Block")
10835        };
10836        assert_eq!(stmts.len(), 2);
10837    }
10838
10839    #[test]
10840    fn while_missing_paren_diagnoses() {
10841        let (ast, diags) = parse_str("while a { }");
10842        assert!(!diags.is_empty());
10843        assert_eq!(diags[0].message, "expected `(`");
10844        assert!(ast.top_level.is_empty());
10845    }
10846
10847    #[test]
10848    fn parse_for_all_slots() {
10849        let (ast, diags) = parse_str("for (let i = 0; i < 10; i = i + 1) { x; }");
10850        assert!(diags.is_empty(), "{diags:?}");
10851        let stmt = single_stmt(&ast);
10852        let crate::StmtKind::For {
10853            init,
10854            condition,
10855            update,
10856            body,
10857        } = &stmt.kind
10858        else {
10859            panic!("expected For");
10860        };
10861        assert!(!init.is_empty(), "init missing");
10862        assert!(condition.is_some(), "condition missing");
10863        assert!(update.is_some(), "update missing");
10864        assert!(matches!(
10865            ast.try_stmt(*body).unwrap().kind,
10866            crate::StmtKind::Block(_)
10867        ));
10868    }
10869
10870    #[test]
10871    fn parse_for_empty_slots() {
10872        let (ast, diags) = parse_str("for (;;) { break; }");
10873        assert!(diags.is_empty(), "{diags:?}");
10874        let stmt = single_stmt(&ast);
10875        let crate::StmtKind::For {
10876            init,
10877            condition,
10878            update,
10879            ..
10880        } = &stmt.kind
10881        else {
10882            panic!("expected For");
10883        };
10884        assert!(init.is_empty() && condition.is_none() && update.is_none());
10885    }
10886
10887    #[test]
10888    fn parse_for_of_const_binding() {
10889        let (ast, diags) = parse_str("for (const x of arr) { y; }");
10890        assert!(diags.is_empty(), "{diags:?}");
10891        let stmt = single_stmt(&ast);
10892        let crate::StmtKind::ForOf {
10893            binding_kind,
10894            ref name,
10895            ref ty,
10896            ..
10897        } = stmt.kind
10898        else {
10899            panic!("expected ForOf");
10900        };
10901        assert!(matches!(binding_kind, crate::BindingKind::Const));
10902        assert_eq!(name.name, "x");
10903        assert!(ty.is_none());
10904    }
10905
10906    #[test]
10907    fn parse_for_of_let_with_annotation() {
10908        let (ast, diags) = parse_str("for (let x: number of arr) { y; }");
10909        assert!(diags.is_empty(), "{diags:?}");
10910        let stmt = single_stmt(&ast);
10911        let crate::StmtKind::ForOf {
10912            binding_kind,
10913            ref ty,
10914            ..
10915        } = stmt.kind
10916        else {
10917            panic!("expected ForOf");
10918        };
10919        assert!(matches!(binding_kind, crate::BindingKind::Let));
10920        assert!(ty.is_some());
10921    }
10922
10923    #[test]
10924    fn parse_do_while_simple() {
10925        let (ast, diags) = parse_str("do { x; } while (a);");
10926        assert!(diags.is_empty(), "{diags:?}");
10927        let stmt = single_stmt(&ast);
10928        assert!(matches!(stmt.kind, crate::StmtKind::DoWhile { .. }));
10929    }
10930
10931    #[test]
10932    fn parse_break_and_continue() {
10933        let (ast, diags) = parse_str("while (a) { break; continue; }");
10934        assert!(diags.is_empty(), "{diags:?}");
10935        let stmt = single_stmt(&ast);
10936        let crate::StmtKind::While { body, .. } = stmt.kind else {
10937            panic!("expected While");
10938        };
10939        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10940            panic!("expected Block");
10941        };
10942        assert!(matches!(
10943            ast.try_stmt(stmts[0]).unwrap().kind,
10944            crate::StmtKind::Break
10945        ));
10946        assert!(matches!(
10947            ast.try_stmt(stmts[1]).unwrap().kind,
10948            crate::StmtKind::Continue
10949        ));
10950    }
10951
10952    #[test]
10953    fn parse_bare_return_in_function() {
10954        let (ast, diags) = parse_str("function f(): void { return; }");
10955        assert!(diags.is_empty());
10956        let f = single_stmt(&ast);
10957        let crate::StmtKind::Function { body, .. } = f.kind else {
10958            panic!("expected Function");
10959        };
10960        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10961            panic!("expected Block")
10962        };
10963        assert_eq!(stmts.len(), 1);
10964        match ast.try_stmt(stmts[0]).unwrap().kind {
10965            crate::StmtKind::Return(None) => {}
10966            _ => panic!("expected Return(None)"),
10967        }
10968    }
10969
10970    #[test]
10971    fn parse_return_with_value() {
10972        let (ast, diags) = parse_str("function f(): number { return 1; }");
10973        assert!(diags.is_empty());
10974        let f = single_stmt(&ast);
10975        let crate::StmtKind::Function { body, .. } = f.kind else {
10976            panic!("expected Function");
10977        };
10978        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10979            panic!("expected Block")
10980        };
10981        match ast.try_stmt(stmts[0]).unwrap().kind {
10982            crate::StmtKind::Return(Some(value)) => {
10983                assert_eq!(
10984                    ast.try_expr(value).unwrap().kind,
10985                    crate::ExprKind::Number(1.0)
10986                );
10987            }
10988            _ => panic!("expected Return(Some)"),
10989        }
10990    }
10991
10992    #[test]
10993    fn parse_return_with_expression() {
10994        let (ast, diags) = parse_str("function f(): number { return 1 + 2; }");
10995        assert!(diags.is_empty());
10996        let f = single_stmt(&ast);
10997        let crate::StmtKind::Function { body, .. } = f.kind else {
10998            panic!("expected Function");
10999        };
11000        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
11001            panic!("expected Block")
11002        };
11003        match ast.try_stmt(stmts[0]).unwrap().kind {
11004            crate::StmtKind::Return(Some(value)) => {
11005                let (op, ..) = binary(ast.try_expr(value).unwrap());
11006                assert_eq!(op, crate::BinOp::Add);
11007            }
11008            _ => panic!("expected Return(Some(Binary))"),
11009        }
11010    }
11011
11012    #[test]
11013    fn return_missing_semicolon_diagnoses() {
11014        let (_, diags) = parse_str("function f(): number { return x x; }");
11015        assert!(!diags.is_empty());
11016        assert_eq!(diags[0].message, "expected `;` after return");
11017    }
11018
11019    #[test]
11020    fn parse_bare_block() {
11021        let (ast, diags) = parse_str("{ x; y; }");
11022        assert!(diags.is_empty());
11023        let stmt = single_stmt(&ast);
11024        let crate::StmtKind::Block(ref stmts) = stmt.kind else {
11025            panic!("expected Block");
11026        };
11027        assert_eq!(stmts.len(), 2);
11028    }
11029
11030    #[test]
11031    fn parse_empty_bare_block() {
11032        let (ast, diags) = parse_str("{ }");
11033        assert!(diags.is_empty());
11034        let stmt = single_stmt(&ast);
11035        let crate::StmtKind::Block(ref stmts) = stmt.kind else {
11036            panic!("expected Block");
11037        };
11038        assert!(stmts.is_empty());
11039    }
11040
11041    #[test]
11042    fn parse_nested_bare_blocks() {
11043        let (ast, diags) = parse_str("{ { } }");
11044        assert!(diags.is_empty());
11045        let outer = single_stmt(&ast);
11046        let crate::StmtKind::Block(ref stmts) = outer.kind else {
11047            panic!("expected outer Block");
11048        };
11049        assert_eq!(stmts.len(), 1);
11050        assert!(matches!(
11051            ast.try_stmt(stmts[0]).unwrap().kind,
11052            crate::StmtKind::Block(ref v) if v.is_empty()
11053        ));
11054    }
11055
11056    #[test]
11057    fn snapshot_function_with_if_and_return() {
11058        let source = "function isPositive(n: number): boolean {\n  if (n > 0) {\n    return true;\n  } else {\n    return false;\n  }\n}";
11059        let (ast, diags) = parse_str(source);
11060        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11061        insta::assert_debug_snapshot!(ast);
11062    }
11063
11064    fn object_literal(e: &crate::Expr) -> &[crate::ObjectLiteralMember] {
11065        match e.kind {
11066            crate::ExprKind::ObjectLiteral { ref members } => members.as_slice(),
11067            _ => panic!("expected ObjectLiteral, got {:?}", e.kind),
11068        }
11069    }
11070
11071    fn object_literal_field(m: &crate::ObjectLiteralMember) -> &crate::ObjectLiteralField {
11072        match m {
11073            crate::ObjectLiteralMember::Field(f) => f,
11074            crate::ObjectLiteralMember::Spread { .. }
11075            | crate::ObjectLiteralMember::Computed { .. } => {
11076                panic!("expected Field member, got Spread")
11077            }
11078        }
11079    }
11080
11081    fn array_literal(e: &crate::Expr) -> &[crate::ArrayLiteralElement] {
11082        match e.kind {
11083            crate::ExprKind::ArrayLiteral { ref elements } => elements.as_slice(),
11084            _ => panic!("expected ArrayLiteral, got {:?}", e.kind),
11085        }
11086    }
11087
11088    fn array_literal_value(el: &crate::ArrayLiteralElement) -> crate::ExprId {
11089        match el {
11090            crate::ArrayLiteralElement::Value(id) => *id,
11091            crate::ArrayLiteralElement::Spread { .. } => {
11092                panic!("expected Value element, got Spread")
11093            }
11094        }
11095    }
11096
11097    #[test]
11098    fn parse_empty_object_literal() {
11099        let (ast, diags) = parse_str("let x = {};");
11100        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11101        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11102            panic!("expected Let");
11103        };
11104        assert!(object_literal(ast.try_expr(value).unwrap()).is_empty());
11105    }
11106
11107    #[test]
11108    fn parse_object_literal_simple() {
11109        let (ast, diags) = parse_str("let p = { x: 1, y: 2 };");
11110        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11111        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11112            panic!("expected Let");
11113        };
11114        let members = object_literal(ast.try_expr(value).unwrap());
11115        assert_eq!(members.len(), 2);
11116        let f0 = object_literal_field(&members[0]);
11117        let f1 = object_literal_field(&members[1]);
11118        assert_eq!(f0.name.name, "x");
11119        assert_eq!(f1.name.name, "y");
11120        assert_eq!(
11121            ast.try_expr(f0.value).unwrap().kind,
11122            crate::ExprKind::Number(1.0)
11123        );
11124        assert_eq!(
11125            ast.try_expr(f1.value).unwrap().kind,
11126            crate::ExprKind::Number(2.0)
11127        );
11128    }
11129
11130    #[test]
11131    fn parse_object_literal_keyword_keys() {
11132        let (ast, diags) = parse_str("let p = { type: 1, default: 2, null: 3, true: 4 };");
11133        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11134        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11135            panic!("expected Let");
11136        };
11137        let names: Vec<&str> = object_literal(ast.try_expr(value).unwrap())
11138            .iter()
11139            .map(object_literal_field)
11140            .map(|f| f.name.name.as_str())
11141            .collect();
11142        assert_eq!(names, vec!["type", "default", "null", "true"]);
11143    }
11144
11145    #[test]
11146    fn parse_object_literal_string_key() {
11147        let (ast, diags) = parse_str(r#"let p = { "hello": 1 };"#);
11148        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11149        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11150            panic!("expected Let");
11151        };
11152        let members = object_literal(ast.try_expr(value).unwrap());
11153        assert_eq!(members.len(), 1);
11154        let f0 = object_literal_field(&members[0]);
11155        assert_eq!(f0.name.name, "hello");
11156    }
11157
11158    #[test]
11159    fn parse_object_literal_string_key_decodes_escape() {
11160        let (ast, diags) = parse_str(r#"let p = { "content\u002dtype": 1 };"#);
11161        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11162        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11163            panic!("expected Let");
11164        };
11165        let members = object_literal(ast.try_expr(value).unwrap());
11166        assert_eq!(members.len(), 1);
11167        let f0 = object_literal_field(&members[0]);
11168        assert_eq!(f0.name.name, "content-type");
11169    }
11170
11171    #[test]
11172    fn parse_object_literal_shorthand() {
11173        let (ast, diags) = parse_str("let p = { x, y: 2 };");
11174        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11175        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11176            panic!("expected Let");
11177        };
11178        let members = object_literal(ast.try_expr(value).unwrap());
11179        assert_eq!(members.len(), 2);
11180        let f0 = object_literal_field(&members[0]);
11181        assert_eq!(f0.name.name, "x");
11182        // `{ x }` desugars to `{ x: x }` — value is an identifier reference.
11183        assert_eq!(
11184            ast.try_expr(f0.value).unwrap().kind,
11185            crate::ExprKind::Identifier(crate::Ident {
11186                name: "x".into(),
11187                span: f0.name.span,
11188            })
11189        );
11190        let f1 = object_literal_field(&members[1]);
11191        assert_eq!(f1.name.name, "y");
11192        assert_eq!(
11193            ast.try_expr(f1.value).unwrap().kind,
11194            crate::ExprKind::Number(2.0)
11195        );
11196    }
11197
11198    #[test]
11199    fn parse_object_literal_string_key_shorthand_rejected() {
11200        let (_ast, diags) = parse_str(r#"let p = { "x" };"#);
11201        assert!(
11202            diags
11203                .iter()
11204                .any(|d| d.message.contains("expected `:` after field name")),
11205            "string-key shorthand should be rejected: {diags:?}"
11206        );
11207    }
11208
11209    #[test]
11210    fn parse_object_literal_trailing_comma() {
11211        let (ast, diags) = parse_str("let p = { x: 1, y: 2, };");
11212        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11213        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11214            panic!("expected Let");
11215        };
11216        assert_eq!(object_literal(ast.try_expr(value).unwrap()).len(), 2);
11217    }
11218
11219    #[test]
11220    fn parse_object_literal_duplicate_key_diagnoses() {
11221        let (_, diags) = parse_str("let p = { x: 1, x: 2 };");
11222        assert_eq!(diags.len(), 1);
11223        assert_eq!(diags[0].message, "duplicate field `x` in object literal");
11224    }
11225
11226    #[test]
11227    fn parse_empty_array_literal() {
11228        let (ast, diags) = parse_str("let xs = [];");
11229        assert!(diags.is_empty());
11230        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11231            panic!("expected Let");
11232        };
11233        assert!(array_literal(ast.try_expr(value).unwrap()).is_empty());
11234    }
11235
11236    #[test]
11237    fn parse_array_literal_simple() {
11238        let (ast, diags) = parse_str("let xs = [1, 2, 3];");
11239        assert!(diags.is_empty());
11240        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11241            panic!("expected Let");
11242        };
11243        let elements = array_literal(ast.try_expr(value).unwrap());
11244        assert_eq!(elements.len(), 3);
11245        assert_eq!(
11246            ast.try_expr(array_literal_value(&elements[0]))
11247                .unwrap()
11248                .kind,
11249            crate::ExprKind::Number(1.0)
11250        );
11251        assert_eq!(
11252            ast.try_expr(array_literal_value(&elements[2]))
11253                .unwrap()
11254                .kind,
11255            crate::ExprKind::Number(3.0)
11256        );
11257    }
11258
11259    #[test]
11260    fn parse_array_literal_trailing_comma() {
11261        let (ast, diags) = parse_str("let xs = [1, 2,];");
11262        assert!(diags.is_empty());
11263        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11264            panic!("expected Let");
11265        };
11266        assert_eq!(array_literal(ast.try_expr(value).unwrap()).len(), 2);
11267    }
11268
11269    #[test]
11270    fn parse_nested_array_in_object() {
11271        let (ast, diags) = parse_str("let p = { items: [1, 2] };");
11272        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11273        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11274            panic!("expected Let");
11275        };
11276        let members = object_literal(ast.try_expr(value).unwrap());
11277        let f0 = object_literal_field(&members[0]);
11278        assert_eq!(f0.name.name, "items");
11279        let inner = array_literal(ast.try_expr(f0.value).unwrap());
11280        assert_eq!(inner.len(), 2);
11281    }
11282
11283    #[test]
11284    fn parse_nested_object_in_array() {
11285        let (ast, diags) = parse_str("let xs = [{ x: 1 }, { x: 2 }];");
11286        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11287        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11288            panic!("expected Let");
11289        };
11290        let elements = array_literal(ast.try_expr(value).unwrap());
11291        assert_eq!(elements.len(), 2);
11292        assert_eq!(
11293            object_literal(ast.try_expr(array_literal_value(&elements[0])).unwrap()).len(),
11294            1
11295        );
11296        assert_eq!(
11297            object_literal(ast.try_expr(array_literal_value(&elements[1])).unwrap()).len(),
11298            1
11299        );
11300    }
11301
11302    #[test]
11303    fn brace_at_statement_start_is_block_not_object() {
11304        let (_, diags) = parse_str("{ x: 1 }");
11305        assert!(
11306            !diags.is_empty(),
11307            "expected a diagnostic for `x:` inside block"
11308        );
11309    }
11310
11311    fn field_access(e: &crate::Expr) -> (crate::ExprId, &str) {
11312        match e.kind {
11313            crate::ExprKind::FieldAccess { receiver, ref name } => (receiver, name.name.as_str()),
11314            _ => panic!("expected FieldAccess, got {:?}", e.kind),
11315        }
11316    }
11317
11318    fn index_access(e: &crate::Expr) -> (crate::ExprId, crate::ExprId) {
11319        match e.kind {
11320            crate::ExprKind::IndexAccess { receiver, index } => (receiver, index),
11321            _ => panic!("expected IndexAccess, got {:?}", e.kind),
11322        }
11323    }
11324
11325    #[test]
11326    fn parse_field_access_simple() {
11327        let (ast, diags) = parse_str("a.b;");
11328        assert!(diags.is_empty());
11329        let outer = expr_of_single_stmt(&ast);
11330        let (receiver, name) = field_access(outer);
11331        assert!(matches!(
11332            ast.try_expr(receiver).unwrap().kind,
11333            crate::ExprKind::Identifier(_)
11334        ));
11335        assert_eq!(name, "b");
11336    }
11337
11338    #[test]
11339    fn parse_field_access_keyword_name() {
11340        let (ast, diags) = parse_str("status.type.default.null;");
11341        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11342        let outer = expr_of_single_stmt(&ast);
11343        let (receiver, name) = field_access(outer);
11344        assert_eq!(name, "null");
11345        let (receiver, name) = field_access(ast.try_expr(receiver).unwrap());
11346        assert_eq!(name, "default");
11347        let (_receiver, name) = field_access(ast.try_expr(receiver).unwrap());
11348        assert_eq!(name, "type");
11349    }
11350
11351    #[test]
11352    fn parse_field_access_chain_left_assoc() {
11353        let (ast, diags) = parse_str("a.b.c;");
11354        assert!(diags.is_empty());
11355        let outer = expr_of_single_stmt(&ast);
11356        let (mid_id, c) = field_access(outer);
11357        assert_eq!(c, "c");
11358        let (a_id, b) = field_access(ast.try_expr(mid_id).unwrap());
11359        assert_eq!(b, "b");
11360        assert!(matches!(
11361            ast.try_expr(a_id).unwrap().kind,
11362            crate::ExprKind::Identifier(_)
11363        ));
11364    }
11365
11366    #[test]
11367    fn parse_index_access_simple() {
11368        let (ast, diags) = parse_str("a[0];");
11369        assert!(diags.is_empty());
11370        let outer = expr_of_single_stmt(&ast);
11371        let (receiver, idx) = index_access(outer);
11372        assert!(matches!(
11373            ast.try_expr(receiver).unwrap().kind,
11374            crate::ExprKind::Identifier(_)
11375        ));
11376        assert_eq!(
11377            ast.try_expr(idx).unwrap().kind,
11378            crate::ExprKind::Number(0.0)
11379        );
11380    }
11381
11382    #[test]
11383    fn parse_index_access_chain() {
11384        let (ast, diags) = parse_str("a[0][1];");
11385        assert!(diags.is_empty());
11386        let outer = expr_of_single_stmt(&ast);
11387        let (mid_id, one) = index_access(outer);
11388        assert_eq!(
11389            ast.try_expr(one).unwrap().kind,
11390            crate::ExprKind::Number(1.0)
11391        );
11392        let (a_id, zero) = index_access(ast.try_expr(mid_id).unwrap());
11393        assert_eq!(
11394            ast.try_expr(zero).unwrap().kind,
11395            crate::ExprKind::Number(0.0)
11396        );
11397        assert!(matches!(
11398            ast.try_expr(a_id).unwrap().kind,
11399            crate::ExprKind::Identifier(_)
11400        ));
11401    }
11402
11403    #[test]
11404    fn parse_mixed_access_and_call_chain() {
11405        let (ast, diags) = parse_str("a.b[c].d();");
11406        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11407        let outer = expr_of_single_stmt(&ast);
11408        let (callee, args) = call(outer);
11409        assert!(args.is_empty());
11410        let (idx_id, d_name) = field_access(ast.try_expr(callee).unwrap());
11411        assert_eq!(d_name, "d");
11412        let (b_id, c_idx) = index_access(ast.try_expr(idx_id).unwrap());
11413        assert!(matches!(
11414            ast.try_expr(c_idx).unwrap().kind,
11415            crate::ExprKind::Identifier(_)
11416        ));
11417        let (a_id, b_name) = field_access(ast.try_expr(b_id).unwrap());
11418        assert_eq!(b_name, "b");
11419        assert!(matches!(
11420            ast.try_expr(a_id).unwrap().kind,
11421            crate::ExprKind::Identifier(_)
11422        ));
11423    }
11424
11425    #[test]
11426    fn parse_call_then_field() {
11427        let (ast, diags) = parse_str("f().x;");
11428        assert!(diags.is_empty());
11429        let outer = expr_of_single_stmt(&ast);
11430        let (call_id, x_name) = field_access(outer);
11431        assert_eq!(x_name, "x");
11432        let (_callee, args) = call(ast.try_expr(call_id).unwrap());
11433        assert!(args.is_empty());
11434    }
11435
11436    #[test]
11437    fn parse_index_with_complex_expression() {
11438        let (ast, diags) = parse_str("a[i + 1];");
11439        assert!(diags.is_empty());
11440        let outer = expr_of_single_stmt(&ast);
11441        let (_, idx) = index_access(outer);
11442        let (op, ..) = binary(ast.try_expr(idx).unwrap());
11443        assert_eq!(op, crate::BinOp::Add);
11444    }
11445
11446    #[test]
11447    fn field_access_missing_name_diagnoses() {
11448        let (_, diags) = parse_str("a.;");
11449        assert!(!diags.is_empty());
11450        assert_eq!(diags[0].message, "expected field name after `.`");
11451    }
11452
11453    #[test]
11454    fn parse_postfix_increment() {
11455        let (ast, diags) = parse_str("x++;");
11456        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11457        let expr = expr_of_single_stmt(&ast);
11458        let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
11459            panic!("expected PostfixUnary, got {:?}", expr.kind);
11460        };
11461        assert_eq!(op, crate::PostfixOp::Inc);
11462        assert!(matches!(
11463            ast.try_expr(operand).unwrap().kind,
11464            crate::ExprKind::Identifier(_)
11465        ));
11466        assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
11467    }
11468
11469    #[test]
11470    fn parse_postfix_decrement() {
11471        let (ast, diags) = parse_str("x--;");
11472        assert!(diags.is_empty());
11473        let expr = expr_of_single_stmt(&ast);
11474        let crate::ExprKind::PostfixUnary { op, .. } = expr.kind else {
11475            panic!("expected PostfixUnary");
11476        };
11477        assert_eq!(op, crate::PostfixOp::Dec);
11478    }
11479
11480    #[test]
11481    fn parse_postfix_non_null_assertion() {
11482        let (ast, diags) = parse_str("x!;");
11483        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11484        let expr = expr_of_single_stmt(&ast);
11485        let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
11486            panic!("expected PostfixUnary, got {:?}", expr.kind);
11487        };
11488        assert_eq!(op, crate::PostfixOp::NonNullAssert);
11489        assert!(matches!(
11490            ast.try_expr(operand).unwrap().kind,
11491            crate::ExprKind::Identifier(_)
11492        ));
11493        assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
11494    }
11495
11496    #[test]
11497    fn parse_non_null_assertion_continues_chain() {
11498        let (ast, diags) = parse_str("x!.y;");
11499        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11500        let expr = expr_of_single_stmt(&ast);
11501        let crate::ExprKind::FieldAccess { receiver, name } = &expr.kind else {
11502            panic!("expected FieldAccess, got {:?}", expr.kind);
11503        };
11504        assert_eq!(name.name, "y");
11505        assert!(matches!(
11506            ast.try_expr(*receiver).unwrap().kind,
11507            crate::ExprKind::PostfixUnary {
11508                op: crate::PostfixOp::NonNullAssert,
11509                ..
11510            }
11511        ));
11512    }
11513
11514    #[test]
11515    fn parse_postfix_on_field_access() {
11516        let (ast, diags) = parse_str("o.f++;");
11517        assert!(diags.is_empty());
11518        let expr = expr_of_single_stmt(&ast);
11519        let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
11520            panic!("expected PostfixUnary");
11521        };
11522        assert!(matches!(
11523            ast.try_expr(operand).unwrap().kind,
11524            crate::ExprKind::FieldAccess { .. }
11525        ));
11526    }
11527
11528    #[test]
11529    fn parse_postfix_on_index_access() {
11530        let (ast, diags) = parse_str("xs[0]++;");
11531        assert!(diags.is_empty());
11532        let expr = expr_of_single_stmt(&ast);
11533        let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
11534            panic!("expected PostfixUnary");
11535        };
11536        assert!(matches!(
11537            ast.try_expr(operand).unwrap().kind,
11538            crate::ExprKind::IndexAccess { .. }
11539        ));
11540    }
11541
11542    #[test]
11543    fn parse_postfix_terminates_chain() {
11544        let (_, diags) = parse_str("x++.y;");
11545        assert!(!diags.is_empty());
11546        assert_eq!(diags[0].message, "expected `;` after expression");
11547    }
11548
11549    #[test]
11550    fn parse_postfix_in_binary_continues() {
11551        let (ast, diags) = parse_str("let y = x++ + 1;");
11552        assert!(diags.is_empty(), "unexpected: {diags:?}");
11553        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11554            panic!("expected Let");
11555        };
11556        let (op, lhs, _) = binary(ast.try_expr(value).unwrap());
11557        assert_eq!(op, crate::BinOp::Add);
11558        assert!(matches!(
11559            ast.try_expr(lhs).unwrap().kind,
11560            crate::ExprKind::PostfixUnary { .. }
11561        ));
11562    }
11563
11564    #[test]
11565    fn parse_index_assignment() {
11566        let (ast, diags) = parse_str("xs[0] = 1;");
11567        assert!(diags.is_empty(), "unexpected: {diags:?}");
11568        let stmt = single_stmt(&ast);
11569        let crate::StmtKind::AssignIndex {
11570            receiver,
11571            index,
11572            value,
11573        } = stmt.kind
11574        else {
11575            panic!("expected AssignIndex, got {:?}", stmt.kind);
11576        };
11577        assert!(matches!(
11578            ast.try_expr(receiver).unwrap().kind,
11579            crate::ExprKind::Identifier(_)
11580        ));
11581        assert_eq!(
11582            ast.try_expr(index).unwrap().kind,
11583            crate::ExprKind::Number(0.0)
11584        );
11585        assert_eq!(
11586            ast.try_expr(value).unwrap().kind,
11587            crate::ExprKind::Number(1.0)
11588        );
11589    }
11590
11591    #[test]
11592    fn parse_for_update_postfix() {
11593        let (ast, diags) = parse_str("function main(): void { for (let i = 0; i < 3; i++) {} }");
11594        assert!(diags.is_empty(), "unexpected: {diags:?}");
11595        assert_eq!(ast.top_level.len(), 1);
11596    }
11597
11598    #[test]
11599    fn parse_for_update_index_assignment() {
11600        let (ast, diags) =
11601            parse_str("function main(): void { for (let i = 0; i < 3; xs[i] = i) {} }");
11602        assert!(diags.is_empty(), "unexpected: {diags:?}");
11603        assert_eq!(ast.top_level.len(), 1);
11604    }
11605
11606    #[test]
11607    fn index_access_missing_close_bracket_diagnoses() {
11608        let (_, diags) = parse_str("a[1;");
11609        assert!(!diags.is_empty());
11610        assert_eq!(diags[0].message, "expected `]`");
11611    }
11612
11613    #[test]
11614    fn field_access_inside_object_literal_value_does_not_steal_dot() {
11615        let (ast, diags) = parse_str("let x = { a: 1 }.foo;");
11616        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11617        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11618            panic!("expected Let");
11619        };
11620        let (recv_id, name) = field_access(ast.try_expr(value).unwrap());
11621        assert_eq!(name, "foo");
11622        assert_eq!(object_literal(ast.try_expr(recv_id).unwrap()).len(), 1);
11623    }
11624
11625    fn type_of_let(stmt: &crate::Stmt) -> &crate::TypeAnnotation {
11626        match stmt.kind {
11627            crate::StmtKind::Let { ref ty, .. } => ty.as_ref().expect("expected type annotation"),
11628            _ => panic!("expected Let"),
11629        }
11630    }
11631
11632    #[test]
11633    fn parse_array_type_annotation() {
11634        let (ast, diags) = parse_str("let xs: number[] = null;");
11635        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11636        let ty = type_of_let(single_stmt(&ast));
11637        match ty.kind {
11638            crate::TypeAnnotationKind::Array(ref inner) => {
11639                assert!(matches!(inner.kind, crate::TypeAnnotationKind::Name { .. }));
11640            }
11641            _ => panic!("expected Array, got {:?}", ty.kind),
11642        }
11643    }
11644
11645    #[test]
11646    fn parse_nested_array_type_annotation() {
11647        let (ast, diags) = parse_str("let xs: number[][] = null;");
11648        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11649        let ty = type_of_let(single_stmt(&ast));
11650        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11651            panic!("expected outer Array");
11652        };
11653        let crate::TypeAnnotationKind::Array(ref inner2) = inner.kind else {
11654            panic!("expected inner Array");
11655        };
11656        assert!(matches!(
11657            inner2.kind,
11658            crate::TypeAnnotationKind::Name { .. }
11659        ));
11660    }
11661
11662    #[test]
11663    fn parse_object_type_annotation_simple() {
11664        let (ast, diags) = parse_str("let p: { x: number; y: number } = null;");
11665        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11666        let ty = type_of_let(single_stmt(&ast));
11667        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11668            panic!("expected Object, got {:?}", ty.kind);
11669        };
11670        assert_eq!(fields.len(), 2);
11671        assert_eq!(fields[0].name.name, "x");
11672        assert_eq!(fields[1].name.name, "y");
11673    }
11674
11675    #[test]
11676    fn readonly_is_rejected_on_methods_accessors_and_method_signatures() {
11677        let source = "class K {\n\
11678             readonly m(): number { return 1; }\n\
11679             public readonly n<T>(): number { return 1; }\n\
11680             readonly get g(): number { return 1; }\n\
11681             readonly p: number = 1;\n\
11682             }\n\
11683             type T = { readonly r(a: number): boolean; readonly f: () => void };\n\
11684             interface I { readonly s(): void; readonly q: number; }\n";
11685        let (_, diags) = parse_str(source);
11686        let lines: Vec<usize> = diags
11687            .iter()
11688            .map(|d| {
11689                assert_eq!(
11690                    d.message,
11691                    "`readonly` can only modify a property or index signature"
11692                );
11693                source[..d.span.start as usize].matches('\n').count() + 1
11694            })
11695            .collect();
11696        assert_eq!(lines, vec![2, 3, 4, 7, 8]);
11697    }
11698
11699    #[test]
11700    fn parse_object_type_method_members() {
11701        // `m(): T` is the same member as `m: () => T`, so it parses to a function-typed
11702        // field — including through the `?` modifier.
11703        let (ast, diags) =
11704            parse_str("let p: { m(): number; opt?(): string; r(a: number): boolean } = null;");
11705        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11706        let ty = type_of_let(single_stmt(&ast));
11707        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11708            panic!("expected Object, got {:?}", ty.kind);
11709        };
11710        assert_eq!(fields.len(), 3);
11711        for field in fields {
11712            assert!(
11713                matches!(field.ty.kind, crate::TypeAnnotationKind::Function { .. }),
11714                "member `{}` should be function-typed, got {:?}",
11715                field.name.name,
11716                field.ty.kind,
11717            );
11718        }
11719        assert!(fields[1].optional, "`opt?()` is an optional member");
11720        let crate::TypeAnnotationKind::Function { ref params, .. } = fields[2].ty.kind else {
11721            unreachable!("checked above");
11722        };
11723        assert_eq!(params.len(), 1);
11724        assert_eq!(params[0].name.name, "a");
11725    }
11726
11727    #[test]
11728    fn parse_object_type_annotation_with_commas() {
11729        let (ast, diags) = parse_str("let p: { x: number, y: number } = null;");
11730        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11731        let ty = type_of_let(single_stmt(&ast));
11732        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11733            panic!("expected Object");
11734        };
11735        assert_eq!(fields.len(), 2);
11736    }
11737
11738    #[test]
11739    fn parse_object_type_annotation_empty() {
11740        let (ast, diags) = parse_str("let p: {} = null;");
11741        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11742        let ty = type_of_let(single_stmt(&ast));
11743        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11744            panic!("expected Object");
11745        };
11746        assert!(fields.is_empty());
11747    }
11748
11749    #[test]
11750    fn parse_object_type_with_optional_field() {
11751        let (ast, diags) = parse_str("let p: { id: number; nick?: string } = null;");
11752        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11753        let ty = type_of_let(single_stmt(&ast));
11754        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11755            panic!("expected Object");
11756        };
11757        assert_eq!(fields.len(), 2);
11758        assert_eq!(fields[0].name.name, "id");
11759        assert!(!fields[0].optional);
11760        assert_eq!(fields[1].name.name, "nick");
11761        assert!(fields[1].optional);
11762    }
11763
11764    #[test]
11765    fn parse_object_type_with_readonly_fields() {
11766        let (ast, diags) = parse_str(
11767            "let issue: { readonly id: string; readonly title?: string; body: string } = null;",
11768        );
11769        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11770        let ty = type_of_let(single_stmt(&ast));
11771        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11772            panic!("expected Object");
11773        };
11774        let fields: Vec<(&str, bool, bool)> = fields
11775            .iter()
11776            .map(|field| (field.name.name.as_str(), field.optional, field.readonly))
11777            .collect();
11778        assert_eq!(
11779            fields,
11780            vec![
11781                ("id", false, true),
11782                ("title", true, true),
11783                ("body", false, false),
11784            ]
11785        );
11786    }
11787
11788    #[test]
11789    fn parse_object_type_mixed_optional_and_required() {
11790        let (ast, diags) = parse_str("let p: { a?: number; b: string; c?: boolean } = null;");
11791        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11792        let ty = type_of_let(single_stmt(&ast));
11793        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11794            panic!("expected Object");
11795        };
11796        let opt: Vec<(&str, bool)> = fields
11797            .iter()
11798            .map(|f| (f.name.name.as_str(), f.optional))
11799            .collect();
11800        assert_eq!(opt, vec![("a", true), ("b", false), ("c", true)]);
11801    }
11802
11803    #[test]
11804    fn parse_object_type_keyword_fields() {
11805        let (ast, diags) =
11806            parse_str("let p: { type: string; default?: number; null: boolean } = null;");
11807        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11808        let ty = type_of_let(single_stmt(&ast));
11809        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11810            panic!("expected Object");
11811        };
11812        let opt: Vec<(&str, bool)> = fields
11813            .iter()
11814            .map(|f| (f.name.name.as_str(), f.optional))
11815            .collect();
11816        assert_eq!(
11817            opt,
11818            vec![("type", false), ("default", true), ("null", false)]
11819        );
11820    }
11821
11822    #[test]
11823    fn parse_object_type_quoted_fields() {
11824        let (ast, diags) =
11825            parse_str(r#"let p: { "content-type": string; "x\u002drequest-id"?: string } = null;"#);
11826        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11827        let ty = type_of_let(single_stmt(&ast));
11828        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11829            panic!("expected Object");
11830        };
11831        let opt: Vec<(&str, bool)> = fields
11832            .iter()
11833            .map(|f| (f.name.name.as_str(), f.optional))
11834            .collect();
11835        assert_eq!(opt, vec![("content-type", false), ("x-request-id", true)]);
11836    }
11837
11838    #[test]
11839    fn parse_interface_with_optional_property() {
11840        let (ast, diags) = parse_str("interface User { id: number; deletedAt?: string; }");
11841        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11842        let stmt = single_stmt(&ast);
11843        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11844            panic!("expected interface decl");
11845        };
11846        let opt: Vec<(&str, bool)> = members
11847            .iter()
11848            .map(|m| match m {
11849                crate::InterfaceMember::Property { name, optional, .. } => {
11850                    (name.name.as_str(), *optional)
11851                }
11852                _ => panic!("expected property"),
11853            })
11854            .collect();
11855        assert_eq!(opt, vec![("id", false), ("deletedAt", true)]);
11856    }
11857
11858    #[test]
11859    fn parse_interface_with_readonly_properties() {
11860        let (ast, diags) = parse_str(
11861            "interface Issue { readonly id: string; readonly title?: string; body: string; }",
11862        );
11863        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11864        let stmt = single_stmt(&ast);
11865        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11866            panic!("expected interface decl");
11867        };
11868        let opt: Vec<(&str, bool, bool)> = members
11869            .iter()
11870            .map(|member| match member {
11871                crate::InterfaceMember::Property {
11872                    name,
11873                    optional,
11874                    readonly,
11875                    ..
11876                } => (name.name.as_str(), *optional, *readonly),
11877                _ => panic!("expected property"),
11878            })
11879            .collect();
11880        assert_eq!(
11881            opt,
11882            vec![
11883                ("id", false, true),
11884                ("title", true, true),
11885                ("body", false, false),
11886            ]
11887        );
11888    }
11889
11890    #[test]
11891    fn parse_readonly_property_name_without_modifier() {
11892        let (ast, diags) = parse_str(
11893            "interface Meta { readonly: boolean; }\nlet meta: { readonly: boolean } = null;",
11894        );
11895        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11896        let crate::StmtKind::InterfaceDecl { ref members, .. } =
11897            ast.try_stmt(ast.top_level[0]).unwrap().kind
11898        else {
11899            panic!("expected interface decl");
11900        };
11901        let crate::InterfaceMember::Property {
11902            ref name, readonly, ..
11903        } = members[0]
11904        else {
11905            panic!("expected property");
11906        };
11907        assert_eq!(name.name, "readonly");
11908        assert!(
11909            !readonly,
11910            "`readonly` here is the property name, not a modifier"
11911        );
11912
11913        let ty = type_of_let(ast.try_stmt(ast.top_level[1]).unwrap());
11914        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11915            panic!("expected Object");
11916        };
11917        assert_eq!(fields[0].name.name, "readonly");
11918        assert!(!fields[0].readonly);
11919    }
11920
11921    #[test]
11922    fn parse_interface_keyword_members() {
11923        let (ast, diags) =
11924            parse_str("interface IssueStatus { type: string; default(): string; null: boolean; }");
11925        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11926        let stmt = single_stmt(&ast);
11927        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11928            panic!("expected interface decl");
11929        };
11930        let names: Vec<&str> = members
11931            .iter()
11932            .map(|m| match m {
11933                crate::InterfaceMember::Method { name, .. }
11934                | crate::InterfaceMember::Property { name, .. } => name.name.as_str(),
11935                crate::InterfaceMember::IndexSignature(_) => panic!("unexpected index signature"),
11936            })
11937            .collect();
11938        assert_eq!(names, vec!["type", "default", "null"]);
11939    }
11940
11941    #[test]
11942    fn parse_interface_quoted_property_members() {
11943        let (ast, diags) = parse_str(
11944            r#"interface Headers { "content-type": string; "x\u002drequest-id"?: string; }"#,
11945        );
11946        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11947        let stmt = single_stmt(&ast);
11948        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11949            panic!("expected interface decl");
11950        };
11951        let names: Vec<(&str, bool)> = members
11952            .iter()
11953            .map(|m| match m {
11954                crate::InterfaceMember::Property { name, optional, .. } => {
11955                    (name.name.as_str(), *optional)
11956                }
11957                _ => panic!("expected property"),
11958            })
11959            .collect();
11960        assert_eq!(names, vec![("content-type", false), ("x-request-id", true)]);
11961    }
11962
11963    #[test]
11964    fn parse_param_optional_marker() {
11965        let (ast, diags) = parse_str("function f(x?: number): void {}");
11966        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11967        let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
11968            panic!("expected function")
11969        };
11970        assert!(params[0].optional);
11971        assert!(params[0].default.is_none());
11972    }
11973
11974    #[test]
11975    fn parse_interface_optional_method() {
11976        let (ast, diags) = parse_str("interface F { foo?(x?: number): void; }");
11977        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11978        let crate::StmtKind::InterfaceDecl { ref members, .. } = single_stmt(&ast).kind else {
11979            panic!("expected interface")
11980        };
11981        let crate::InterfaceMember::Property { optional, ty, .. } = &members[0] else {
11982            panic!("expected callable property")
11983        };
11984        assert!(*optional);
11985        let crate::TypeAnnotationKind::Function { params, .. } = &ty.kind else {
11986            panic!("expected function type")
11987        };
11988        assert!(params[0].optional);
11989    }
11990
11991    #[test]
11992    fn parse_interface_with_call_signature() {
11993        let (ast, diags) = parse_str("interface F { (s: string): number; }");
11994        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11995        let stmt = single_stmt(&ast);
11996        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11997            panic!("expected interface decl");
11998        };
11999        assert_eq!(members.len(), 1);
12000        match &members[0] {
12001            crate::InterfaceMember::Method { name, params, .. } => {
12002                assert_eq!(name.name, "@call");
12003                assert_eq!(params.len(), 1);
12004                assert_eq!(params[0].name.name, "s");
12005            }
12006            other => panic!("expected method (call signature), got {other:?}"),
12007        }
12008    }
12009
12010    #[test]
12011    fn parse_interface_mixed_call_signature_and_method() {
12012        let (ast, diags) = parse_str("interface F { greet(): string; (x: number): number; }");
12013        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12014        let stmt = single_stmt(&ast);
12015        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
12016            panic!("expected interface decl");
12017        };
12018        let names: Vec<&str> = members
12019            .iter()
12020            .map(|m| match m {
12021                crate::InterfaceMember::Method { name, .. } => name.name.as_str(),
12022                _ => "<property>",
12023            })
12024            .collect();
12025        assert_eq!(names, vec!["greet", "@call"]);
12026    }
12027
12028    #[test]
12029    fn parse_interface_rejects_generic_call_signature() {
12030        let (_ast, diags) = parse_str("interface F { <T>(x: T): T; }");
12031        assert!(
12032            diags
12033                .iter()
12034                .any(|d| d.message.contains("generic call signatures")),
12035            "expected generic-call-signature diagnostic, got {diags:?}"
12036        );
12037    }
12038
12039    #[test]
12040    fn parse_object_type_with_array_field() {
12041        let (ast, diags) = parse_str("let p: { xs: number[] } = null;");
12042        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12043        let ty = type_of_let(single_stmt(&ast));
12044        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
12045            panic!("expected Object");
12046        };
12047        assert_eq!(fields[0].name.name, "xs");
12048        assert!(matches!(
12049            fields[0].ty.kind,
12050            crate::TypeAnnotationKind::Array(_)
12051        ));
12052    }
12053
12054    #[test]
12055    fn parse_array_of_objects() {
12056        let (ast, diags) = parse_str("let xs: { x: number }[] = null;");
12057        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12058        let ty = type_of_let(single_stmt(&ast));
12059        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
12060            panic!("expected Array");
12061        };
12062        assert!(matches!(
12063            inner.kind,
12064            crate::TypeAnnotationKind::Object { .. }
12065        ));
12066    }
12067
12068    #[test]
12069    fn parse_tuple_type_annotation() {
12070        let (ast, diags) = parse_str("let p: [string, number] = null;");
12071        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12072        let ty = type_of_let(single_stmt(&ast));
12073        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
12074            panic!("expected Tuple, got {:?}", ty.kind);
12075        };
12076        assert_eq!(elems.len(), 2);
12077        assert!(matches!(
12078            elems[0].kind,
12079            crate::TypeAnnotationKind::Name { .. }
12080        ));
12081        assert!(matches!(
12082            elems[1].kind,
12083            crate::TypeAnnotationKind::Name { .. }
12084        ));
12085    }
12086
12087    #[test]
12088    fn parse_singleton_tuple_type() {
12089        let (ast, diags) = parse_str("let p: [number] = null;");
12090        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12091        let ty = type_of_let(single_stmt(&ast));
12092        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
12093            panic!("expected Tuple");
12094        };
12095        assert_eq!(elems.len(), 1);
12096    }
12097
12098    #[test]
12099    fn parse_nested_tuple_type() {
12100        let (ast, diags) = parse_str("let p: [[number, string], boolean] = null;");
12101        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12102        let ty = type_of_let(single_stmt(&ast));
12103        let crate::TypeAnnotationKind::Tuple(ref outer) = ty.kind else {
12104            panic!("expected outer Tuple");
12105        };
12106        assert_eq!(outer.len(), 2);
12107        let crate::TypeAnnotationKind::Tuple(ref inner) = outer[0].kind else {
12108            panic!("expected inner Tuple");
12109        };
12110        assert_eq!(inner.len(), 2);
12111    }
12112
12113    #[test]
12114    fn parse_array_of_tuple_type() {
12115        let (ast, diags) = parse_str("let xs: [string, number][] = null;");
12116        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12117        let ty = type_of_let(single_stmt(&ast));
12118        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
12119            panic!("expected outer Array, got {:?}", ty.kind);
12120        };
12121        assert!(matches!(inner.kind, crate::TypeAnnotationKind::Tuple(_)));
12122    }
12123
12124    #[test]
12125    fn parse_trailing_comma_tuple_type() {
12126        let (ast, diags) = parse_str("let p: [number, string,] = null;");
12127        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12128        let ty = type_of_let(single_stmt(&ast));
12129        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
12130            panic!("expected Tuple");
12131        };
12132        assert_eq!(elems.len(), 2);
12133    }
12134
12135    #[test]
12136    fn parse_empty_tuple_type_rejected() {
12137        let (_ast, diags) = parse_str("let p: [] = null;");
12138        assert!(
12139            diags.iter().any(|d| d
12140                .message
12141                .contains("tuple types must have at least one element")),
12142            "expected empty-tuple diagnostic, got: {diags:?}"
12143        );
12144    }
12145
12146    #[test]
12147    fn parse_labeled_tuple_drops_labels() {
12148        let (ast, diags) = parse_str("let p: [first: number, string] = null;");
12149        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12150        let ty = type_of_let(single_stmt(&ast));
12151        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
12152            panic!("expected Tuple, got {:?}", ty.kind);
12153        };
12154        assert_eq!(elems.len(), 2);
12155        assert!(
12156            elems
12157                .iter()
12158                .all(|e| matches!(e.kind, crate::TypeAnnotationKind::Name { .. }))
12159        );
12160    }
12161
12162    #[test]
12163    fn parse_labeled_optional_tuple_element() {
12164        let (ast, diags) = parse_str("let p: [first: number, second?: string] = null;");
12165        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12166        let ty = type_of_let(single_stmt(&ast));
12167        let crate::TypeAnnotationKind::Tuple(elements) = &ty.kind else {
12168            panic!("expected tuple")
12169        };
12170        assert!(matches!(
12171            elements[1].kind,
12172            crate::TypeAnnotationKind::Optional(_)
12173        ));
12174    }
12175
12176    #[test]
12177    fn parse_readonly_binds_to_the_postfix_type() {
12178        let (ast, diags) = parse_str("let p: readonly number[][] | null = null;");
12179        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12180        let ty = type_of_let(single_stmt(&ast));
12181        let crate::TypeAnnotationKind::Union(ref members) = ty.kind else {
12182            panic!("expected Union, got {:?}", ty.kind);
12183        };
12184        let crate::TypeAnnotationKind::Readonly(ref operand) = members[0].kind else {
12185            panic!("expected Readonly, got {:?}", members[0].kind);
12186        };
12187        let crate::TypeAnnotationKind::Array(ref inner) = operand.kind else {
12188            panic!("expected Array, got {:?}", operand.kind);
12189        };
12190        assert!(matches!(inner.kind, crate::TypeAnnotationKind::Array(_)));
12191    }
12192
12193    #[test]
12194    fn parse_readonly_as_a_type_name_is_not_the_operator() {
12195        let (ast, diags) = parse_str("let p: readonly = null;");
12196        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12197        let ty = type_of_let(single_stmt(&ast));
12198        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
12199    }
12200
12201    #[test]
12202    fn parse_generic_one_arg() {
12203        let (ast, diags) = parse_str("let x: Foo<number> = null;");
12204        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12205        let ty = type_of_let(single_stmt(&ast));
12206        let crate::TypeAnnotationKind::Name { ref name, ref args } = ty.kind else {
12207            panic!("expected Name, got {:?}", ty.kind);
12208        };
12209        assert_eq!(name.span, crate::Span::new(F, 7, 10).unwrap());
12210        assert_eq!(ty.span, crate::Span::new(F, 7, 18).unwrap());
12211        assert_eq!(args.len(), 1);
12212        assert!(matches!(
12213            args[0].kind,
12214            crate::TypeAnnotationKind::Name { .. }
12215        ));
12216    }
12217
12218    #[test]
12219    fn parse_generic_two_args() {
12220        let (ast, diags) = parse_str("let m: Map<string, number> = null;");
12221        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12222        let ty = type_of_let(single_stmt(&ast));
12223        let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
12224            panic!("expected Name");
12225        };
12226        assert_eq!(args.len(), 2);
12227    }
12228
12229    #[test]
12230    fn parse_generic_nested() {
12231        let (ast, diags) = parse_str("let b: Box<Box<T>> = null;");
12232        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12233        let ty = type_of_let(single_stmt(&ast));
12234        let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
12235            panic!("expected outer Name");
12236        };
12237        assert_eq!(args.len(), 1);
12238        let crate::TypeAnnotationKind::Name {
12239            args: ref inner, ..
12240        } = args[0].kind
12241        else {
12242            panic!("expected inner Name");
12243        };
12244        assert_eq!(inner.len(), 1);
12245    }
12246
12247    #[test]
12248    fn parse_generic_array_postfix() {
12249        let (ast, diags) = parse_str("let xs: Foo<number>[] = null;");
12250        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12251        let ty = type_of_let(single_stmt(&ast));
12252        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
12253            panic!("expected Array");
12254        };
12255        assert!(
12256            matches!(inner.kind, crate::TypeAnnotationKind::Name { ref args, .. } if args.len() == 1)
12257        );
12258    }
12259
12260    #[test]
12261    fn parse_generic_empty_diagnoses() {
12262        let (_ast, diags) = parse_str("let x: Foo<> = null;");
12263        assert!(
12264            diags
12265                .iter()
12266                .any(|d| d.message.contains("empty generic argument list")),
12267            "expected empty-generic-args diagnostic, got: {diags:?}"
12268        );
12269    }
12270
12271    #[test]
12272    fn parse_generic_trailing_comma_ok() {
12273        let (_ast, diags) = parse_str("let x: Foo<number,> = null;");
12274        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12275    }
12276
12277    #[test]
12278    fn parse_generic_missing_close_diagnoses() {
12279        let (_ast, diags) = parse_str("let x: Foo<number = null;");
12280        assert!(
12281            diags
12282                .iter()
12283                .any(|d| d.message.contains("expected `,` or `>`")),
12284            "expected unterminated-generic diagnostic, got: {diags:?}"
12285        );
12286    }
12287
12288    #[test]
12289    fn parse_qualified_two_segments() {
12290        let (ast, diags) = parse_str("let x: Foo.Bar = null;");
12291        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12292        let ty = type_of_let(single_stmt(&ast));
12293        let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
12294            panic!("expected Qualified, got {:?}", ty.kind);
12295        };
12296        assert_eq!(path.len(), 2);
12297        // `Foo` at 7..10, `Bar` at 11..14.
12298        assert_eq!(path[0].span, crate::Span::new(F, 7, 10).unwrap());
12299        assert_eq!(path[1].span, crate::Span::new(F, 11, 14).unwrap());
12300        assert!(args.is_empty());
12301        assert_eq!(ty.span, crate::Span::new(F, 7, 14).unwrap());
12302    }
12303
12304    #[test]
12305    fn parse_qualified_three_segments() {
12306        let (ast, diags) = parse_str("let x: Foo.Bar.Baz = null;");
12307        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12308        let ty = type_of_let(single_stmt(&ast));
12309        let crate::TypeAnnotationKind::Qualified { ref path, .. } = ty.kind else {
12310            panic!("expected Qualified, got {:?}", ty.kind);
12311        };
12312        assert_eq!(path.len(), 3);
12313    }
12314
12315    #[test]
12316    fn parse_qualified_with_generic_args() {
12317        let (ast, diags) = parse_str("let x: Foo.Box<number> = null;");
12318        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12319        let ty = type_of_let(single_stmt(&ast));
12320        let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
12321            panic!("expected Qualified, got {:?}", ty.kind);
12322        };
12323        assert_eq!(path.len(), 2);
12324        assert_eq!(args.len(), 1);
12325        assert!(matches!(
12326            args[0].kind,
12327            crate::TypeAnnotationKind::Name { .. }
12328        ));
12329    }
12330
12331    #[test]
12332    fn parse_qualified_with_array_postfix() {
12333        let (ast, diags) = parse_str("let xs: Foo.Bar[] = null;");
12334        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12335        let ty = type_of_let(single_stmt(&ast));
12336        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
12337            panic!("expected Array, got {:?}", ty.kind);
12338        };
12339        assert!(matches!(
12340            inner.kind,
12341            crate::TypeAnnotationKind::Qualified { .. }
12342        ));
12343    }
12344
12345    #[test]
12346    fn parse_qualified_trailing_dot_diagnoses() {
12347        let (_ast, diags) = parse_str("let x: Foo. = null;");
12348        assert!(
12349            diags.iter().any(|d| d
12350                .message
12351                .contains("expected identifier after `.` in type name")),
12352            "expected dotted-name diagnostic, got: {diags:?}"
12353        );
12354    }
12355
12356    #[test]
12357    fn parse_qualified_void_root_rejected_dot() {
12358        let (ast, diags) = parse_str("function f(): void.Bar { }");
12359        assert!(!diags.is_empty(), "expected a diagnostic for `void.Bar`");
12360        let _ = ast;
12361    }
12362
12363    fn union_members(ty: &crate::TypeAnnotation) -> &[crate::TypeAnnotation] {
12364        match &ty.kind {
12365            crate::TypeAnnotationKind::Union(members) => members,
12366            other => panic!("expected Union, got {other:?}"),
12367        }
12368    }
12369
12370    #[test]
12371    fn parse_union_two_members() {
12372        let (ast, diags) = parse_str("let x: number | string = null;");
12373        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12374        let ty = type_of_let(single_stmt(&ast));
12375        let members = union_members(ty);
12376        assert_eq!(members.len(), 2);
12377        assert!(matches!(
12378            members[0].kind,
12379            crate::TypeAnnotationKind::Name { .. }
12380        ));
12381        assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); // `number`
12382        assert!(matches!(
12383            members[1].kind,
12384            crate::TypeAnnotationKind::Name { .. }
12385        ));
12386        assert_eq!(members[1].span, crate::Span::new(F, 16, 22).unwrap()); // `string`
12387        assert_eq!(ty.span, crate::Span::new(F, 7, 22).unwrap());
12388    }
12389
12390    #[test]
12391    fn parse_union_three_members() {
12392        let (ast, diags) = parse_str("let x: A | B | C = null;");
12393        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12394        let ty = type_of_let(single_stmt(&ast));
12395        let members = union_members(ty);
12396        assert_eq!(members.len(), 3);
12397        for m in members {
12398            assert!(matches!(m.kind, crate::TypeAnnotationKind::Name { .. }));
12399        }
12400    }
12401
12402    #[test]
12403    fn parse_union_lower_than_array() {
12404        let (ast, diags) = parse_str("let x: number[] | string = null;");
12405        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12406        let ty = type_of_let(single_stmt(&ast));
12407        let members = union_members(ty);
12408        assert_eq!(members.len(), 2);
12409        assert!(
12410            matches!(members[0].kind, crate::TypeAnnotationKind::Array(_)),
12411            "expected first member to be Array, got {:?}",
12412            members[0].kind
12413        );
12414        assert!(matches!(
12415            members[1].kind,
12416            crate::TypeAnnotationKind::Name { .. }
12417        ));
12418    }
12419
12420    #[test]
12421    fn parse_union_with_null_member() {
12422        let (ast, diags) = parse_str("let x: number | null = null;");
12423        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12424        let ty = type_of_let(single_stmt(&ast));
12425        let members = union_members(ty);
12426        assert_eq!(members.len(), 2);
12427        assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); // `number`
12428        assert!(matches!(
12429            members[1].kind,
12430            crate::TypeAnnotationKind::Name { .. }
12431        ));
12432        assert_eq!(members[1].span, crate::Span::new(F, 16, 20).unwrap()); // `null`
12433    }
12434
12435    #[test]
12436    fn parse_union_in_function_return_type() {
12437        let (ast, diags) = parse_str("function f(): number | string { }");
12438        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12439        let stmt = single_stmt(&ast);
12440        match stmt.kind {
12441            crate::StmtKind::Function {
12442                ref return_type, ..
12443            } => {
12444                let return_type = return_type.as_ref().expect("plain return type");
12445                let members = union_members(return_type);
12446                assert_eq!(members.len(), 2);
12447            }
12448            _ => panic!("expected Function"),
12449        }
12450    }
12451
12452    #[test]
12453    fn parse_union_in_function_param_type() {
12454        let (ast, diags) = parse_str("function f(x: number | string): void { }");
12455        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12456        let stmt = single_stmt(&ast);
12457        match stmt.kind {
12458            crate::StmtKind::Function { ref params, .. } => {
12459                let p_ty = params[0].ty.as_ref().expect("param type");
12460                let members = union_members(p_ty);
12461                assert_eq!(members.len(), 2);
12462            }
12463            _ => panic!("expected Function"),
12464        }
12465    }
12466
12467    #[test]
12468    fn parse_union_in_object_field_type() {
12469        let (ast, diags) = parse_str("let p: { x: number | string } = null;");
12470        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12471        let ty = type_of_let(single_stmt(&ast));
12472        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
12473            panic!("expected Object, got {:?}", ty.kind);
12474        };
12475        assert_eq!(fields.len(), 1);
12476        let members = union_members(&fields[0].ty);
12477        assert_eq!(members.len(), 2);
12478    }
12479
12480    #[test]
12481    fn parse_union_leading_pipe_is_forgiven() {
12482        let (ast, diags) = parse_str("let x: | number | string = null;");
12483        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12484        let ty = type_of_let(single_stmt(&ast));
12485        let members = union_members(ty);
12486        assert_eq!(members.len(), 2);
12487    }
12488
12489    #[test]
12490    fn parse_union_leading_pipe_single_member_unwraps() {
12491        let (ast, diags) = parse_str("let x: | number = null;");
12492        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12493        let ty = type_of_let(single_stmt(&ast));
12494        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
12495    }
12496
12497    #[test]
12498    fn parse_union_trailing_pipe_diagnoses() {
12499        let (_, diags) = parse_str("let x: number | = null;");
12500        assert!(!diags.is_empty());
12501        assert_eq!(diags[0].message, "expected type");
12502    }
12503
12504    #[test]
12505    fn snapshot_union_type_mix() {
12506        let (ast, diags) = parse_str("let xs: number[] | string | null = null;");
12507        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12508        insta::assert_debug_snapshot!(ast);
12509    }
12510
12511    #[test]
12512    fn parse_object_type_duplicate_field_diagnoses() {
12513        let (_, diags) = parse_str("let p: { readonly x: number; x: string } = null;");
12514        assert!(!diags.is_empty());
12515        assert!(
12516            diags
12517                .iter()
12518                .any(|d| d.message == "duplicate field `x` in object type"),
12519            "expected duplicate-field diagnostic, got: {diags:?}"
12520        );
12521    }
12522
12523    #[test]
12524    fn snapshot_object_and_array_literals() {
12525        let (ast, diags) = parse_str(r#"let mix = [{ a: 1, b: "hi" }, {}];"#);
12526        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12527        insta::assert_debug_snapshot!(ast);
12528    }
12529
12530    #[test]
12531    fn snapshot_function_with_while() {
12532        let source = "function loop(n: number): void {\n  while (n > 0) {\n    log(n);\n  }\n}";
12533        let (ast, diags) = parse_str(source);
12534        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12535        insta::assert_debug_snapshot!(ast);
12536    }
12537
12538    fn parse_arrow_const(
12539        source: &str,
12540    ) -> (
12541        Ast,
12542        Vec<crate::ParamDecl>,
12543        Option<crate::TypeAnnotation>,
12544        crate::ArrowBody,
12545    ) {
12546        let (ast, diags) = parse_str(source);
12547        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12548        assert_eq!(ast.top_level.len(), 1);
12549        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12550        let value = match &stmt.kind {
12551            StmtKind::Const { value, .. } => *value,
12552            _ => panic!("expected const declaration"),
12553        };
12554        let value = match ast.try_expr(value).unwrap().kind {
12555            ExprKind::FunctionExpression { function, .. } => function,
12556            _ => value,
12557        };
12558        let expr = ast.try_expr(value).unwrap().clone();
12559        let (params, return_type, body) = match expr.kind {
12560            ExprKind::Arrow {
12561                params,
12562                return_type,
12563                body,
12564                ..
12565            } => (params, return_type, body),
12566            other => panic!("expected Arrow, got {other:?}"),
12567        };
12568        (ast, params, return_type, body)
12569    }
12570
12571    #[test]
12572    fn parses_anonymous_function_expression_as_arrow() {
12573        let (ast, params, return_type, body) =
12574            parse_arrow_const("const f = function (x: number): number { return x + 1; };");
12575        assert_eq!(params.len(), 1);
12576        assert_eq!(params[0].name.name, "x");
12577        assert!(return_type.is_some());
12578        assert!(matches!(body, crate::ArrowBody::Block(_)));
12579        let _ = ast;
12580    }
12581
12582    #[test]
12583    fn parses_function_expression_with_no_params() {
12584        let (_ast, params, _return_type, body) =
12585            parse_arrow_const("const f = function (): void {};");
12586        assert!(params.is_empty());
12587        assert!(matches!(body, crate::ArrowBody::Block(_)));
12588    }
12589
12590    /// The named wrapper preserves its body signature.
12591    #[test]
12592    fn parses_named_function_expression_signature() {
12593        let (_ast, params, _return_type, _body) =
12594            parse_arrow_const("const f = function named(x: number): number { return x + 1; };");
12595        assert_eq!(params.len(), 1);
12596    }
12597
12598    /// Self references are resolved in the function expression scope.
12599    #[test]
12600    fn accepts_a_named_function_expression_that_calls_itself() {
12601        let (_ast, diags) =
12602            parse_str("const f = function bar(x: number): number { return bar(x); };");
12603        assert!(diags.is_empty(), "{diags:?}");
12604    }
12605
12606    /// A member name after `.` is never a reference to the function itself.
12607    #[test]
12608    fn accepts_a_named_function_expression_using_the_name_as_a_property() {
12609        let (_ast, diags) = parse_str("const f = function bar(o: O): number { return o.bar; };");
12610        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12611    }
12612
12613    /// Nor is a key before `:`.
12614    #[test]
12615    fn accepts_a_named_function_expression_using_the_name_as_an_object_key() {
12616        let (_ast, diags) = parse_str(
12617            "const f = function bar(x: number): number { const o = { bar: 1 }; return o.bar + x; };",
12618        );
12619        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12620    }
12621
12622    /// A string that happens to spell the name is not a reference.
12623    #[test]
12624    fn accepts_a_named_function_expression_with_its_name_in_a_string() {
12625        let (_ast, diags) =
12626            parse_str("const f = function bar(x: number): string { return \"bar\"; };");
12627        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12628    }
12629
12630    /// A self reference in a template substitution remains a normal expression.
12631    #[test]
12632    fn accepts_a_self_call_inside_a_template_substitution() {
12633        let (_ast, diags) =
12634            parse_str("const f = function bar(x: number): string { return `${bar(0)}`; };");
12635        assert!(diags.is_empty(), "{diags:?}");
12636    }
12637
12638    #[test]
12639    fn rejects_a_generic_function_expression() {
12640        let (_ast, diags) = parse_str("const f = function <T>(x: T): T { return x; };");
12641        assert!(
12642            diags
12643                .iter()
12644                .any(|d| d.message.contains("generic function expressions")),
12645            "generic function expression should be rejected: {diags:?}"
12646        );
12647    }
12648
12649    #[test]
12650    fn accepts_this_inside_a_function_expression_in_a_class_method() {
12651        let (_ast, diags) = parse_str(
12652            "class C { x: number = 1; m(): number { \
12653             const f = function (this: { x: number }): number { return this.x; }; return f(); } }",
12654        );
12655        assert!(diags.is_empty(), "{diags:?}");
12656    }
12657
12658    #[test]
12659    fn retains_this_boundary_inside_method_shorthand() {
12660        let (ast, diags) = parse_str(
12661            "class C { x: number = 1; m(): number { \
12662             const o = { x: 2, g(): number { return this.x; } }; return o.g(); } }",
12663        );
12664        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12665        assert!((0..ast.exprs_len()).any(|index| matches!(
12666            ast.try_expr(crate::ExprId(index as u32)).unwrap().kind,
12667            ExprKind::ThisOutsideReceiver
12668        )));
12669    }
12670
12671    #[test]
12672    fn this_still_parses_in_an_ordinary_class_method() {
12673        let (_ast, diags) = parse_str("class C { x: number = 1; m(): number { return this.x; } }");
12674        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12675    }
12676
12677    /// `function` is a valid property name; the expression form must not shadow that.
12678    #[test]
12679    fn function_keyword_still_parses_as_an_object_key() {
12680        let (_ast, diags) = parse_str("let o = { function: 1 };");
12681        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12682    }
12683
12684    fn object_literal_member_values(source: &str) -> (Ast, Vec<crate::ExprId>) {
12685        let (ast, diags) = parse_str(source);
12686        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12687        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12688        let value = match &stmt.kind {
12689            StmtKind::Const { value, .. } => *value,
12690            other => panic!("expected a const declaration, got {other:?}"),
12691        };
12692        let members = match &ast.try_expr(value).unwrap().kind {
12693            ExprKind::ObjectLiteral { members } => members
12694                .iter()
12695                .map(crate::ObjectLiteralMember::value)
12696                .collect::<Vec<_>>(),
12697            other => panic!("expected ObjectLiteral, got {other:?}"),
12698        };
12699        (ast, members)
12700    }
12701
12702    #[test]
12703    fn parses_object_literal_method_shorthand_as_arrow() {
12704        let (ast, members) =
12705            object_literal_member_values("const o = { m(x: number): number { return x; } };");
12706        assert_eq!(members.len(), 1);
12707        match &ast.try_expr(members[0]).unwrap().kind {
12708            ExprKind::Arrow { params, body, .. } => {
12709                assert_eq!(params.len(), 1);
12710                assert_eq!(params[0].name.name, "x");
12711                assert!(matches!(body, crate::ArrowBody::Block(_)));
12712            }
12713            other => panic!("expected Arrow, got {other:?}"),
12714        }
12715    }
12716
12717    /// A keyword is a valid method name, just as it is a valid property name.
12718    #[test]
12719    fn parses_method_shorthand_with_a_keyword_name() {
12720        let (ast, members) =
12721            object_literal_member_values("const o = { if(x: number): number { return x; } };");
12722        assert!(matches!(
12723            ast.try_expr(members[0]).unwrap().kind,
12724            ExprKind::Arrow { .. }
12725        ));
12726    }
12727
12728    #[test]
12729    fn parses_method_shorthand_alongside_plain_and_shorthand_properties() {
12730        let (ast, members) =
12731            object_literal_member_values("const o = { a: 1, m(): void {}, b: 2 };");
12732        assert_eq!(members.len(), 3);
12733        assert!(matches!(
12734            ast.try_expr(members[1]).unwrap().kind,
12735            ExprKind::Arrow { .. }
12736        ));
12737    }
12738
12739    #[test]
12740    fn parses_bare_ident_arrow_expression_body() {
12741        let (_ast, params, return_type, body) = parse_arrow_const("const f = x => x + 1;");
12742        assert_eq!(params.len(), 1);
12743        assert_eq!(params[0].name.name, "x");
12744        assert!(params[0].ty.is_none());
12745        assert!(return_type.is_none());
12746        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12747    }
12748
12749    #[test]
12750    fn parses_zero_param_arrow() {
12751        let (_ast, params, return_type, body) = parse_arrow_const("const f = () => 0;");
12752        assert!(params.is_empty());
12753        assert!(return_type.is_none());
12754        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12755    }
12756
12757    #[test]
12758    fn parses_paren_unannotated_arrow() {
12759        let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x) => x;");
12760        assert_eq!(params.len(), 1);
12761        assert_eq!(params[0].name.name, "x");
12762        assert!(params[0].ty.is_none());
12763    }
12764
12765    #[test]
12766    fn parses_typed_param_arrow() {
12767        let (_ast, params, return_type, body) =
12768            parse_arrow_const("const f = (x: number) => x * 2;");
12769        assert_eq!(params.len(), 1);
12770        assert!(params[0].ty.is_some());
12771        assert!(return_type.is_none());
12772        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12773    }
12774
12775    #[test]
12776    fn parses_typed_param_and_return_arrow() {
12777        let (_ast, params, return_type, body) =
12778            parse_arrow_const("const f = (x: number): number => x * 2;");
12779        assert_eq!(params.len(), 1);
12780        assert!(params[0].ty.is_some());
12781        assert!(return_type.is_some());
12782        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12783    }
12784
12785    #[test]
12786    fn parses_multi_param_unannotated_arrow() {
12787        let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x, y) => x + y;");
12788        assert_eq!(params.len(), 2);
12789        assert_eq!(params[0].name.name, "x");
12790        assert_eq!(params[1].name.name, "y");
12791        assert!(params[0].ty.is_none());
12792        assert!(params[1].ty.is_none());
12793    }
12794
12795    #[test]
12796    fn parses_multi_param_typed_arrow() {
12797        let (_ast, params, _return_type, _body) =
12798            parse_arrow_const("const f = (x: number, y: number) => x + y;");
12799        assert_eq!(params.len(), 2);
12800        assert!(params[0].ty.is_some());
12801        assert!(params[1].ty.is_some());
12802    }
12803
12804    #[test]
12805    fn parses_block_body_arrow() {
12806        let (_ast, params, _return_type, body) =
12807            parse_arrow_const("const f = (x: number) => { return x; };");
12808        assert_eq!(params.len(), 1);
12809        assert!(matches!(body, crate::ArrowBody::Block(_)));
12810    }
12811
12812    #[test]
12813    fn rejects_typed_bare_ident_arrow() {
12814        let (_ast, diags) = parse_str("const f = x: number => x;");
12815        assert!(
12816            !diags.is_empty(),
12817            "expected diagnostics for `x: number => …`",
12818        );
12819    }
12820
12821    #[test]
12822    fn arrow_inside_call_argument_parses() {
12823        let (ast, diags) = parse_str("f((x: number) => x + 1, 2);");
12824        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12825        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12826        let call = match &stmt.kind {
12827            StmtKind::Expr(eid) => ast.try_expr(*eid).unwrap(),
12828            _ => panic!("expected expression statement"),
12829        };
12830        let args = match &call.kind {
12831            ExprKind::Call { args, .. } => args.clone(),
12832            other => panic!("expected Call, got {other:?}"),
12833        };
12834        assert_eq!(args.len(), 2);
12835        assert!(matches!(
12836            ast.try_expr(args[0]).unwrap().kind,
12837            ExprKind::Arrow { .. }
12838        ));
12839    }
12840
12841    #[test]
12842    fn function_decl_param_still_requires_annotation() {
12843        let (_ast, diags) = parse_str("function f(x): number { return x; }");
12844        assert!(
12845            diags.iter().any(|d| d.message.contains("type annotation")),
12846            "expected `parameter requires a type annotation` diagnostic, got {diags:?}",
12847        );
12848    }
12849
12850    #[test]
12851    fn paren_expression_still_parses_when_no_arrow() {
12852        let (ast, diags) = parse_str("const a = (1 + 2);");
12853        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12854        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12855        let value = match &stmt.kind {
12856            StmtKind::Const { value, .. } => *value,
12857            _ => panic!("expected const declaration"),
12858        };
12859        assert!(matches!(
12860            ast.try_expr(value).unwrap().kind,
12861            ExprKind::Paren(_)
12862        ));
12863    }
12864
12865    #[test]
12866    fn parse_named_import_single() {
12867        let (ast, diags) = parse_str("import { v4 } from \"submilli:uuid\";");
12868        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12869        assert_eq!(ast.top_level.len(), 1);
12870        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12871        let (module, kind) = match &stmt.kind {
12872            StmtKind::Import { module, kind, .. } => (module, kind),
12873            other => panic!("expected Import, got {other:?}"),
12874        };
12875        assert_eq!(module, "submilli:uuid");
12876        let specs = match kind {
12877            ImportKind::Named(specs) => specs,
12878            other => panic!("expected Named, got {other:?}"),
12879        };
12880        assert_eq!(specs.len(), 1);
12881        assert_eq!(specs[0].imported_name.name, "v4");
12882        assert_eq!(specs[0].local_name.name, "v4");
12883    }
12884
12885    #[test]
12886    fn parse_named_import_multi_and_alias() {
12887        let (ast, diags) =
12888            parse_str("import { v4, v7 as makeId, validate } from \"submilli:uuid\";");
12889        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12890        let specs = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12891            StmtKind::Import {
12892                kind: ImportKind::Named(s),
12893                ..
12894            } => s.clone(),
12895            other => panic!("expected named import, got {other:?}"),
12896        };
12897        assert_eq!(specs.len(), 3);
12898        assert_eq!(specs[0].imported_name.name, "v4");
12899        assert_eq!(specs[0].local_name.name, "v4");
12900        assert_eq!(specs[1].imported_name.name, "v7");
12901        assert_eq!(specs[1].local_name.name, "makeId");
12902        assert_eq!(specs[2].imported_name.name, "validate");
12903        assert_eq!(specs[2].local_name.name, "validate");
12904    }
12905
12906    #[test]
12907    fn parse_namespace_import() {
12908        let (ast, diags) = parse_str("import uuid from \"submilli:uuid\";");
12909        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12910        let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12911            StmtKind::Import { kind, .. } => kind.clone(),
12912            other => panic!("expected import, got {other:?}"),
12913        };
12914        let local = match kind {
12915            ImportKind::Namespace { local_name } => local_name,
12916            other => panic!("expected Namespace, got {other:?}"),
12917        };
12918        assert_eq!(local.name, "uuid");
12919    }
12920
12921    #[test]
12922    fn parse_wildcard_namespace_import_synonym() {
12923        let (ast, diags) = parse_str("import * as uuid from \"submilli:uuid\";");
12924        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12925        let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12926            StmtKind::Import { kind, .. } => kind.clone(),
12927            other => panic!("expected import, got {other:?}"),
12928        };
12929        let local = match kind {
12930            ImportKind::Namespace { local_name } => local_name,
12931            other => panic!("expected Namespace, got {other:?}"),
12932        };
12933        assert_eq!(local.name, "uuid");
12934    }
12935
12936    #[test]
12937    fn parse_wildcard_namespace_import_without_as_rejected() {
12938        let (_ast, diags) = parse_str("import * from \"submilli:uuid\";");
12939        assert!(
12940            diags
12941                .iter()
12942                .any(|d| d.message.contains("expected `as <name>`")),
12943            "expected missing-as diagnostic, got: {diags:?}",
12944        );
12945    }
12946
12947    #[test]
12948    fn parse_combined_default_and_named_rejected() {
12949        let (_ast, diags) = parse_str("import uuid, { v4 } from \"submilli:uuid\";");
12950        assert!(
12951            diags
12952                .iter()
12953                .any(|d| d.message.contains("combining default and named")),
12954            "expected combined-import diagnostic, got: {diags:?}",
12955        );
12956    }
12957
12958    #[test]
12959    fn parse_side_effect_import_rejected() {
12960        let (_ast, diags) = parse_str("import \"submilli:uuid\";");
12961        assert!(
12962            diags
12963                .iter()
12964                .any(|d| d.message.contains("side-effect-only imports")),
12965            "expected side-effect-import diagnostic, got: {diags:?}",
12966        );
12967    }
12968
12969    #[test]
12970    fn parse_empty_specifier_list_rejected() {
12971        let (_ast, diags) = parse_str("import {} from \"submilli:uuid\";");
12972        assert!(
12973            diags
12974                .iter()
12975                .any(|d| d.message.contains("empty import specifier list")),
12976            "expected empty-specifier diagnostic, got: {diags:?}",
12977        );
12978    }
12979
12980    #[test]
12981    fn parse_import_inside_function_body_rejected() {
12982        let (_ast, diags) =
12983            parse_str("function main(): void { import { v4 } from \"submilli:uuid\"; }");
12984        assert!(
12985            diags
12986                .iter()
12987                .any(|d| d.message.contains("must appear at the top of the file")),
12988            "expected top-level diagnostic, got: {diags:?}",
12989        );
12990    }
12991
12992    #[test]
12993    fn parse_missing_from_rejected() {
12994        let (_ast, diags) = parse_str("import { v4 } \"submilli:uuid\";");
12995        assert!(
12996            diags.iter().any(|d| d.message.contains("expected `from`")),
12997            "expected missing-from diagnostic, got: {diags:?}",
12998        );
12999    }
13000
13001    #[test]
13002    fn parse_missing_module_specifier_rejected() {
13003        let (_ast, diags) = parse_str("import { v4 } from;");
13004        assert!(
13005            diags
13006                .iter()
13007                .any(|d| d.message.contains("expected module specifier")),
13008            "expected missing-module diagnostic, got: {diags:?}",
13009        );
13010    }
13011
13012    #[test]
13013    fn parse_export_function_marks_exported() {
13014        // `export function main() {}` parses to a single function declaration in
13015        // `top_level`, plus an `exported_decls` entry pointing at it.
13016        let (ast, diags) = parse_str("export function main(): void {}");
13017        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13018        let stmt = single_stmt(&ast);
13019        assert!(
13020            matches!(stmt.kind, crate::StmtKind::Function { .. }),
13021            "expected Function decl, got {:?}",
13022            stmt.kind,
13023        );
13024        assert_eq!(ast.exported_decls.len(), 1, "one exported decl");
13025        assert_eq!(ast.exported_decls[0].stmt, ast.top_level[0]);
13026    }
13027
13028    #[test]
13029    fn parse_export_const_type_interface_enum_are_marked() {
13030        for src in [
13031            "export const X: number = 1;",
13032            "export type Id = string;",
13033            "export interface P { x: number; }",
13034            "export enum E { A, B }",
13035        ] {
13036            let (ast, diags) = parse_str(src);
13037            assert!(diags.is_empty(), "unexpected diags for {src:?}: {diags:?}");
13038            assert_eq!(ast.top_level.len(), 1, "one decl for {src:?}");
13039            assert_eq!(ast.exported_decls.len(), 1, "one exported decl for {src:?}");
13040        }
13041    }
13042
13043    #[test]
13044    fn parse_export_default_is_rejected_with_focused_message() {
13045        let (_ast, diags) = parse_str("export default function main(): void {}");
13046        assert!(
13047            diags
13048                .iter()
13049                .any(|d| d.message.contains("`export default` is not supported")),
13050            "expected focused export-default diagnostic, got: {diags:?}",
13051        );
13052        assert!(
13053            !diags.iter().any(|d| d.message.contains("expected `;`")),
13054            "the spurious missing-semicolon error must be gone: {diags:?}",
13055        );
13056    }
13057
13058    #[test]
13059    fn parse_export_list_parses_as_export_from() {
13060        // Form 2 (`export { x };`, no `from`) now parses cleanly — the
13061        // single-file gating happens in the typechecker, not the parser.
13062        let (ast, diags) = parse_str("function main(): void {}\nexport { main };");
13063        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13064        assert_eq!(ast.top_level.len(), 2, "function + export-from");
13065        let export = ast.try_stmt(ast.top_level[1]).unwrap();
13066        let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
13067            panic!("expected ExportFrom, got {:?}", export.kind);
13068        };
13069        assert!(source.is_none(), "bare `export {{ x }}` has no source");
13070        assert_eq!(specs.len(), 1);
13071        assert_eq!(specs[0].imported_name.name, "main");
13072        assert_eq!(specs[0].local_name.name, "main");
13073    }
13074
13075    #[test]
13076    fn parse_export_from_with_source_and_alias_parses() {
13077        let (ast, diags) = parse_str("export { foo, bar as baz } from \"./util\";");
13078        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13079        let export = ast.try_stmt(ast.top_level[0]).unwrap();
13080        let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
13081            panic!("expected ExportFrom, got {:?}", export.kind);
13082        };
13083        let Some((module, _)) = source else {
13084            panic!("expected a `from` source");
13085        };
13086        assert_eq!(module, "./util");
13087        assert_eq!(specs.len(), 2);
13088        assert_eq!(specs[1].imported_name.name, "bar");
13089        assert_eq!(specs[1].local_name.name, "baz");
13090    }
13091
13092    #[test]
13093    fn parse_export_inside_function_body_rejected() {
13094        let (_ast, diags) = parse_str("function main(): void { export const X: number = 1; }");
13095        assert!(
13096            diags
13097                .iter()
13098                .any(|d| d.message.contains("must appear at the top of the file")),
13099            "expected top-level diagnostic, got: {diags:?}",
13100        );
13101    }
13102
13103    #[test]
13104    fn parse_throw_with_expression() {
13105        let (ast, diags) = parse_str(r#"throw new Error("oops");"#);
13106        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13107        let stmt = single_stmt(&ast);
13108        let crate::StmtKind::Throw { value } = stmt.kind else {
13109            panic!("expected Throw, got {:?}", stmt.kind);
13110        };
13111        assert!(matches!(
13112            ast.try_expr(value).unwrap().kind,
13113            crate::ExprKind::New { .. }
13114        ));
13115    }
13116
13117    #[test]
13118    fn parse_new_with_dotted_callee() {
13119        let (ast, diags) = parse_str("new Temporal.Duration({});");
13120        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13121        let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
13122            panic!("expected expression statement");
13123        };
13124        let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
13125            panic!("expected New");
13126        };
13127        let crate::ExprKind::FieldAccess { name, .. } = &ast.try_expr(*callee).unwrap().kind else {
13128            panic!("expected FieldAccess callee");
13129        };
13130        assert_eq!(name.name, "Duration");
13131    }
13132
13133    #[test]
13134    fn parse_new_with_three_level_dotted_callee() {
13135        let (ast, diags) = parse_str("new Foo.Bar.Baz();");
13136        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13137        let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
13138            panic!("expected expression statement");
13139        };
13140        let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
13141            panic!("expected New");
13142        };
13143        let crate::ExprKind::FieldAccess { receiver, name } = &ast.try_expr(*callee).unwrap().kind
13144        else {
13145            panic!("expected outer FieldAccess");
13146        };
13147        assert_eq!(name.name, "Baz");
13148        let crate::ExprKind::FieldAccess { name: mid_name, .. } =
13149            &ast.try_expr(*receiver).unwrap().kind
13150        else {
13151            panic!("expected inner FieldAccess");
13152        };
13153        assert_eq!(mid_name.name, "Bar");
13154    }
13155
13156    #[test]
13157    fn parse_new_with_trailing_dot_diagnoses() {
13158        let (_ast, diags) = parse_str("new Foo.;");
13159        assert!(
13160            diags.iter().any(|d| d
13161                .message
13162                .contains("expected identifier after `.` in constructor name")),
13163            "expected trailing-dot diagnostic, got: {diags:?}",
13164        );
13165    }
13166
13167    #[test]
13168    fn throw_without_expression_diagnoses() {
13169        let (_ast, diags) = parse_str("throw;");
13170        assert!(
13171            diags
13172                .iter()
13173                .any(|d| d.message == "expected expression after `throw`"),
13174            "expected bare-throw diagnostic, got: {diags:?}",
13175        );
13176    }
13177
13178    #[test]
13179    fn parse_try_catch() {
13180        let (ast, diags) = parse_str("try { } catch (e: Error) { }");
13181        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13182        let stmt = single_stmt(&ast);
13183        let crate::StmtKind::Try {
13184            body,
13185            ref catches,
13186            ref finally,
13187        } = stmt.kind
13188        else {
13189            panic!("expected Try, got {:?}", stmt.kind);
13190        };
13191        assert!(matches!(
13192            ast.try_stmt(body).unwrap().kind,
13193            crate::StmtKind::Block(ref v) if v.is_empty()
13194        ));
13195        let [clause] = catches.as_slice() else {
13196            panic!("expected one catch clause, got {catches:?}");
13197        };
13198        assert_eq!(clause.binding.name, "e");
13199        assert!(clause.ty.is_some());
13200        assert!(finally.is_none());
13201    }
13202
13203    #[test]
13204    fn parse_try_finally() {
13205        let (ast, diags) = parse_str("try { } finally { }");
13206        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13207        let stmt = single_stmt(&ast);
13208        let crate::StmtKind::Try {
13209            ref catches,
13210            ref finally,
13211            ..
13212        } = stmt.kind
13213        else {
13214            panic!("expected Try");
13215        };
13216        assert!(catches.is_empty());
13217        let finally_id = finally.expect("finally block");
13218        assert!(matches!(
13219            ast.try_stmt(finally_id).unwrap().kind,
13220            crate::StmtKind::Block(_)
13221        ));
13222    }
13223
13224    #[test]
13225    fn parse_try_catch_finally() {
13226        let (ast, diags) = parse_str("try { } catch (e: Error) { } finally { }");
13227        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13228        let stmt = single_stmt(&ast);
13229        let crate::StmtKind::Try {
13230            ref catches,
13231            ref finally,
13232            ..
13233        } = stmt.kind
13234        else {
13235            panic!("expected Try");
13236        };
13237        assert_eq!(catches.len(), 1);
13238        assert!(finally.is_some());
13239    }
13240
13241    #[test]
13242    fn bare_try_diagnoses() {
13243        let (_ast, diags) = parse_str("try { }");
13244        assert!(
13245            diags
13246                .iter()
13247                .any(|d| d.message == "try requires at least one of `catch` or `finally`"),
13248            "expected bare-try diagnostic, got: {diags:?}",
13249        );
13250    }
13251
13252    #[test]
13253    fn parse_untyped_catch() {
13254        let (ast, diags) = parse_str("try { } catch (e) { }");
13255        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13256        let stmt = single_stmt(&ast);
13257        let crate::StmtKind::Try {
13258            ref catches,
13259            ref finally,
13260            ..
13261        } = stmt.kind
13262        else {
13263            panic!("expected Try");
13264        };
13265        let [clause] = catches.as_slice() else {
13266            panic!("expected one catch clause, got {catches:?}");
13267        };
13268        assert_eq!(clause.binding.name, "e");
13269        assert!(clause.ty.is_none());
13270        assert!(finally.is_none());
13271    }
13272
13273    #[test]
13274    fn parse_multiple_catch_clauses() {
13275        let (ast, diags) = parse_str("try { } catch (e: HttpError) { } catch (f) { }");
13276        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13277        let stmt = single_stmt(&ast);
13278        let crate::StmtKind::Try {
13279            ref catches,
13280            ref finally,
13281            ..
13282        } = stmt.kind
13283        else {
13284            panic!("expected Try");
13285        };
13286        let [first, second] = catches.as_slice() else {
13287            panic!("expected two catch clauses, got {catches:?}");
13288        };
13289        assert_eq!(first.binding.name, "e");
13290        assert!(first.ty.is_some());
13291        assert_eq!(second.binding.name, "f");
13292        assert!(second.ty.is_none());
13293        assert!(finally.is_none());
13294    }
13295
13296    #[test]
13297    fn catch_after_finally_diagnoses() {
13298        let (_ast, diags) = parse_str("try { } finally { } catch (e: Error) { }");
13299        assert!(
13300            !diags.is_empty(),
13301            "expected diagnostic for catch-after-finally"
13302        );
13303    }
13304
13305    #[test]
13306    fn catch_missing_open_paren_diagnoses() {
13307        let (_ast, diags) = parse_str("try { } catch e: Error) { }");
13308        assert!(
13309            diags
13310                .iter()
13311                .any(|d| d.message == "expected `(` after `catch`"),
13312            "expected open-paren diagnostic, got: {diags:?}",
13313        );
13314    }
13315
13316    fn class_members(ast: &Ast) -> &[crate::ClassMember] {
13317        match &single_stmt(ast).kind {
13318            StmtKind::ClassDecl { members, .. } => members,
13319            other => panic!("expected ClassDecl, got {other:?}"),
13320        }
13321    }
13322
13323    #[test]
13324    fn parse_animal_class() {
13325        let src = r#"
13326class Animal {
13327  name: string;
13328  private sound: string;
13329  readonly species: string;
13330
13331  constructor(name: string, sound: string, species: string) {
13332    this.name = name;
13333    this.sound = sound;
13334    this.species = species;
13335  }
13336
13337  speak(): string {
13338    return this.name + " says " + this.sound;
13339  }
13340}
13341"#;
13342        let (ast, diags) = parse_str(src);
13343        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13344        let StmtKind::ClassDecl {
13345            name,
13346            generics,
13347            extends,
13348            implements,
13349            members,
13350            ..
13351        } = &single_stmt(&ast).kind
13352        else {
13353            panic!("expected ClassDecl");
13354        };
13355        assert_eq!(name.name, "Animal");
13356        assert!(generics.is_empty());
13357        assert!(extends.is_none());
13358        assert!(implements.is_empty());
13359        assert_eq!(members.len(), 5);
13360
13361        let crate::ClassMember::Field {
13362            name, modifiers, ..
13363        } = &members[0]
13364        else {
13365            panic!("member 0 should be a field");
13366        };
13367        assert_eq!(name.name, "name");
13368        assert_eq!(modifiers.visibility, crate::Visibility::Public);
13369        assert!(modifiers.readonly.is_none());
13370
13371        let crate::ClassMember::Field { modifiers, .. } = &members[1] else {
13372            panic!("member 1 should be a field");
13373        };
13374        assert_eq!(modifiers.visibility, crate::Visibility::Private);
13375
13376        let crate::ClassMember::Field { modifiers, .. } = &members[2] else {
13377            panic!("member 2 should be a field");
13378        };
13379        assert!(modifiers.readonly.is_some());
13380        assert_eq!(modifiers.visibility, crate::Visibility::Public);
13381
13382        assert!(matches!(
13383            &members[3],
13384            crate::ClassMember::Constructor { .. }
13385        ));
13386
13387        let crate::ClassMember::Method { name, .. } = &members[4] else {
13388            panic!("member 4 should be a method");
13389        };
13390        assert_eq!(name.name, "speak");
13391    }
13392
13393    #[test]
13394    fn parse_dog_class_extends_super_and_void_method() {
13395        let src = r#"
13396class Dog extends Animal {
13397  private tricks: string[];
13398
13399  constructor(name: string) {
13400    super(name, "woof", "canis familiaris");
13401    this.tricks = [];
13402  }
13403
13404  learn(trick: string): void {
13405    this.tricks.push(trick);
13406  }
13407}
13408"#;
13409        let (ast, diags) = parse_str(src);
13410        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13411        let StmtKind::ClassDecl {
13412            extends, members, ..
13413        } = &single_stmt(&ast).kind
13414        else {
13415            panic!("expected ClassDecl");
13416        };
13417        assert!(extends.is_some(), "Dog should extend Animal");
13418        assert_eq!(members.len(), 3);
13419        assert!(matches!(&members[0], crate::ClassMember::Field { .. }));
13420        assert!(matches!(
13421            &members[1],
13422            crate::ClassMember::Constructor { .. }
13423        ));
13424        assert!(matches!(&members[2], crate::ClassMember::Method { .. }));
13425    }
13426
13427    #[test]
13428    fn parse_class_implements_list() {
13429        let (ast, diags) = parse_str("class C implements I, J { run(): void {} }");
13430        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13431        let StmtKind::ClassDecl { implements, .. } = &single_stmt(&ast).kind else {
13432            panic!("expected ClassDecl");
13433        };
13434        assert_eq!(implements.len(), 2);
13435    }
13436
13437    #[test]
13438    fn parse_class_extends_and_implements() {
13439        let (ast, diags) = parse_str("class C extends B implements I { run(): void {} }");
13440        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13441        let StmtKind::ClassDecl {
13442            extends,
13443            implements,
13444            ..
13445        } = &single_stmt(&ast).kind
13446        else {
13447            panic!("expected ClassDecl");
13448        };
13449        assert!(extends.is_some());
13450        assert_eq!(implements.len(), 1);
13451    }
13452
13453    #[test]
13454    fn parse_class_generics_optional_field_and_initializer() {
13455        let (ast, diags) = parse_str("class Box<T> { value?: T; items: T[] = []; }");
13456        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13457        let StmtKind::ClassDecl {
13458            generics, members, ..
13459        } = &single_stmt(&ast).kind
13460        else {
13461            panic!("expected ClassDecl");
13462        };
13463        assert_eq!(generics.len(), 1);
13464        assert_eq!(generics[0].name, "T");
13465
13466        let crate::ClassMember::Field {
13467            optional,
13468            initializer,
13469            ..
13470        } = &members[0]
13471        else {
13472            panic!("member 0 should be a field");
13473        };
13474        assert!(*optional);
13475        assert!(initializer.is_none());
13476
13477        let crate::ClassMember::Field { initializer, .. } = &members[1] else {
13478            panic!("member 1 should be a field");
13479        };
13480        assert!(initializer.is_some());
13481    }
13482
13483    #[test]
13484    fn export_class_is_marked() {
13485        let (ast, diags) = parse_str("export class Foo {}");
13486        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13487        assert_eq!(ast.top_level.len(), 1);
13488        assert_eq!(ast.exported_decls.len(), 1);
13489        assert!(matches!(
13490            &single_stmt(&ast).kind,
13491            StmtKind::ClassDecl { .. }
13492        ));
13493    }
13494
13495    #[test]
13496    fn parameterless_index_signature_is_reported_from_its_member_start() {
13497        for (source, expected) in [
13498            ("interface P { readonly []: number }", "readonly []"),
13499            ("let p: { readonly []: number } = {};", "readonly []"),
13500            ("let q: { []: number } = {};", "[]"),
13501        ] {
13502            let (_ast, diags) = parse_str(source);
13503            let diag = diags
13504                .iter()
13505                .find(|d| d.message.contains("must declare exactly one parameter"))
13506                .unwrap_or_else(|| panic!("no diagnostic for {source}"));
13507            let reported = &source[diag.span.start as usize..diag.span.end as usize];
13508            assert_eq!(reported, expected, "{source}");
13509        }
13510    }
13511
13512    #[test]
13513    fn unterminated_object_type_expects_closing_brace() {
13514        let (_ast, diags) = parse_str("let p: { a: number\n");
13515        assert!(
13516            diags.iter().any(|d| d.message == "expected `}`"),
13517            "{diags:?}"
13518        );
13519    }
13520
13521    #[test]
13522    fn conflicting_visibility_modifiers_diagnose() {
13523        let (_ast, diags) = parse_str("class C { public private x: number; }");
13524        assert!(
13525            diags
13526                .iter()
13527                .any(|d| d.message.contains("at most one visibility modifier")),
13528            "got: {diags:?}",
13529        );
13530    }
13531
13532    #[test]
13533    fn duplicate_readonly_modifier_diagnoses() {
13534        let (_ast, diags) = parse_str("class C { readonly readonly x: number; }");
13535        assert!(
13536            diags
13537                .iter()
13538                .any(|d| d.message.contains("duplicate `readonly`")),
13539            "got: {diags:?}",
13540        );
13541    }
13542
13543    #[test]
13544    fn field_named_like_a_modifier_is_allowed() {
13545        let (ast, diags) = parse_str("class C { private: number; }");
13546        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13547        let crate::ClassMember::Field {
13548            name, modifiers, ..
13549        } = &class_members(&ast)[0]
13550        else {
13551            panic!("expected a field");
13552        };
13553        assert_eq!(name.name, "private");
13554        assert_eq!(modifiers.visibility, crate::Visibility::Public);
13555    }
13556
13557    #[test]
13558    fn method_named_get_is_allowed() {
13559        let (ast, diags) = parse_str("class C { get(): number { return 1; } }");
13560        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13561        let crate::ClassMember::Method { name, .. } = &class_members(&ast)[0] else {
13562            panic!("expected a method");
13563        };
13564        assert_eq!(name.name, "get");
13565    }
13566
13567    #[test]
13568    fn static_method_parses() {
13569        let (ast, diags) = parse_str("class C { static f(): number { return 1; } }");
13570        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13571        let crate::ClassMember::Method {
13572            name, modifiers, ..
13573        } = &class_members(&ast)[0]
13574        else {
13575            panic!("expected a method");
13576        };
13577        assert_eq!(name.name, "f");
13578        assert!(modifiers.static_span.is_some());
13579    }
13580
13581    #[test]
13582    fn static_readonly_field_parses() {
13583        let (ast, diags) = parse_str("class C { static readonly x: number = 1; }");
13584        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13585        let crate::ClassMember::Field {
13586            name,
13587            modifiers,
13588            initializer,
13589            ..
13590        } = &class_members(&ast)[0]
13591        else {
13592            panic!("expected a field");
13593        };
13594        assert_eq!(name.name, "x");
13595        assert!(modifiers.static_span.is_some());
13596        assert!(modifiers.readonly.is_some());
13597        assert!(initializer.is_some());
13598    }
13599
13600    #[test]
13601    fn private_static_method_parses() {
13602        let (ast, diags) = parse_str("class C { private static f(): void {} }");
13603        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13604        let crate::ClassMember::Method { modifiers, .. } = &class_members(&ast)[0] else {
13605            panic!("expected a method");
13606        };
13607        assert_eq!(modifiers.visibility, crate::Visibility::Private);
13608        assert!(modifiers.static_span.is_some());
13609    }
13610
13611    #[test]
13612    fn method_named_static_is_allowed() {
13613        let (ast, diags) = parse_str("class C { static(): number { return 1; } }");
13614        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13615        let crate::ClassMember::Method {
13616            name, modifiers, ..
13617        } = &class_members(&ast)[0]
13618        else {
13619            panic!("expected a method");
13620        };
13621        assert_eq!(name.name, "static");
13622        assert!(modifiers.static_span.is_none());
13623    }
13624
13625    #[test]
13626    fn visibility_after_static_diagnoses() {
13627        let (_ast, diags) = parse_str("class C { static private f(): void {} }");
13628        assert!(
13629            diags
13630                .iter()
13631                .any(|d| d.message.contains("`private` must come before `static`")),
13632            "got: {diags:?}",
13633        );
13634    }
13635
13636    #[test]
13637    fn duplicate_static_modifier_diagnoses() {
13638        let (_ast, diags) = parse_str("class C { static static f(): void {} }");
13639        assert!(
13640            diags
13641                .iter()
13642                .any(|d| d.message.contains("duplicate `static` modifier")),
13643            "got: {diags:?}",
13644        );
13645    }
13646
13647    #[test]
13648    fn static_accessor_is_rejected() {
13649        let (_ast, diags) = parse_str("class C { static get x(): number { return 1; } }");
13650        assert!(
13651            diags
13652                .iter()
13653                .any(|d| d.message.contains("static accessors are not supported")),
13654            "got: {diags:?}",
13655        );
13656    }
13657
13658    #[test]
13659    fn static_constructor_is_rejected() {
13660        let (_ast, diags) = parse_str("class C { static constructor() {} }");
13661        assert!(
13662            diags
13663                .iter()
13664                .any(|d| d.message.contains("a constructor cannot be `static`")),
13665            "got: {diags:?}",
13666        );
13667    }
13668
13669    #[test]
13670    fn optional_static_field_is_rejected() {
13671        let (_ast, diags) = parse_str("class C { static x?: number; }");
13672        assert!(
13673            diags
13674                .iter()
13675                .any(|d| d.message.contains("a static field cannot be optional")),
13676            "got: {diags:?}",
13677        );
13678    }
13679
13680    #[test]
13681    fn async_function_names_the_synchronous_rewrite() {
13682        let (_ast, diags) = parse_str(
13683            "async function main(): Promise<string> {\n  const x = await f();\n  return x;\n}",
13684        );
13685        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13686        assert_eq!(
13687            messages,
13688            [
13689                "`async` functions are not supported",
13690                "`await` is not supported"
13691            ],
13692            "{diags:?}"
13693        );
13694        assert_eq!(
13695            diags[0].help,
13696            [
13697                "calls return their value directly: remove `async`, write the return type `T` \
13698              instead of `Promise<T>`, and drop `await`"
13699            ]
13700        );
13701        assert_eq!(
13702            diags[1].help,
13703            ["calls return their value directly: remove `await`"]
13704        );
13705    }
13706
13707    #[test]
13708    fn async_is_rejected_on_every_function_form() {
13709        for source in [
13710            "export async function f(): void {}",
13711            "const f = async (): void => {};",
13712            "const f = async x => x;",
13713            "const f = async function (): void {};",
13714            "class C { async run(): void {} }",
13715            "class C { static async run(): void {} }",
13716        ] {
13717            let (_ast, diags) = parse_str(source);
13718            assert_eq!(
13719                diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13720                ["`async` functions are not supported"],
13721                "{source}: {diags:?}"
13722            );
13723        }
13724    }
13725
13726    #[test]
13727    fn await_before_a_template_is_reported() {
13728        for source in ["const a = await `hi`;", "const a = await `a${1}b`;"] {
13729            let (_ast, diags) = parse_str(source);
13730            let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13731            assert_eq!(
13732                messages,
13733                ["`await` is not supported"],
13734                "{source}: {diags:?}"
13735            );
13736        }
13737    }
13738
13739    #[test]
13740    fn async_and_await_stay_ordinary_names() {
13741        for source in [
13742            "const async = 1; const y = async + 1;",
13743            "function async(n: number): number { return n; } async(1);",
13744            "const await = 2; const z = await * 2;",
13745            "const await = (n: number): number => n; const a = await(1);",
13746            "const await = [1, 2]; const b = await[0];",
13747            "const async = { k: 1 }; const c = \"k\" in async; const d = async in ({ o: 1 });",
13748            "const async: unknown = 1; const n = async as (number);",
13749            "const await: unknown = 1; const m = await as number;",
13750            "const await = 1; const t = `${await}`;",
13751            "class C { async(): void {} async: number = 1; }",
13752        ] {
13753            let (_ast, diags) = parse_str(source);
13754            assert!(diags.is_empty(), "{source}: {diags:?}");
13755        }
13756    }
13757
13758    #[test]
13759    fn an_async_object_literal_method_names_the_rewrite_once() {
13760        assert_eq!(
13761            crate::typechecker::rules::test_util::compile_messages(
13762                "function main(): string {\n  const o = { async run(): string { return \"a\"; } };\n  return o.run();\n}\n"
13763            ),
13764            ["`async` functions are not supported"]
13765        );
13766        let (_ast, diags) = parse_str("const o = { async run(): string { return \"a\"; } };");
13767        assert_eq!(
13768            diags[0].help,
13769            [
13770                "calls return their value directly: remove `async`, write the return type `T` \
13771              instead of `Promise<T>`, and drop `await`"
13772            ]
13773        );
13774    }
13775
13776    #[test]
13777    fn await_in_an_async_object_literal_method_is_reported_too() {
13778        let (_ast, diags) =
13779            parse_str("const o = { async run(): string { const x = await f(); return x; } };");
13780        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13781        assert_eq!(
13782            messages,
13783            [
13784                "`async` functions are not supported",
13785                "`await` is not supported"
13786            ],
13787            "{diags:?}"
13788        );
13789    }
13790
13791    #[test]
13792    fn every_async_member_form_reports_async() {
13793        for source in [
13794            "const o = { async *run(): void {} };",
13795            "class C { async *run(): void {} }",
13796            "const o = { async [k](): void {} };",
13797            "const o = { async in(): void {} };",
13798            "class C { async as(): void {} }",
13799        ] {
13800            let (_ast, diags) = parse_str(source);
13801            assert_eq!(
13802                diags[0].message, "`async` functions are not supported",
13803                "{source}: {diags:?}"
13804            );
13805        }
13806    }
13807
13808    #[test]
13809    fn async_string_key_methods_report_only_async() {
13810        for source in [
13811            "const o = { async \"s\"(): string { return \"a\"; } };",
13812            "class C { async \"s\"(): string { return \"a\"; } }",
13813        ] {
13814            let (_ast, diags) = parse_str(source);
13815            assert_eq!(
13816                diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13817                ["`async` functions are not supported"],
13818                "{source}: {diags:?}"
13819            );
13820        }
13821    }
13822
13823    #[test]
13824    fn async_before_a_line_break_is_a_member_of_its_own() {
13825        for source in [
13826            "class C {\n  async\n  run(): void {}\n}",
13827            "const o = { async\n run(): void {} };",
13828        ] {
13829            let (_ast, diags) = parse_str(source);
13830            assert!(
13831                diags
13832                    .iter()
13833                    .all(|d| d.message != "`async` functions are not supported"),
13834                "{source}: {diags:?}"
13835            );
13836        }
13837    }
13838
13839    #[test]
13840    fn async_stays_an_object_literal_key() {
13841        let messages = crate::typechecker::rules::test_util::compile_messages(
13842            "function main(): number {\n  const async = 2;\n  const a = { async: 1 };\n  const b = { async };\n  const c = { async(): number { return 3; } };\n  const d = { async, e: async * 2 };\n  return a.async + b.async + c.async() + d.e;\n}\n",
13843        );
13844        assert!(messages.is_empty(), "{messages:?}");
13845    }
13846
13847    #[test]
13848    fn for_await_names_the_rewrite_once() {
13849        assert_eq!(
13850            crate::typechecker::rules::test_util::compile_messages(
13851                "import * as fs from \"submilli:fs\";\nfunction main(): string {\n  for await (const e of fs.list(\".\", { recursive: false })) { }\n  return \"x\";\n}\n"
13852            ),
13853            ["`for await` is not supported"]
13854        );
13855        let (ast, diags) = parse_str("for await (const e of [1]) { }");
13856        assert_eq!(
13857            diags[0].help,
13858            ["calls return their values directly: remove `await` and write `for (const e of …)`"]
13859        );
13860        assert_eq!(ast.top_level.len(), 1, "the loop is still read");
13861    }
13862
13863    #[test]
13864    fn a_broken_switch_leaves_later_declarations_at_the_top_level() {
13865        assert_eq!(
13866            crate::typechecker::rules::test_util::compile_messages(
13867                "function g(): void { switch (1) { case 1 } }\nclass C {}\nfunction main() { return \"x\"; }\n"
13868            ),
13869            [
13870                "expected `:` after `case` label",
13871                "expected expression",
13872                "expected `:` and return type"
13873            ]
13874        );
13875    }
13876
13877    #[test]
13878    fn every_switch_arm_error_restores_the_nesting() {
13879        for switch in [
13880            "switch (1) { case 1 }",
13881            "switch (1) { case : break; }",
13882            "switch (1) { default: break; default: break; }",
13883            "switch (1) { default }",
13884            "switch (1) { x; }",
13885            "switch (1) { case 1: }",
13886        ] {
13887            let source = format!("function g(): void {{ {switch} }}\nclass C {{}}\n");
13888            let (_ast, diags) = parse_str(&source);
13889            assert!(
13890                diags.iter().all(|d| !d.message.contains("top level")),
13891                "{switch}: {diags:?}"
13892            );
13893        }
13894    }
13895
13896    #[test]
13897    fn protected_member_is_rejected() {
13898        let (_ast, diags) = parse_str("class C { protected x: number; }");
13899        assert!(
13900            diags
13901                .iter()
13902                .any(|d| d.message.contains("`protected` is not supported")),
13903            "got: {diags:?}",
13904        );
13905    }
13906
13907    #[test]
13908    fn abstract_member_is_rejected() {
13909        let (_ast, diags) = parse_str("class C { abstract run(): void; }");
13910        assert!(
13911            diags
13912                .iter()
13913                .any(|d| d.message.contains("abstract classes are not supported")),
13914            "got: {diags:?}",
13915        );
13916    }
13917
13918    #[test]
13919    fn getter_parses_as_accessor() {
13920        let (ast, diags) = parse_str("class C { get value(): number { return 1; } }");
13921        assert!(diags.is_empty(), "got: {diags:?}");
13922        let crate::ClassMember::Accessor { kind, name, .. } = &class_members(&ast)[0] else {
13923            panic!("expected an accessor member");
13924        };
13925        assert_eq!(*kind, crate::AccessorKind::Get);
13926        assert_eq!(name.name, "value");
13927    }
13928
13929    #[test]
13930    fn getter_without_return_type_is_rejected() {
13931        let (ast, diags) = parse_str("class C { get value() { return 1; } }");
13932        assert_eq!(diags.len(), 1, "got: {diags:?}");
13933        assert_eq!(diags[0].message, "expected `:` and return type");
13934        assert_eq!(diags[0].help, vec!["get name(): T { … }".to_string()]);
13935        assert!(
13936            matches!(
13937                &class_members(&ast)[0],
13938                crate::ClassMember::Accessor {
13939                    return_type: None,
13940                    ..
13941                }
13942            ),
13943            "the getter is still recovered as a member"
13944        );
13945    }
13946
13947    #[test]
13948    fn setter_parses_as_accessor() {
13949        let (ast, diags) = parse_str("class C { set value(v: number) {} }");
13950        assert!(diags.is_empty(), "got: {diags:?}");
13951        let crate::ClassMember::Accessor { kind, param, .. } = &class_members(&ast)[0] else {
13952            panic!("expected an accessor member");
13953        };
13954        assert_eq!(*kind, crate::AccessorKind::Set);
13955        assert!(param.is_some(), "setter should have a parameter");
13956    }
13957
13958    #[test]
13959    fn param_property_modifier_outside_constructor_is_rejected() {
13960        let (_ast, diags) = parse_str("function f(public x: number): void {}");
13961        assert!(
13962            diags.iter().any(|d| d
13963                .message
13964                .contains("parameter properties are only allowed in a constructor")),
13965            "got: {diags:?}",
13966        );
13967    }
13968
13969    #[test]
13970    fn constructor_return_type_is_rejected() {
13971        let (_ast, diags) = parse_str("class C { constructor(): void {} }");
13972        assert!(
13973            diags.iter().any(|d| d
13974                .message
13975                .contains("constructor cannot declare a return type")),
13976            "got: {diags:?}",
13977        );
13978    }
13979
13980    #[test]
13981    fn field_initializer_without_type_is_rejected() {
13982        let (_ast, diags) = parse_str("class C { x = 1; }");
13983        assert!(
13984            diags
13985                .iter()
13986                .any(|d| d.message.contains("class fields require a type annotation")),
13987            "got: {diags:?}",
13988        );
13989    }
13990
13991    #[test]
13992    fn this_outside_method_is_left_to_typechecker() {
13993        let (_ast, diags) = parse_str("function f(): string { return this.name; }");
13994        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13995    }
13996
13997    #[test]
13998    fn super_outside_method_is_rejected() {
13999        let (_ast, diags) = parse_str("function f(): void { super(1); }");
14000        assert!(
14001            diags.iter().any(|d| d
14002                .message
14003                .contains("`super` is only valid inside a class method or constructor body")),
14004            "got: {diags:?}",
14005        );
14006    }
14007
14008    #[test]
14009    fn this_inside_method_is_accepted_by_parser() {
14010        let (_ast, diags) = parse_str("class C { x: number; read(): number { return this.x; } }");
14011        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
14012    }
14013    #[test]
14014    fn parse_void_expression_preserves_operand_and_precedence() {
14015        let (ast, diags) = parse_str("void effect() === undefined;");
14016        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
14017        let expression = expr_of_single_stmt(&ast);
14018        let crate::ExprKind::Binary { lhs, rhs, .. } = expression.kind else {
14019            panic!("expected equality")
14020        };
14021        let crate::ExprKind::Void { operand } = ast.try_expr(lhs).unwrap().kind else {
14022            panic!("expected void")
14023        };
14024        assert!(matches!(
14025            ast.try_expr(operand).unwrap().kind,
14026            crate::ExprKind::Call { .. }
14027        ));
14028        assert!(matches!(
14029            ast.try_expr(rhs).unwrap().kind,
14030            crate::ExprKind::Identifier(_)
14031        ));
14032    }
14033
14034    #[test]
14035    fn parse_void_newline_and_object_operands() {
14036        for source in [
14037            "void\n0;",
14038            "void { value: 1 };",
14039            "void\n{ value: 1 };",
14040            "void /x/;",
14041            "void void 0;",
14042        ] {
14043            let (_, diags) = parse_str(source);
14044            assert!(diags.is_empty(), "{source}: {diags:?}");
14045        }
14046        let (_, diags) = parse_str("void; 0;");
14047        assert!(
14048            !diags.is_empty(),
14049            "an explicit semicolon cannot split void from its operand"
14050        );
14051    }
14052
14053    #[test]
14054    fn parse_optional_parameters_and_defaults_in_callable_forms() {
14055        for source in [
14056            "function f(x = 1): number { return x; }",
14057            "function f(x: number = 1, y: number): number { return y; }",
14058            "function f({ x } = { x: 1 }): number { return x; }",
14059            "const f = (x?: number) => x;",
14060            "const f = (x: number = effect()) => x;",
14061            "const f = function(x: number = effect()) { return x; };",
14062            "const f = { m(x: number = effect()): number { return x; } };",
14063            "const f = ([x = 1]: number[] = values) => x;",
14064            "let f: (x?: number, ...rest: string[]) => void = value;",
14065            "let f: { method?(x?: number): void } = value;",
14066            "function f(x: number = 1, y?: number): void {}",
14067        ] {
14068            let (_, diags) = parse_str(source);
14069            assert!(diags.is_empty(), "{source}: {diags:?}");
14070        }
14071    }
14072
14073    #[test]
14074    fn parse_invalid_optional_parameters() {
14075        for source in [
14076            "function f(x?: number, y: number): void {}",
14077            "const f = (x?: number, y: number) => y;",
14078            "let f: (x?: number, y: number) => void = value;",
14079            "function f(...x?: number[]): void {}",
14080            "function f(x?: number = 1): void {}",
14081            "const f = (x?: number = 1) => x;",
14082            "function f({x}?: T): void {}",
14083        ] {
14084            let (_, diags) = parse_str(source);
14085            assert!(!diags.is_empty(), "expected diagnostic for {source}");
14086        }
14087    }
14088
14089    #[test]
14090    fn parse_optional_tuple_forms_and_order() {
14091        for source in [
14092            "let x: [number, string?] = value;",
14093            "let x: [x?: number, y?: string] = value;",
14094            "let x: readonly [(number | null)?, string?] = value;",
14095        ] {
14096            let (_, diags) = parse_str(source);
14097            assert!(diags.is_empty(), "{source}: {diags:?}");
14098        }
14099        for source in [
14100            "let x: [number?, string] = value;",
14101            "let x: [x: number?] = value;",
14102        ] {
14103            let (_, diags) = parse_str(source);
14104            assert!(!diags.is_empty(), "expected diagnostic for {source}");
14105        }
14106    }
14107
14108    #[test]
14109    fn parse_array_pattern_defaults_preserve_holes() {
14110        let (ast, diags) = parse_str("const [, a = effect(), b] = values;");
14111        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
14112        let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
14113            panic!("expected pattern")
14114        };
14115        let crate::Binding::Array {
14116            elems, defaults, ..
14117        } = binding
14118        else {
14119            panic!("expected array")
14120        };
14121        assert_eq!(elems.len(), 3);
14122        assert_eq!(defaults.len(), elems.len());
14123        assert!(elems[0].is_none());
14124        assert!(defaults[0].is_none());
14125        assert!(defaults[1].is_some());
14126        assert!(defaults[2].is_none());
14127    }
14128
14129    #[test]
14130    fn parse_optional_class_method_forms() {
14131        let (ast, diags) =
14132            parse_str("class C { declared?(x?: number): void; implemented?(): void\n{} }");
14133        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
14134        let members = class_members(&ast);
14135        assert!(matches!(
14136            &members[0],
14137            crate::ClassMember::Field {
14138                optional: true,
14139                initializer: None,
14140                ty: crate::TypeAnnotation {
14141                    kind: crate::TypeAnnotationKind::Function { .. },
14142                    ..
14143                },
14144                ..
14145            }
14146        ));
14147        assert!(matches!(
14148            &members[1],
14149            crate::ClassMember::Method { optional: true, .. }
14150        ));
14151    }
14152    #[test]
14153    fn defaults_retain_their_callable_receiver_scope() {
14154        for source in [
14155            "class C { value: number = 1; method(value: number = this.value): number { return value; } }",
14156            "class C { constructor(value: number = this.value) {} value: number = 1; }",
14157            "const f = function(this: { value: number }, value: number = this.value): number { return value; };",
14158        ] {
14159            let (ast, diagnostics) = parse_str(source);
14160            assert!(diagnostics.is_empty(), "{source}: {diagnostics:?}");
14161            assert!(
14162                ast.source_expressions()
14163                    .iter()
14164                    .any(|expr| matches!(expr.kind, ExprKind::This))
14165            );
14166            assert!(
14167                !ast.source_expressions()
14168                    .iter()
14169                    .any(|expr| matches!(expr.kind, ExprKind::ThisOutsideReceiver))
14170            );
14171        }
14172    }
14173}